允许通过多个 URL 经由 RD 网关服务器进行 RDP 连接

允许通过多个 URL 经由 RD 网关服务器进行 RDP 连接

我有一台可从外部访问的 RD 网关服务器,用于访问一组服务器。它目前使用采用外部 FQDN 的自签名证书,即MyServer.PrimaryLocation.Provider.com,当系统托管在主要位置时,这会起到很好的作用。

当我们需要故障转移到 DR 时,就会出现问题,此时我们无法迁移与服务器关联的 FQDN。这给我们留下了一个新的 FQDNMyServer.SecondaryLocation.Provider.com

我正在寻找一种方法来支持通过两个 URL 进行的连接,而无需更改证书,这会令用户感到沮丧,而且不幸的是,由于证书的自签名性质,这又是必要的(我们显然不拥有域“Provider.com”,因此无法购买外部 CA 证书)。

非常感谢任何帮助。

答案1

事实证明,使用带有 SAN(主体备用名称)条目的证书可以实现这一点。对于任何感兴趣的人,以下是执行此操作的 PowerShell:

<#
.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

相关内容