Tutorial: adicionar um ponto de extremidade HTTPS a um serviço de front-end de API Web do ASP.NET Core usando o Kestrel
Este tutorial é a parte três de uma série. Você aprenderá a habilitar HTTPS em um serviço ASP.NET Core em execução no Service Fabric. Quando você terminar, terá um aplicativo de votação com um front-end Web do ASP.NET Core habilitado para HTTPS escutando na porta 443. Se você não quiser criar manualmente o aplicativo de votação em Criar um aplicativo do Service Fabric no .NET, baixe o código-fonte do aplicativo concluído.
Na terceira parte da série, 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 SERVIÇO DE REDE acesso à chave privada do certificado
- Abrir a porta 443 no balanceador de carga do Azure
- Implantar o aplicativo em um cluster remoto
Nesta série de tutoriais, você aprenderá a:
- Criar um aplicativo .NET do Service Fabric
- Implantar o aplicativo em um cluster remoto
- Adicionar um ponto de extremidade HTTPS a um serviço de front-end do ASP.NET Core
- Configurar CI/CD usando o Azure Pipelines
- Configurar monitoramento e diagnóstico para o aplicativo
Observação
Recomendamos que você use o módulo Az PowerShell do Azure para interagir com o Azure. Confira Instalar o Azure PowerShell para começar. 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:
- Se você não tem uma assinatura do Azure, crie uma conta gratuita
- Instale o Visual Studio 2019 versão 16.5 ou posterior com as cargas de trabalho de Desenvolvimento do Azure e Desenvolvimento para a Web e ASP.NET.
- Instalar o SDK do Service Fabric
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 fornece o script CertSetup.ps1, que cria um certificado autoassinado e o importa para o repositório de certificados Cert:\LocalMachine\My
. Abra um prompt de comando como administrador e execute o seguinte comando para criar um certificado com o a entidade"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 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
Inicie o Visual Studio como um 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 Pontos de extremidade e edite o ponto de extremidade "ServiceEndpoint" existente. Altere o nome para "EndpointHttps", defina o protocolo como https, o tipo como Entrada 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 instruções using:
using System.Net;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography.X509Certificates;
Atualize o ServiceInstanceListener
para usar o novo ponto de extremidade EndpointHttps e escutar na porta 443. Ao configurar o host da Web para usar o servidor 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();
}))
Adicione também 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ê tiver criado um certificado autoassinado com o comando anterior do PowerShell ou use o valor de CN do seu certificado.
Lembre-se de que, no caso da implantação local em localhost
, é preferível usar "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 ao SERVIÇO DE REDE acesso à chave privada do certificado
Em uma etapa anterior, você importou o certificado para o repositório Cert:\LocalMachine\My
no computador de desenvolvimento. Agora, permita acesso explicitamente à conta que executa o serviço (SERVIÇO DE REDE, por padrão) à chave privada do certificado. Você pode executar essa etapa manualmente (usando a ferramenta certlm.msc), mas é melhor executar automaticamente um script do PowerShell configurando um script de inicialização no SetupEntryPoint do manifesto do serviço.
Observação
O Service Fabric é compatível com a declaração de certificados de ponto de extremidade por impressão digital ou por nome comum da entidade. Nesse caso, o runtime configurará a associação e a ACL da chave privada do certificado para a identidade em que o serviço está sendo executado. O runtime também monitorará o certificado em busca de alterações/renovações e solicitará a nova ACL 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 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 ponto SetupEntryPoint, execute 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, selecione Adicionar->Novo Item e adicione um novo arquivo denominado "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 como "Copiar se mais recente".
No Gerenciador de Soluções, clique com o botão direito do mouse em VotingWeb, selecione Adicionar->Novo Item e adicione um novo arquivo denominado "SetCertAccess.ps1". Edite o arquivo SetCertAccess.ps1 e adicione o script a seguir:
$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 mais recente".
Executar o script de instalação como um administrador local
Por padrão, o executável de ponto de entrada de instalação do serviço é executado com as mesmas credenciais do Service Fabric (normalmente a conta NetworkService). O SetCertAccess.ps1 requer privilégios 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 Entidades e adicione um novo usuário (por exemplo, "SetupAdminUser". Adicione a conta de suário SetupAdminUser ao grupo do sistema Administradores. Em seguida, na seção ServiceManifestImport do VotingWebPkg, configure uma RunAsPolicy para aplicar a entidade SetupAdminUser ao ponto de entrada da instalação. Essa política informa ao Service Fabric que o arquivo Setup.bat é executado como SetupAdminUser (com privilégios 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 pressione F5 para executar o aplicativo localmente. Depois que o aplicativo é implantado, um navegador da Web abre como https://localhost:443. Se você estiver usando um certificado autoassinado, verá um aviso informando que seu computador não confia na segurança desse site. Siga até a página da Web.
Instalar 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 pesquisará o certificado e configurará permissões de acesso.
Primeiro, exporte o certificado para um arquivo PFX. Abra o aplicativo certlm.msc e navegue até Pessoal>Certificados. Clique com o botão direito do mouse no certificado mytestcert e selecione Todas as Tarefas>Exportar.
No assistente de exportação, escolha Sim, exportar a chave privada e escolha o formato PFX (Troca de Informações Pessoais). Exporte o arquivo C:\Users\sfuser\votingappcert.pfx.
Em seguida, instale o certificado no cluster remoto usando estes scripts do PowerShell fornecidos.
Aviso
Um certificado autoassinado é suficiente para aplicativos de desenvolvimento e teste. Para aplicativos de produção, use um certificado de uma CA (autoridade de certificação) em vez de um certificado autoassinado.
Abrir a porta 443 no Azure Load Balancer e na rede virtual
Abra a porta 443 no balanceador de carga, se já 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 NSG 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 NSG.
$nsg | Set-AzNetworkSecurityGroup
Implantar o aplicativo no Azure
Salve todos os arquivos, alterne de Depurar para Lançar e pressione 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. Clique em Publicar para publicar o aplicativo no cluster remoto.
Quando o aplicativo é implantado, abra um navegador da Web e navegue até https://mycluster.region.cloudapp.azure.com:443
(atualize a URL com o ponto de extremidade de conexão para o cluster). Se você estiver usando um certificado autoassinado, verá um aviso informando que seu computador não confia na segurança desse site. Siga até a página da Web.
Próximas etapas
Nesta parte do tutorial, você aprendeu 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 SERVIÇO DE REDE acesso à chave privada do certificado
- Abrir a porta 443 no balanceador de carga do Azure
- Implantar o aplicativo em um cluster remoto
Prosseguir para o próximo tutorial: