PKI (infraestrutura de chave pública) para operações desconectadas no Azure Local

Este artigo explica os requisitos de PKI (infraestrutura de chave pública) para operações desconectadas no Azure Local. Você aprenderá a criar certificados para proteger pontos de extremidade do dispositivo e garantir uma comunicação segura em seu ambiente.

Visão geral da PKI para operações desconectadas

A PKI para operações desconectadas é essencial para proteger os endpoints que o appliance de operações desconectadas fornece. Crie e gerencie certificados digitais para garantir a comunicação segura e a transferência de dados em seu ambiente local do Azure.

Requisitos de PKI

Os certificados devem vir de uma AC (autoridade de certificação) pública ou uma autoridade de certificação corporativa. Verifique se os certificados fazem parte do Programa Raiz Confiável da Microsoft. Para obter mais informações, consulte Lista de Participantes – Microsoft Trusted Root Program.

Agrupe os certificados obrigatórios por área com o Nome Alternativo do Assunto (SAN) apropriado. Antes de criar os certificados, examine estes requisitos:

  • Não há suporte para o uso de certificados autoassinados. Recomendamos que você use certificados emitidos por uma AC corporativa.
  • As operações em modo desconectado exigem vários certificados externos para os endpoints que expõem.
  • Gere certificados individuais para cada ponto de extremidade e copie-os para o diretório ou a estrutura de pastas correspondente. Esses certificados são necessários para implantação de operações desconectadas.
  • Defina o assunto e a SAN para todos os certificados, conforme exigido pela maioria dos navegadores.
  • Todos os certificados devem compartilhar a mesma cadeia de confiança e ter pelo menos uma expiração de dois anos a partir do dia da implantação.
  • Exportar todos os certificados raiz no formato codificado em Base64. O arquivo resultante normalmente tem uma extensão .cer, .crt ou .pem.
  • Para implantações totalmente desconectadas:
    • Use uma AC (autoridade de certificação) privada ou interna.
    • Somente o acesso à lista de revogação de certificados (CRL) pela rede interna é necessário.
    • A conectividade com a Internet não é necessária.
    • Certifique-se de que sua infraestrutura de operações desconectada possa alcançar o ponto de extremidade da CRL especificado na extensão do Ponto de Distribuição da CRL (CDP) dos certificados.
    • Use o script no apêndice para validar seus endpoints de CRL, caso não tenha certeza de que sua configuração esteja correta.
    • Não use uma AC pública ou externa. As implantações falharão se os certificados vierem de uma AC pública, pois a conectividade com a Internet é necessária para acessar os serviços da CRL e do Protocolo de Status de Certificado Online (OCSP) para HTTPS.

Requisitos de certificado do ponto de entrada

Esta tabela lista os certificados obrigatórios necessários para operações desconectadas no Azure Local.

Serviço Assunto do certificado obrigatório e Nome Alternativo do Assunto (SAN)
Armazenamento de Blobs do Azure *.blob.fqdn
Registro de Contêiner do Azure *.edgeacr.fqdn
Armazenamento de filas do Azure *.queue.fqdn
Barramento de Serviço do Azure *.servicebus.fqdn
Armazenamento de Tabelas do Azure *.table.fqdn
Azure Key Vault *.vault.fqdn
Gerenciamento de administração adminmanagement.fqdn
Serviço de dados do Arc for Server Agent agentserviceapi.fqdn
Plano de dados de configuração do Arc

Kubernetes habilitado para Azure Arc
arckubernetesconfig.fqdn
Azure Resource Manager armmanagement.fqdn

management.fqdn
APIs do catálogo do portal público catalogapi.fqdn
Política de Dados do Azure data.policy.fqdn
Plano de dados da ponte de recursos do Azure Arc dp.appliances.fqdn
Licenciamento dp.aszrp.fqdn
Dispositivos front-end frontend.appliances.fqdn
Graph graph.fqdn
Serviço de notificação de convidado do Arc guestnotificationservice.fqdn
Arc para servidor his.fqdn
Hospedagem do portal público hosting.fqdn
Serviço de token seguro login.fqdn
Portal público portal.fqdn

Pontos de extremidade de gerenciamento

O ponto de extremidade de gerenciamento requer dois certificados. Coloque esses certificados na mesma pasta, ManagementEndpointsCerts. Os certificados são:

Certificado do ponto de extremidade de gerenciamento Assunto do certificado necessário
Servidor Endereço IP do ponto de extremidade de gerenciamento: $ManagementIngressIpAddress.
Se o IP do ponto de extremidade de gerenciamento for 192.168.50.100, o nome do assunto do certificado do servidor deverá corresponder exatamente. Por exemplo, Assunto = 192.168.50.100. Você também pode usar um nome de domínio totalmente qualificado (FQDN) como nome do servidor (SN), desde que ele seja resolvido para o endereço IP de gerenciamento.
Cliente Use um assunto de certificado que o ajude a distingui-lo de outras pessoas. Qualquer cadeia de caracteres é aceitável.
Por exemplo, Subject = ManagementEndpointClientAuth.

Criar certificados para proteger terminais

Pontos de extremidade de entrada

Na máquina host ou VM (máquina virtual) do Active Directory, siga as etapas nesta seção para criar certificados para o tráfego de entrada e pontos de extremidade externos do dispositivo de operações desconectado. O OperationsModule fornece métodos auxiliares para gerar solicitações de assinatura de certificado ou automatizar a criação completa do certificado.

Você precisa desses certificados para implantar o appliance de operações desconectadas. Você também precisa da chave pública para sua infraestrutura local fornecer uma cadeia de confiança segura.

Observação

IngressEndpointsCerts é a pasta em que você armazena todos os arquivos de certificado. IngressEndpointPassword é uma cadeia de caracteres segura com a senha do certificado.

  1. Conecte-se à CA.
  2. Crie uma pasta chamada IngressEndpointsCerts. Use esta pasta para armazenar todos os certificados.
  3. Crie os certificados utilizando o método auxiliar OperationsModule na pasta de destino IngressEndpointsCerts.
  4. Exiba e copie os certificados (arquivos .pfx) exportados para IngressEndpointsCerts.

O script a seguir mostra como usar o OperationsModule para gerar certificados. O script cria CSRs (Solicitações de Autenticação de Certificado), envia-as à AC (Autoridade de Certificação) e exporta os certificados gerados com proteção por senha.

Observação

Execute esse script em um computador ingressado no domínio usando uma conta com acesso do Administrador de Domínio para emitir certificados.

# Make sure you have the OperationsModule in this folder
# In the Appendix you can find an alternative to the OperationModule if you prefer writing your own automation

$applianceConfigBasePath = "C:\AzureLocalDisconnectedOperations\"
$fqdn = "autonomous.cloud.private" 
$IngressEndpointsCertsFolder = 'C:\Certs\IngressEndpointsCerts'
$certPassword  = Read-Host 'Password for output certificate file .pfx' -AsSecureString
## Automation alternative
## $certPassword =  (ConvertTo-SecureString "REPLACEME" -AsPlainText -Force)
$caName = "mycaserver.contoso.com\Contoso-RootCA" # Replace with your CA server and CA name (Run certutil -config - -ping to find the names)

Import-Module "$applianceConfigBasePath\OperationsModule\Azure.Local.DisconnectedOperations.psd1" -Force

New-ApplianceExternalCertificatesFromCA -ExternalFQDN $fqdn -OutputFolder $IngressEndpointsCertsFolder -CAConfig $caName -CertificatePassword $certPassword

Ponto de extremidade de gerenciamento

Aqui está um exemplo de como criar certificados para a segurança do endpoint de gerenciamento.

Observação

Execute esse script em um computador ingressado no domínio usando uma conta com acesso do Administrador de Domínio para emitir certificados.

Depois de criar os certificados, copie os certificados de gerenciamento (*.pfx) para a estrutura de diretório representada em ManagementEndpointCerts.

$applianceConfigBasePath = "C:\AzureLocalDisconnectedOperations\"
$fqdn = "autonomous.cloud.private" 
$managementEndpointIp = '192.168.100.25'
$managementEndpointCertsFolder = 'C:\Certs\ManagementEndpointsCerts'
$certPassword  = Read-Host 'Password for output certificate file .pfx' -AsSecureString
## Automation alternative
## $certPassword =  (ConvertTo-SecureString "REPLACEME" -AsPlainText -Force)
$caName = "mycaserver.contoso.com\Contoso-RootCA" # Replace with your CA server and CA name (Run certutil -config - -ping to find the names)

Import-Module "$applianceConfigBasePath\OperationsModule\Azure.Local.DisconnectedOperations.psd1" -Force

New-ApplianceManagementCertificatesFromCA -ManagementEndpoint $managementEndpointIp -OutputFolder $managementEndpointCertsFolder -CAConfig $caName -CertificatePassword $certpassword

# Alternative pass -CertificateTemplate 'MyTemplate' to override the default Webserver 
# New-ApplianceManagementCertificatesFromCA -ManagementEndpoint $managementEndpointIp -OutputFolder $managementEndpointCertsFolder -CAConfig $caName -CertificatePassword $certpassword -CertificateTemplate 'MyTemplate'

Observação

Se você não tiver um modelo chamado Webserver, o método falhará ao emitir certificados. Você pode substituir o nome padrão e usar um nome de modelo diferente.

Use o seguinte parâmetro para substituir o nome do modelo ao criar certificados: -CertificateTemplate 'MyTemplate'

Exportar certificado de Autoridade Certificadora Raiz

Você precisa da chave pública do certificado raiz para implantação. Exporte o certificado raiz com codificação base64.

Aqui está um exemplo de como exportar sua chave pública do certificado raiz:

$applianceRootcert = "C:\AzureLocalDisconnectedOperations\applianceRoot.cer"
$dcName = "corp.contoso.com"
$caName = "$($dcname)$\Contoso-RootCA" # Replace with your CA server and CA name (Run certutil -config - -ping to find the names)

# Option 1) Get the Root CA certificate by its name:
$RootCACert = Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*$($dcname)*" } | Select-Object -First 1

# # Option 2) Get the Root CA certificate by its thumbprint:
$RootCACert = Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object { $_.Thumbprint -eq "AA11BB22CC33DD44EE55FF66AA77BB88CC99DD00" } | Select-Object -First 1

# Check you have the correct Root CA certificate:
$RootCACert

# If it matches, export in CER (DER) format
Export-Certificate -Cert $RootCACert -FilePath "C:\Temp\RootCA-DER.cer" -Type CERT

# Finally, convert from CER (DER) to Base-64 CER (and store it in $applianceRootcert)
certutil -encode "C:\Temp\RootCA-DER.cer" $applianceRootcert

## Alternative method (If CA is setup and responds)
# certutil -ca.cert $applianceRootCert

Para saber mais, confira Serviços de Certificados do Active Directory.

Observação

O certificado raiz é necessário. Use o certificado raiz explícito, não um certificado intermediário. A implantação falha se a cadeia de confiança completa do certificado do endpoint de entrada estiver ausente.

Obter informações de certificado para integração de identidade

Para proteger sua integração de identidade, passe estes dois parâmetros:

  • LdapsCertChainInfo
  • OidcCertChainInfo

Essas verificações confirmam que os certificados e a cadeia desses endpoints não foram adulterados.

Use um método auxiliar no OperationsModule para preencher esses parâmetros.

Aqui está um exemplo de como preencher os parâmetros necessários:

Import-Module "$applianceConfigBasePath\OperationsModule\Azure.Local.DisconnectedOperations.psd1" -Force
$oidcCertChain = Get-CertificateChainFromEndpoint -requestUri 'https://adfs.azurestack.local/adfs'
$ldapsCertChain = Get-CertificateChainFromEndpoint -requestUri 'https://dc01.azurestack.local'

Aqui está um exemplo da saída de Get-CertificateChainFromEndpoint

# Returns: System.Security.Cryptography.X509Certificates.X509Certificate2[]
>> Get-CertificateChainFromEndpoint
>>
Thumbprint                                Subject
----------                                -------
TESTING580E20618EA15357FC1028622518DDC4D  CN=www.website.com, O=Contoso Corporation, L=Redmond, S=WA, C=US
TESTINGDAA2345B48E507320B695D386080E5B25  CN=www.website.com, O=Contoso Corporation, L=Redmond, S=WA, C=US
TESTING9BFD666761B268073FE06D1CC8D4F82A4  CN=www.website.com, O=Contoso Corporation, L=Redmond, S=WA, C=US

Apêndice

Validar CRL usando o script do PowerShell

  • Copie o script abaixo para um arquivo e nomeie-o ValidateCRL.ps1
  • Invocar o script usando .\ValidateCRL.ps1 -domainFQDN 'cloud.contoso.com'
# Powershell script for validation of certificates

param (
[Parameter(Mandatory=$false)]
    [ValidateNotNullOrEmpty()]
    [string] $domainFQDN = "cloud.contoso.com"
)


$portalURI = "https://portal.$domainFQDN"
$armURI = "https://armmanagement.$domainFQDN/metadata/endpoints?api-version=2022-09-01"


Add-Type -AssemblyName System.Net.Http


foreach ($uri in @($portalURI, $armURI)) {
    Write-Host "`n============================================" -ForegroundColor Cyan
    Write-Host "Testing: $uri" -ForegroundColor Cyan
    Write-Host "============================================" -ForegroundColor Cyan


    # --- Test 1: CRL check ENABLED (matches your failing code) ---
    Write-Host "`n[CRL CHECK = TRUE]" -ForegroundColor Yellow
    try {
        $handler = [System.Net.Http.HttpClientHandler]::new()
        $handler.CheckCertificateRevocationList = $true
        $client = [System.Net.Http.HttpClient]::new($handler)
        $task = $client.GetAsync($uri)
        $response = $task.GetAwaiter().GetResult()
        Write-Host "  Status: $([int]$response.StatusCode) ($($response.StatusCode))" -ForegroundColor Green
    }
    catch {
        Write-Host "  FAILED:" -ForegroundColor Red
        $ex = $_.Exception
        $depth = 0
        while ($ex) {
            $indent = "  " + ("  " * $depth)
            Write-Host "${indent}[$($ex.GetType().FullName)]" -ForegroundColor Red
            Write-Host "${indent}Message: $($ex.Message)" -ForegroundColor Red
            Write-Host "${indent}HResult: 0x$($ex.HResult.ToString('X8'))" -ForegroundColor DarkYellow
            $ex = $ex.InnerException
            $depth++
        }
    }
    finally {
        if ($client) { $client.Dispose() }
        if ($handler) { $handler.Dispose() }
    }


    # --- Test 2: CRL check DISABLED (matches your passing PowerShell test) ---
    Write-Host "`n[CRL CHECK = FALSE]" -ForegroundColor Yellow
    try {
        $handler2 = [System.Net.Http.HttpClientHandler]::new()
        $handler2.CheckCertificateRevocationList = $false
        $client2 = [System.Net.Http.HttpClient]::new($handler2)
        $task2 = $client2.GetAsync($uri)
        $response2 = $task2.GetAwaiter().GetResult()
        Write-Host "  Status: $([int]$response2.StatusCode) ($($response2.StatusCode))" -ForegroundColor Green
    }
    catch {
        Write-Host "  FAILED:" -ForegroundColor Red
        $ex2 = $_.Exception
        $depth2 = 0
        while ($ex2) {
            $indent2 = "  " + ("  " * $depth2)
            Write-Host "${indent2}[$($ex2.GetType().FullName)]" -ForegroundColor Red
            Write-Host "${indent2}Message: $($ex2.Message)" -ForegroundColor Red
            $ex2 = $ex2.InnerException
            $depth2++
        }
    }
    finally {
        if ($client2) { $client2.Dispose() }
        if ($handler2) { $handler2.Dispose() }
    }
}



Crie certificados com base em script

Se você preferir controlar a geração de certificados, veja um exemplo que pode ser modificado para criar CSRs e emitir certificados.

$fqdn = "autonomous.cloud.private" 
$caName = "<CA Computer Name>\<CA Name>" # Replace with your CA server and CA name (Run certutil -config - -ping to find the names)

$extCertFilePath = "C:\AzureLocalDisconnectedOperations\Certs\IngressEndpointsCerts"
# Making sure to create this directory if it does not exist
[void](New-Item -ItemType Directory -path $extCertFilePath -force)

$certPassword = Read-Host -AsSecureString -Message 'CertPass' -Force  
# Alternative
# $certPassword = "REPLACEME"|ConvertTo-SecureString -AsPlainText -Force
# Make sure the certificate list matches the table. Refer to the "Ingress endpoint certificate requirements" section for details.
$AzLCerts = @(    
  "*.edgeacr.$fqdn"      
  "*.vault.$fqdn"
  "*.queue.$fqdn"    
  "*.table.$fqdn"
  "*.blob.$fqdn" 
  "*.servicebus.$fqdn"   
  "data.policy.$fqdn"
  "arckubernetesconfig.$fqdn"
  "agentserviceapi.$fqdn"
  "his.$fqdn"
  "guestnotificationservice.$fqdn"
  "dp.appliances.$fqdn"
  "armmanagement.$fqdn"
  "adminmanagement.$fqdn"
  "management.$fqdn"
  "frontend.appliances.$fqdn"
  "graph.$fqdn"
  "dp.aszrp.$fqdn"
  "portal.$fqdn"
  "hosting.$fqdn"    
  "catalogapi.$fqdn"    
  "login.$fqdn"    
  # Multi-San could be added with comma seperated list x.$fqdn,y.$fqdn    
)

$AzLCerts | ForEach-Object {
    # Check if this is a multi SAN certificate
    if ($_.Contains(',')) {
        $certSubject = "CN=$($_.Split(',')[0])"
        $dns = $_.Replace(',', '&DNS=').Replace(' ', '')
        $filePrefix = $_.Split(',')[0].Replace('*.', '')
    }
    else {
        $certSubject = "CN=$_"
        $dns = $certSubject.Split('=')[1]
        $filePrefix = $dns.Replace('*.', '')
    }
    $certFilePath = "$extCertFilePath\INF"
    New-Item -ItemType Directory -Path $certFilePath -Force | Out-Null
    Remove-Item "$certFilePath\$filePrefix.*" -Force -ErrorAction SilentlyContinue
    $csrPath = Join-Path -Path $certFilePath -ChildPath "$filePrefix.csr"
    $infPath = Join-Path -Path $certFilePath -ChildPath "$filePrefix.inf"

    # Create the INF file for the CSR
    @"
[NewRequest]
Subject = "$certSubject"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = FALSE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
HashAlgorithm = sha256

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=$dns"
"@ | Out-File -FilePath $infPath

    # Generate the CSR
    certreq -new $infPath $csrPath
    # Define parameters to submit the CSR
    $certPath = Join-Path $certFilePath -ChildPath "$filePrefix.cer"

    # Submit the CSR to the CA
    certreq -submit -attrib "CertificateTemplate:WebServer" -config $caName $csrPath $certPath
    Write-Verbose "Certificate request submitted. Certificate saved to $certPath" -Verbose

    # Accept the certificate and install it.
    $certReqOutput = certreq.exe -accept $certPath

    # Parse the thumbprint and export the certificate
    $match = $certReqOutput -match 'Thumbprint:\s*([a-fA-F0-9]+)'
    if ($null -ne $match) {
        $thumbprint = (($match[0]).Split(':')[1]).Trim()
        Write-Verbose "Thumbprint: $thumbprint" -Verbose
    }
    else {
        Write-Verbose "Thumbprint not found" -Verbose
        #return;
    }

    # Export the certificate to a PFX file
    $cert = Get-Item -Path "Cert:\LocalMachine\My\$thumbprint"
    $cert | Export-PfxCertificate -FilePath "$extCertFilePath\$filePrefix.pfx" -Password $certPassword -Force
    Write-Verbose "Certificate for $certSubject and private key exported to $extCertFilePath" -Verbose
}
# Management certs alternative method
$caName = "mycaserver.contoso.com\Contoso-RootCA" # Replace with your CA server and CA name (Run certutil -config - -ping to find the names)

# For more info on how to find your CA: https://learn.microsoft.com/en-us/troubleshoot/windows-server/certificates-and-public-key-infrastructure-pki/find-name-enterprise-root-ca-server 
$certPassword = Read-Host -AsSecureString -Message 'ManagementCertPass' -Force 
# Alternative
# $certPassword = "REPLACEME"|ConvertTo-SecureString -AsPlainText -Force

$managementendpointPath = "C:\AzureLocalDisconnectedOperations\Certs\ManagementEndpointsCerts"
[void](New-Item -ItemType Directory -path $managementendpointPath -force)
$managementEndpointIPAddress = '192.168.100.25'
$fileNames = @('ManagementEndpointSsl', 'ManagementEndpointClientAuth')
$subjects = @($managementEndpointIPAddress,'ManagementEndpointClientAuth')  

$subjects|Foreach-Object {
    $subject=$_    
    $filename = $fileNames[$subjects.IndexOf($_)] 
    $infFilename = "$($managementendpointPath)\$($filename).inf"
    $csrPath = "$($managementendpointPath)\$($filename).csr"
    $certPath = "$($managementendpointPath)\$($filename).cer"
    $pfxPath = "$($managementendpointPath)\$($filename).pfx"
@"
[NewRequest]
Subject = "CN=$subject"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = FALSE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
HashAlgorithm = sha256

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=$subject"
"@ | Out-File -FilePath $infFilename

    # Generate the CSR
    certreq -new $infFilename $csrPath

    # Submit the CSR to the CA
    certreq -submit -attrib "CertificateTemplate:WebServer" -config $caName $csrPath $certPath
    Write-Verbose "Certificate request submitted. Certificate saved to $certPath" -Verbose

    # Accept the certificate and install it.
    $certReqOutput = certreq.exe -accept $certPath

    # Parse the thumbprint and export the certificate
    $match = $certReqOutput -match 'Thumbprint:\s*([a-fA-F0-9]+)'
    if ($null -ne $match) {
        $thumbprint = (($match[0]).Split(':')[1]).Trim()
        Write-Verbose "Thumbprint: $thumbprint" -Verbose
    }
    else {
        Write-Verbose "Thumbprint not found" -Verbose
        #return;
    }

    # Export the certificate to a PFX file
    $cert = Get-Item -Path "Cert:\LocalMachine\My\$thumbprint"
    $cert | Export-PfxCertificate -FilePath $pfxPath -Password $certPassword -Force
    Write-Verbose "Certificate for $subject and private key exported to $certPath" -Verbose
}

Esse recurso só está disponível no Azure Local 2602 ou posterior.