Partilhar via


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

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

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 acesso NetworkService à 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 utilize o módulo do Azure Az PowerShell para interagir com o Azure. Para começar, consulte Instalar o Azure PowerShell. Para saber como migrar para o módulo do Az PowerShell, veja 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 autoridade de certificação (CA). 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 armazenamento de certificados Cert:\LocalMachine\My . Abra uma janela de 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 PFX (Personal Information Exchange) de certificado, execute o seguinte para importar o certificado para o armazenamento 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, em seguida, abra a solução de votação. No Gerenciador de Soluções, abra VotingWeb/PackageRoot/ServiceManifest.xml. O manifesto de serviço define os pontos de extremidade de serviço. Encontre Endpoints secção e edite o valor para ServiceEndpoint endpoint. Altere o nome para EndpointHttps, defina o protocolo como https, o tipo para Inpute a porta para 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 procurar o certificado no repositório Cert:\LocalMachine\My . Adicione as seguintes instruções using:

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

Atualize o valor de ServiceInstanceListener para utilizar o novo ponto de extremidade EndpointHttps e escutar na porta 443. Quando você configura o host para usar o servidor Kestrel, você deve configurar o Kestrel para ouvir endereços IPv6 em todas as interfaces 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 seguinte método para que 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 do Serviço de Rede à chave privada do certificado

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

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

Observação

O Service Fabric oferece suporte à configuração de certificados de endpoint por impressão digital ou por nome comum do sujeito. Nesse caso, o runtime configura a associação e alocação da chave privada do certificado à identidade sob a qual o serviço está a ser executado. O tempo de execução também monitora o certificado em busca de alterações, renovações e atualizações relativas à alocação da chave privada correspondente.

Configurar o ponto de entrada para configuração do serviço

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

<?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 batch e os scripts de configuração do PowerShell

Para executar o PowerShell a partir do valor do SetupEntryPoint, você pode executar PowerShell.exe em um arquivo em lotes que aponte 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 seguinte comando:

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

Modifique as propriedades do arquivo Setup.bat para definir Copiar para Diretório de Saída como 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 Copy to Output Directory como Copy se for mais recente.

Execute o script de instalação como administrador

Por padrão, o executável do ponto de entrada da configuração do 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 Principals seção e adicione um novo usuário (por exemplo, SetupAdminUser). Adicione a conta de usuário SetupAdminUser ao grupo de sistema Administradores.

Em seguida, em VotingWebPkg, na ServiceManifestImport seção , configure um RunAsPolicy para aplicar o principal 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>

Execute 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 da implantação da aplicação, abre-se um navegador para https://localhost:443. Se estiver a utilizar um certificado autoassinado, verá um aviso de que o seu PC não confia na segurança deste Web site. Continue para a página web.

Captura de tela que mostra o aplicativo de Exemplo de Votação do Service Fabric em execução em um navegador e a URL do host local.

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 ser movidos para diferentes nós do cluster. Quando o serviço Web front-end é iniciado em um nó de cluster, o script de inicialização procura 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á paraCertificados Pessoais>. Clique com o botão direito do mouse no certificado mytestcert e selecione Todas as tarefas>de exportação.

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

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

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

Advertência

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

Abra a porta 443 no balanceador de carga do Azure 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

Implementar a aplicação no Azure

Salve todos os arquivos, alterne de Debug para Release e selecione F6 para reconstruir. 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 para seu cluster). Se estiver a utilizar um certificado autoassinado, verá um aviso de que o seu PC não confia na segurança deste Web site. Continue para a página 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óximo passo

Avance para o tutorial seguinte: