Allow RDP connection through RD Gateway Server via multiple URLs

0

I have an externally-accessible RD Gateway server which is used to gain access to a group of servers. This currently uses a self-signed certificate using the external FQDN, i.e. MyServer.PrimaryLocation.Provider.com, and this works a treat whilst the system is hosted in the primary location.

The problem occurs when we need to fail-over to DR, at which point we're unable to migrate the FQDN associated with the server. This leaves us with a new FQDN of MyServer.SecondaryLocation.Provider.com.

I'm looking for a way to support connections via both URLs without the need to change certificates, which would be frustrating for the users, and unfortunately necessary due to the self-signed nature of the certificates (we obviously don't own the domain 'Provider.com' and therefore can't purchase External CA certificates.

Any assistance greatly appreciated.

AndyHerb

Posted 2017-06-27T13:58:38.203

Reputation: 111

Answers

1

Turns out that it's possible to deliver this using a certificate with SAN (Subject Alternative Name) entries. For anyone who's interested, here's the PowerShell to do it:

<#
.Synopsis
   Creates a new Certificate for an RD Gateway Server
.DESCRIPTION
   Creates a self-signed Certificate and associates it with the local RD Gateway Server
.EXAMPLE
   New-RDGCertificate -SubjectName 
'CN=MyServer.PrimaryLocation.Provider.com'

   Creates a new self-signed certificate which can be used to to authenticate incoming RD Gateway traffic 
   to the DNS name MyServer.PrimaryLocation.Provider.com
.EXAMPLE
   [string[]] $SAN = "MyServer.SecondaryLocation.Provider.com"
   $SAN += "MyServer.TertiaryLocation.Provider.com"
   $SAN += "MyServer.local"

   New-RDGCertificate -SubjectName 
'CN=MyServer.PrimaryLocation.Provider.com' -SAN $SAN

   Creates a new self-signed certificate which can be used to to authenticate incoming RD Gateway traffic 
   to the following DNS names:
   1) MyServer.PrimaryLocation.Provider.com
   2) MyServer.SecondaryLocation.Provider.com
   3) MyServer.TertiaryLocation.Provider.com
   4) MyServer.local
#>
function New-RDGCertificate
{
    [CmdletBinding()]
    Param
    (
        # Name of the certificate to create
        [Parameter(Mandatory=$true)]
        [string]$SubjectName,

        # Subject-alternative name(s) to add to the certificate
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [string[]]$SAN
    )

    Write-Verbose "Creating Certificate: $SubjectName"
    $Name = New-Object -Com 'X509Enrollment.CX500DistinguishedName.1'
    $Name.Encode($SubjectName, 0)

    $Key = New-Object -Com 'X509Enrollment.CX509PrivateKey.1'
    $Key.ProviderName = 'Microsoft RSA SChannel Cryptographic Provider'
    $Key.KeySpec = 1
    $Key.Length = 1024
    $Key.SecurityDescriptor = 'D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)'
    $Key.MachineContext = 1
    $Key.Create()

    $ServerAuthoID = New-Object -Com 'X509Enrollment.CObjectId.1'
    $ServerAuthoID.InitializeFromValue('1.3.6.1.5.5.7.3.1')
    $ekuoids = New-Object -Com 'X509Enrollment.CObjectIds.1'
    $ekuoids.add($ServerAuthoID)
    $ekuext = New-Object -Com 'X509Enrollment.CX509ExtensionEnhancedKeyUsage.1'
    $ekuext.InitializeEncode($ekuoids)

    $Cert = New-Object -Com 'X509Enrollment.CX509CertificateRequestCertificate.1'
    $Cert.InitializeFromPrivateKey(2, $Key, '')
    $Cert.Subject = $Name
    $Cert.Issuer = $Cert.Subject
    $Cert.NotBefore = (Get-Date).AddHours(-2)
    $Cert.NotAfter = $Cert.NotBefore.AddDays(2998)

    #SAN 
    if ($SAN) {
        $IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames

        foreach ($AN in $SAN) {
            # Instantiate a IAlternativeName object
            $AltName = New-Object -ComObject X509Enrollment.CAlternativeName

            # Initialize the object by using current element
            $AltName.InitializeFromString(0x3,$AN)

        # Add created object to an object of collection of IAlternativeNames
        $IAlternativeNames.Add($AltName)
    }
    $SubjectAlternativeName = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames 
    $SubjectAlternativeName.InitializeEncode($IAlternativeNames) 
    $Cert.X509Extensions.Add($SubjectAlternativeName)
    }

    $Cert.X509Extensions.Add($ekuext)
    $Cert.Encode()

    $Enrollment = New-Object -Com 'X509Enrollment.CX509Enrollment.1'
    $Enrollment.InitializeFromRequest($Cert)
    $CertData = $Enrollment.CreateRequest(0)
    $Enrollment.InstallResponse(2, $CertData, 0, '')
}

<#
.Synopsis
   Searches certificate store for a given Subject Name, returning the Certificate
.DESCRIPTION
   Searches through the LocalMachine or CurrentUser store location for a certificate whose Subject Name matches the given value
.EXAMPLE
   Find-RDSCertificate -SubjectName "CN=MyDNS.com"

   This will search the LocalMachine\My store for a certificate whose Subject Name matches MyDNS.com.
   It returns the certificate or $Null, based on whether or not a matching certificate is found.

.EXAMPLE
   Find-RDSCertificate -SubjectName "CN=MyOtherDNS.com" -Store "TrustedPublisher" -Location "CurrentUser"

   This will search the CurrentUser\TrustedPublisher store for a certificate whose Subject Name matches MyDNSOther.com.
   It returns the certificate or $Null, based on whether or not a matching certificate is found.
#>
function Find-RDSCertificate
{
    [CmdletBinding()]
    Param
    (
        # Name of the certificate to search for
        [Parameter(Mandatory=$true)]
        $SubjectName,

        # Certificate store to search in
        [Parameter(Mandatory=$false)]
        [ValidateSet("AddressBook", "AuthRoot", "CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
        $Store = "My",

        # Location to search in
        [Parameter(Mandatory=$false)]
        [ValidateSet("LocalMachine", "CurrentUser")]
        $Location = "LocalMachine"
    )

    $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store($Store,$Location)
    $CertStore.Open('ReadOnly')

    $CertStore.Certificates | where {$_.Subject -Eq $SubjectName}
}

Import-Module RemoteDesktopServices

$RDGCertificateSubject = 'CN=MyServer.PrimaryLocation.Provider.com'
[string[]] $SAN = 'MyServer.SecondaryLocation.Provider.com'
$SAN += 'MyServer.TertiaryLocation.Provider.com'
$SAN += 'MyServer.local'
New-RDGCertificate -SubjectName $RDGCertificateSubject -SAN $SAN

$Cert = Find-RDSCertificate -SubjectName $RDGCertificateSubject
#Apply cert
Set-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint" -Value $Cert.Thumbprint

AndyHerb

Posted 2017-06-27T13:58:38.203

Reputation: 111