Compartilhar via


Tutorial: adicionar um ponto de extremidade HTTPS para um aplicativo do Service Fabric usando o Kestrel

Este tutorial é a parte três de uma série. Saiba como adicionar um ponto de extremidade HTTPS em um serviço do ASP.NET Core em execução no Azure Service Fabric. Quando terminar, você terá um aplicativo de votação com um front-end da Web do ASP.NET Core habilitado para HTTPS que escuta na porta 443. Se você não quiser criar manualmente o aplicativo de votação na parte um da série de tutoriais, você poderá baixar o código-fonte para obter o aplicativo concluído.

Neste tutorial, você aprenderá a:

  • Definir um ponto de extremidade HTTPS no serviço
  • Configurar o Kestrel para usar HTTPS
  • Instalar o certificado TLS/SSL nos nós de cluster remoto
  • Conceder ao NetworkService acesso à chave privada do certificado
  • Abrir a porta 443 no balanceador de carga do Azure
  • Implantar o aplicativo em um cluster remoto

A série de tutoriais mostra como:

Observação

Recomendamos que você use o módulo Az PowerShell do Azure para interagir com o Azure. Para começar, consulte Instalar o Azure PowerShell. Para saber como migrar para o módulo Az PowerShell, confira Migrar o Azure PowerShell do AzureRM para o Az.

Pré-requisitos

Antes de começar este tutorial:

Obter um certificado ou criar um certificado de desenvolvimento autoassinado

Para aplicativos de produção, use um certificado de uma CA (autoridade de certificação). Para fins de desenvolvimento e teste, você pode criar e usar um certificado autoassinado. O SDK do Service Fabric inclui o script CertSetup.ps1. O script cria um certificado autoassinado e o importa para o repositório de certificados Cert:\LocalMachine\My. Abra uma janela do Prompt de Comando como administrador e execute o seguinte comando para criar um certificado que tenha o assunto "CN=mytestcert":

PS C:\program files\microsoft sdks\service fabric\clustersetup\secure> .\CertSetup.ps1 -Install -CertSubjectName CN=mytestcert

Se você já tiver um arquivo de Troca de Informações Pessoais (PFX) de certificado, execute o seguinte para importar o certificado para o repositório de certificados Cert:\LocalMachine\My:


PS C:\mycertificates> Import-PfxCertificate -FilePath .\mysslcertificate.pfx -CertStoreLocation Cert:\LocalMachine\My -Password (ConvertTo-SecureString "!Passw0rd321" -AsPlainText -Force)


   PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject
----------                                -------
3B138D84C077C292579BA35E4410634E164075CD  CN=zwin7fh14scd.westus.cloudapp.azure.com

Definir um ponto de extremidade HTTPS no manifesto do serviço

Abra o Visual Studio usando a opção Executar como administrador e abra a solução de Votação. No Gerenciador de Soluções, abra VotingWeb/PackageRoot/ServiceManifest.xml. O manifesto do serviço define os pontos de extremidade de serviço. Localize a seção Endpoints e edite o valor do ponto de extremidade ServiceEndpoint. Altere o nome para EndpointHttps, defina o protocolo como https, o tipo como Input, e a porta como 443. Salve suas alterações.

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="VotingWebPkg"
                 Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric"
                 xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <ServiceTypes>
    <StatelessServiceType ServiceTypeName="VotingWebType" />
  </ServiceTypes>

  <CodePackage Name="Code" Version="1.0.0">
    <EntryPoint>
      <ExeHost>
        <Program>VotingWeb.exe</Program>
        <WorkingFolder>CodePackage</WorkingFolder>
      </ExeHost>
    </EntryPoint>
  </CodePackage>

  <ConfigPackage Name="Config" Version="1.0.0" />

  <Resources>
    <Endpoints>
      <Endpoint Protocol="https" Name="EndpointHttps" Type="Input" Port="443" />
    </Endpoints>
  </Resources>
</ServiceManifest>

Configurar o Kestrel para usar HTTPS

No Gerenciador de Soluções, abra o arquivo VotingWeb/VotingWeb.cs. Configure o Kestrel para usar HTTPS e pesquisar o certificado no repositório Cert:\LocalMachine\My. Adicione as seguintes declarações de using :

using System.Net;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography.X509Certificates;

Atualize o valor para ServiceInstanceListener usar o novo ponto de extremidade EndpointHttps e escutar na porta 443. Ao configurar o host da Web para usar o servidor do Kestrel, você deve configurar o Kestrel para escutar endereços IPv6 em todos os adaptadores de rede: opt.Listen(IPAddress.IPv6Any, port, listenOptions => {...}.

new ServiceInstanceListener(
serviceContext =>
    new KestrelCommunicationListener(
        serviceContext,
        "EndpointHttps",
        (url, listener) =>
        {
            ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

            return new WebHostBuilder()
                .UseKestrel(opt =>
                {
                    int port = serviceContext.CodePackageActivationContext.GetEndpoint("EndpointHttps").Port;
                    opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                    {
                        listenOptions.UseHttps(FindMatchingCertificateBySubject());
                        listenOptions.NoDelay = true;
                    });
                })
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })

                .ConfigureServices(
                    services => services
                        .AddSingleton<HttpClient>(new HttpClient())
                        .AddSingleton<FabricClient>(new FabricClient())
                        .AddSingleton<StatelessServiceContext>(serviceContext))
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                .UseUrls(url)
                .Build();
        }))

Em seguida, adicione o método a seguir para que o Kestrel possa encontrar o certificado no repositório Cert:\LocalMachine\My usando o assunto.

Substitua <your_CN_value> por mytestcert se você criou um certificado autoassinado usando o comando anterior do PowerShell, ou use o CN do seu certificado.

Se você usar uma implantação local para localhost, recomendamos que você use CN=localhost para evitar exceções de autenticação.

private X509Certificate2 FindMatchingCertificateBySubject(string subjectCommonName)
{
    using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
        var certCollection = store.Certificates;
        var matchingCerts = new X509Certificate2Collection();
    
    foreach (var enumeratedCert in certCollection)
    {
      if (StringComparer.OrdinalIgnoreCase.Equals(subjectCommonName, enumeratedCert.GetNameInfo(X509NameType.SimpleName, forIssuer: false))
        && DateTime.Now < enumeratedCert.NotAfter
        && DateTime.Now >= enumeratedCert.NotBefore)
        {
          matchingCerts.Add(enumeratedCert);
        }
    }

        if (matchingCerts.Count == 0)
    {
        throw new Exception($"Could not find a match for a certificate with subject 'CN={subjectCommonName}'.");
    }
        
        return matchingCerts[0];
    }
}


Conceder acesso ao Serviço de Rede à chave privada do certificado

Em uma etapa anterior, você importou o certificado para o repositório Cert:\LocalMachine\My no computador de desenvolvimento.

Agora, forneça explicitamente à conta que está executando o serviço (Serviço de Rede, por padrão) acesso à chave privada do certificado. Você pode executar esta etapa manualmente (usando a ferramenta certlm.msc), mas é melhor executar um script do PowerShell configurando um script de inicialização no SetupEntryPoint do manifesto do serviço.

Observação

O Service Fabric dá suporte à declaração de certificados de ponto de extremidade por impressão digital ou por nome comum da entidade. Nesse caso, o runtime configura a associação e a alocação da chave privada do certificado para a identidade em que o serviço está sendo executado. O runtime também monitora o certificado para detectar alterações, renovações e atualizações de alocação da chave privada correspondente.

Configurar um ponto de entrada de instalação do serviço

No Gerenciador de Soluções, abra VotingWeb/PackageRoot/ServiceManifest.xml. Na seção CodePackage, adicione o nó SetupEntryPoint e adicione um nó ExeHost. Em ExeHost, defina Program como Setup.bat, e WorkingFolder como CodePackage. Quando o serviço VotingWeb é iniciado, o script Setup.bat é executado na pasta CodePackage antes do início de VotingWeb.exe.

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="VotingWebPkg"
                 Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric"
                 xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <ServiceTypes>
    <StatelessServiceType ServiceTypeName="VotingWebType" />
  </ServiceTypes>

  <CodePackage Name="Code" Version="1.0.0">
    <SetupEntryPoint>
      <ExeHost>
        <Program>Setup.bat</Program>
        <WorkingFolder>CodePackage</WorkingFolder>
      </ExeHost>
    </SetupEntryPoint>

    <EntryPoint>
      <ExeHost>
        <Program>VotingWeb.exe</Program>
        <WorkingFolder>CodePackage</WorkingFolder>
      </ExeHost>
    </EntryPoint>
  </CodePackage>

  <ConfigPackage Name="Config" Version="1.0.0" />

  <Resources>
    <Endpoints>
      <Endpoint Protocol="https" Name="EndpointHttps" Type="Input" Port="443" />
    </Endpoints>
  </Resources>
</ServiceManifest>

Adicionar os scripts de instalação do PowerShell e o batch

Para executar o PowerShell do valor para SetupEntryPoint, você pode executar PowerShell.exe em um arquivo em lote que aponta para um arquivo do PowerShell.

Primeiro, adicione o arquivo em lotes ao projeto de serviço. No Gerenciador de Soluções, clique com o botão direito do mouse em VotingWeb e selecione Adicionar>Novo item. Adicione um novo arquivo chamado Setup.bat. Edite o arquivo Setup.bat e adicione o comando a seguir:

powershell.exe -ExecutionPolicy Bypass -Command ".\SetCertAccess.ps1"

Modifique as propriedades do arquivo Setup.bat para definir Copiar para diretório de saída para Copiar se for mais recente.

Captura de tela que mostra a configuração das propriedades do arquivo.

No Gerenciador de Soluções, clique com o botão direito do mouse em VotingWeb. Em seguida, selecione Adicionar>Novo item e adicione um novo arquivo chamado SetCertAccess.ps1. Edite o arquivo SetCertAccess.ps1 para adicionar o seguinte script:

$subject="mytestcert"
$userGroup="Network Service"

Write-Host "Checking permissions to certificate $subject.." -ForegroundColor DarkCyan

$cert = (gci Cert:\LocalMachine\My\ | where { $_.Subject.Contains($subject) })[-1]

if ($cert -eq $null)
{
    $message="Certificate with subject:"+$subject+" does not exist at Cert:\LocalMachine\My\"
    Write-Host $message -ForegroundColor Red
    exit 1;
}elseif($cert.HasPrivateKey -eq $false){
    $message="Certificate with subject:"+$subject+" does not have a private key"
    Write-Host $message -ForegroundColor Red
    exit 1;
}else
{
    $keyName=$cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName

    $keyPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\"

    if ($keyName -eq $null){
      $privateKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)      
      $keyName = $privateKey.Key.UniqueName
      $keyPath = "C:\ProgramData\Microsoft\Crypto\Keys"
    }

    $fullPath=$keyPath+$keyName
    $acl=(Get-Item $fullPath).GetAccessControl('Access')


    $hasPermissionsAlready = ($acl.Access | where {$_.IdentityReference.Value.Contains($userGroup.ToUpperInvariant()) -and $_.FileSystemRights -eq [System.Security.AccessControl.FileSystemRights]::FullControl}).Count -eq 1

    if ($hasPermissionsAlready){
        Write-Host "Account $userGroup already has permissions to certificate '$subject'." -ForegroundColor Green
        return $false;
    } else {
        Write-Host "Need add permissions to '$subject' certificate..." -ForegroundColor DarkYellow

        $permission=$userGroup,"Full","Allow"
        $accessRule=new-object System.Security.AccessControl.FileSystemAccessRule $permission
        $acl.AddAccessRule($accessRule)
        Set-Acl $fullPath $acl

        Write-Output "Permissions were added"

        return $true;
    }
}

Modifique as propriedades do arquivo SetCertAccess.ps1 para definir Copiar para diretório de saída como Copiar se for mais recente.

Executar o script de configuração como administrador

Por padrão, o arquivo executável do ponto de entrada de configuração de serviço é executado usando as mesmas credenciais do Service Fabric (normalmente, a conta do Serviço de Rede). O SetCertAccess.ps1 requer permissões de administrador. No manifesto do aplicativo, você pode alterar as permissões de segurança para executar o script de inicialização em uma conta de administrador local.

No Gerenciador de Soluções, abra Voting/ApplicationPackageRoot/ApplicationManifest.xml. Primeiro, crie uma seção Principals e adicione um novo usuário (por exemplo, SetupAdminUser). Adicione a conta de suário SetupAdminUser ao grupo do sistema Administradores.

Em seguida, em VotingWebPkg, na seção ServiceManifestImport, configure um RunAsPolicy para aplicar a entidade de segurança SetupAdminUser ao ponto de entrada de instalação. Esta política informa ao Service Fabric que o arquivo Setup.bat é executado como SetupAdminUser (com permissões de administrador).

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="VotingType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Parameters>
    <Parameter Name="VotingData_MinReplicaSetSize" DefaultValue="3" />
    <Parameter Name="VotingData_PartitionCount" DefaultValue="1" />
    <Parameter Name="VotingData_TargetReplicaSetSize" DefaultValue="3" />
    <Parameter Name="VotingWeb_InstanceCount" DefaultValue="-1" />
  </Parameters>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="VotingDataPkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
  </ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="VotingWebPkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <RunAsPolicy CodePackageRef="Code" UserRef="SetupAdminUser" EntryPointType="Setup" />
    </Policies>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="VotingData">
      <StatefulService ServiceTypeName="VotingDataType" TargetReplicaSetSize="[VotingData_TargetReplicaSetSize]" MinReplicaSetSize="[VotingData_MinReplicaSetSize]">
        <UniformInt64Partition PartitionCount="[VotingData_PartitionCount]" LowKey="0" HighKey="25" />
      </StatefulService>
    </Service>
    <Service Name="VotingWeb" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="VotingWebType" InstanceCount="[VotingWeb_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
  <Principals>
    <Users>
      <User Name="SetupAdminUser">
        <MemberOf>
          <SystemGroup Name="Administrators" />
        </MemberOf>
      </User>
    </Users>
  </Principals>
</ApplicationManifest>

Executar o aplicativo localmente

No Gerenciador de Soluções, selecione o aplicativo de Votação e defina a propriedade URL do aplicativo como https://localhost:443.

Salve todos os arquivos e selecione F5 para executar o aplicativo localmente. Depois que o aplicativo é implantado, um navegador é aberto para https://localhost:443. Se você estiver usando um certificado autoassinado, verá um aviso de que seu computador não confia na segurança deste site. Continue para a página da Web.

Captura de tela que mostra o aplicativo de exemplo de votação do Service Fabric em execução em um navegador e na URL localhost.

Instalar o certificado em nós de cluster

Antes de implantar o aplicativo no Azure, instale o certificado no repositório Cert:\LocalMachine\My de todos os nós de cluster remoto. Os serviços podem migrar para diferentes nós do cluster. Quando o serviço Web de front-end é iniciado em um nó de cluster, o script de inicialização pesquisa o certificado e configura as permissões de acesso.

Para instalar o certificado em nós de cluster, primeiro exporte o certificado como um arquivo PFX. Abra o arquivo de aplicativo certlm.msc e vá para Pessoal>Certificados. Clique com o botão direito do mouse no certificado mytestcert e selecione Todas as tarefas>Exportar.

Captura de tela que mostra a exportação do certificado.

No assistente de exportação, selecione Sim, exportar a chave privada e selecione o formato PFX. Exporte o arquivo C:\Users\sfuser\votingappcert.pfx.

Em seguida, instale o certificado no cluster remoto usando scripts do PowerShell.

Aviso

Um certificado autoassinado é suficiente para aplicativos de desenvolvimento e teste. Para aplicativos de produção, use um certificado de uma autoridade de certificação (AC) em vez de usar um certificado autoassinado.

Abrir a porta 443 no Azure Load Balancer e na rede virtual

Abra a porta 443 no balanceador de carga se ela não estiver aberta:

$probename = "AppPortProbe6"
$rulename="AppPortLBRule6"
$RGname="voting_RG"
$port=443

# Get the load balancer resource
$resource = Get-AzResource | Where {$_.ResourceGroupName –eq $RGname -and $_.ResourceType -eq "Microsoft.Network/loadBalancers"}
$slb = Get-AzLoadBalancer -Name $resource.Name -ResourceGroupName $RGname

# Add a new probe configuration to the load balancer
$slb | Add-AzLoadBalancerProbeConfig -Name $probename -Protocol Tcp -Port $port -IntervalInSeconds 15 -ProbeCount 2

# Add rule configuration to the load balancer
$probe = Get-AzLoadBalancerProbeConfig -Name $probename -LoadBalancer $slb
$slb | Add-AzLoadBalancerRuleConfig -Name $rulename -BackendAddressPool $slb.BackendAddressPools[0] -FrontendIpConfiguration $slb.FrontendIpConfigurations[0] -Probe $probe -Protocol Tcp -FrontendPort $port -BackendPort $port

# Set the goal state for the load balancer
$slb | Set-AzLoadBalancer

Faça o mesmo para a rede virtual associada:

$rulename="allowAppPort$port"
$nsgname="voting-vnet-security"
$RGname="voting_RG"
$port=443

# Get the network security group resource
$nsg = Get-AzNetworkSecurityGroup -Name $nsgname -ResourceGroupName $RGname

# Add the inbound security rule.
$nsg | Add-AzNetworkSecurityRuleConfig -Name $rulename -Description "Allow app port" -Access Allow `
    -Protocol * -Direction Inbound -Priority 3891 -SourceAddressPrefix "*" -SourcePortRange * `
    -DestinationAddressPrefix * -DestinationPortRange $port

# Update the network security group
$nsg | Set-AzNetworkSecurityGroup

Implantar o aplicativo no Azure

Salve todos os arquivos, alterne de Depuração para Versão e selecione F6 para recompilar. No Gerenciador de Soluções, clique com o botão direito do mouse em Votação e selecione Publicar. Selecione o ponto de extremidade de conexão do cluster criado em Implantar um aplicativo em um cluster ou selecione outro cluster. Selecione Publicar para publicar o aplicativo no cluster remoto.

Quando o aplicativo for implantado, abra um navegador da Web e vá para https://mycluster.region.cloudapp.azure.com:443 (atualize a URL com o ponto de extremidade de conexão do seu cluster). Se você estiver usando um certificado autoassinado, verá um aviso de que seu computador não confia na segurança deste site. Continue para a página da Web.

Captura de tela que mostra o aplicativo de exemplo de votação do Service Fabric em execução em uma janela do navegador.

Próxima etapa

Prosseguir para o próximo tutorial: