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, irá aprender a:

  • Definir um ponto final de 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
  • Implementar a aplicação num cluster remoto

A série de tutoriais mostra como:

Nota

Recomendamos que utilize o módulo Azure Az do 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 aplicações de produção, utilize um certificado de uma autoridade de certificação (AC). Para fins de desenvolvimento e teste, pode criar e utilizar 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 final de HTTPS no manifesto de serviço

Abra o Visual Studio usando a opção Executar como administrador e, em seguida, abra a solução de votação. No Explorador de Soluções, abra VotingWeb/PackageRoot/ServiceManifest.xml. O manifesto do serviço declara os pontos finais do serviço. Encontre a Endpoints seção e edite o valor do ponto de ServiceEndpoint extremidade. Altere o nome para EndpointHttps, defina o protocolo como https, o tipo para Inpute a porta para 443. Guardar as 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 utilizar HTTPS

No Explorador de Soluções, abra o ficheiro 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 para ServiceInstanceListener usar o novo EndpointHttps ponto de extremidade 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.

Nota

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

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

No Explorador de Soluções, abra VotingWeb/PackageRoot/ServiceManifest.xml. CodePackage Na seção, adicione o SetupEntryPoint nó e, em seguida, adicione um ExeHost nó. 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 de batch e 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 ficheiro de batch ao projeto do 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 ficheiro Setup.bat e adicione o seguinte comando:

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

Modifique as propriedades do arquivo Setup.bat para definir Copy to Output Directory como Copy 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 da aplicação, pode alterar as permissões de segurança para executar o script de arranque com uma conta de administrador local.

No Explorador 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 utilizador SetupAdminUser ao grupo Administradores do sistema.

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>

Executar a aplicação 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 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á para Certificados 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 ficheiro para C:\Users\sfuser\votingappcert.pfx.

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

Aviso

Para as aplicações de desenvolvimento e teste, basta um certificado autoassinado. 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 final da ligação do cluster criado em Implementar uma aplicação num 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: