Esercitazione: Aggiungere un endpoint HTTPS a un servizio front-end API Web ASP.NET Core usando Kestrel

Questa è la terza di una serie di esercitazioni. Verrà illustrato come abilitare HTTPS in un servizio ASP.NET Core eseguito in Service Fabric. Al termine, si avrà un'applicazione di voto con un front-end Web ASP.NET Core abilitato per HTTPS in ascolto sulla porta 443. Se non si vuole creare manualmente l'applicazione di voto come illustrato in Creare un'applicazione di Service Fabric .NET, è possibile scaricare il codice sorgente per l'applicazione completa.

Nella terza parte della serie si apprenderà come:

  • Definire un endpoint HTTPS nel servizio
  • Configurare Kestrel per l'uso di HTTPS
  • Installare il certificato TLS/SSL nei nodi del cluster remoto
  • Concedere a NETWORK SERVICE l'accesso alla chiave privata del certificato
  • Aprire la porta 443 nel servizio di bilanciamento del carico di Azure
  • Distribuire l'applicazione in un cluster remoto

In questa serie di esercitazioni si apprenderà come:

Nota

È consigliabile usare il modulo Azure Az PowerShell per interagire con Azure. Per iniziare, vedere Installare Azure PowerShell. Per informazioni su come eseguire la migrazione al modulo AZ PowerShell, vedere Eseguire la migrazione di Azure PowerShell da AzureRM ad Az.

Prerequisiti

Prima di iniziare questa esercitazione:

Ottenere un certificato o creare un certificato di sviluppo autofirmato

Per le applicazioni di produzione, usare un certificato di un'autorità di certificazione (CA). Per finalità di sviluppo e test, è possibile creare e usare un certificato autofirmato. Service Fabric SDK offre lo script CertSetup.ps1, che crea un certificato autofirmato e lo importa nell'archivio certificati Cert:\LocalMachine\My. Aprire il prompt dei comandi come amministratore ed eseguire questo comando per creare un certificato con soggetto "CN=mytestcert":

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

Se si ha già un file PFX di certificato, eseguire questo comando per importare il certificato nell'archivio certificati 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

Definire un endpoint HTTPS nel manifesto del servizio

Avviare Visual Studio come amministratore e aprire la soluzione Voting. In Esplora soluzioni aprire VotingWeb/PackageRoot/ServiceManifest.xml. Il manifesto del servizio definisce gli endpoint di servizio. Trovare la sezione Endpoints e modificare l'endpoint "ServiceEndpoint" esistente. Modificare il nome in "EndpointHttps" e impostare il protocollo su https, il tipo su Input e la porta su 443. Salvare le modifiche.

<?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>

Configurare Kestrel per l'uso di HTTPS

In Esplora soluzioni aprire il file VotingWeb/VotingWeb.cs. Configurare Kestrel per usare HTTPS e cercare il certificato nell'archivio Cert:\LocalMachine\My. Aggiungere le istruzioni using seguenti:

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

Aggiornare ServiceInstanceListener per l'uso del nuovo endpoint EndpointHttps e l'ascolto sulla porta 443. Quando si configura l'host Web per usare un server Kestrel, è necessario configurare Kestrel per l'ascolto di indirizzi IPv6 su tutte le interfacce di rete: 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();
        }))

Aggiungere anche il metodo seguente per consentire a Kestrel di trovare il certificato nell'archivio Cert:\LocalMachine\My usando il soggetto.

Sostituire "<your_CN_value>" con "mytestcert" se si è creato un certificato autofirmato con il comando di PowerShell precedente oppure usare il nome comune del certificato. Tenere presente che nel caso di una distribuzione locale a localhost è preferibile usare "CN=localhost" per evitare eccezioni di autenticazione.

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];
    }
}


Concedere a NETWORK SERVICE l'accesso alla chiave privata del certificato

In un passaggio precedente si è importato il certificato nell'archivio Cert:\LocalMachine\My nel computer di sviluppo. Ora assegnare in modo esplicito all'account che esegue il servizio (per impostazione predefinita, NETWORK SERVICE) l'accesso alla chiave privata del certificato. È possibile eseguire questo passaggio manualmente (con lo strumento certlm.msc), ma è preferibile eseguire automaticamente uno script di PowerShell configurando uno script di avvio nel nodo SetupEntryPoint del manifesto del servizio.

Nota

Service Fabric supporta la dichiarazione di certificati di endpoint in base all'identificazione personale o al nome comune del soggetto. In tal caso, il runtime configurerà l'associazione e imposterà l'elenco di controllo di accesso della chiave privata del certificato in base all'identità con cui è in esecuzione il servizio. Il runtime eseguirà inoltre il monitoraggio del certificato per le modifiche o i rinnovi e imposterà nuovamente l'elenco di controllo di accesso per la chiave privata corrispondente di conseguenza.

Configurare il punto di ingresso dell'installazione del servizio

In Esplora soluzioni aprire VotingWeb/PackageRoot/ServiceManifest.xml. Nella sezione CodePackage aggiungere il nodo SetupEntryPoint e quindi un nodo ExeHost. In ExeHost impostare Program su "Setup.bat" e WorkingFolder su "CodePackage". Quando verrà avviato il servizio VotingWeb, verrà eseguito lo script Setup.bat nella cartella CodePackage prima dell'avvio di 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>

Aggiungere gli script batch e di configurazione di PowerShell

Per eseguire PowerShell dal punto SetupEntryPoint, è possibile eseguire PowerShell.exe in un file batch che punta a un file di PowerShell. Per prima cosa, aggiungere il file batch al progetto del servizio. In Esplora soluzioni fare clic con il pulsante destro del mouse su VotingWeb e scegliere Aggiungi nuovo>elemento e aggiungere un nuovo file denominato "Setup.bat". Modificare il file Setup.bat e aggiungere il comando seguente:

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

Modificare le proprietà del file Setup.bat impostando Copia nella directory di output su "Copia se più recente".

Impostare le proprietà del file

In Esplora soluzioni fare clic con il pulsante destro del mouse su VotingWeb e scegliere Aggiungi nuovo>elemento e aggiungere un nuovo file denominato "SetCertAccess.ps1". Modificare il file SetCertAccess.ps1 e aggiungere lo script seguente:

$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;
    }
}

Modificare le proprietà del file SetCertAccess.ps1 impostando Copia nella directory di output su "Copia se più recente".

Eseguire lo script di configurazione come amministratore locale

Per impostazione predefinita, l'eseguibile del punto di ingresso di configurazione del servizio viene eseguito con le stesse credenziali di Service Fabric (in genere, l'account NetworkService). SetCertAccess.ps1 richiede privilegi di amministratore. Nel manifesto dell'applicazione, è possibile modificare le autorizzazioni di sicurezza per eseguire lo script di avvio con un account amministratore locale.

In Esplora soluzioni aprire Voting/ApplicationPackageRoot/ApplicationManifest.xml. Per prima cosa, creare una sezione Principals e aggiungere un nuovo utente, ad esempio "SetupAdminUser". Aggiungere l'account utente SetupAdminUser al gruppo di sistema Administrators. Nella sezione ServiceManifestImport di VotingWebPkg configurare quindi un criterio RunAsPolicy per applicare l'entità di sicurezza SetupAdminUser al punto di ingresso di configurazione. Il criterio indica a Service Fabric che il file Setup.bat verrà eseguito con l'account SetupAdminUser, con privilegi di amministratore.

<?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>

Eseguire l'applicazione in locale

In Esplora soluzioni selezionare l'applicazione Voting e impostare la proprietà URL applicazione su "https://localhost:443".

Salvare tutti i file e premere F5 per eseguire l'applicazione in locale. Dopo la distribuzione dell'applicazione, viene aperto un Web browser https://localhost:443. Se si usa un certificato autofirmato, verrà visualizzato un avviso che informa che la sicurezza del sito Web non è considerata attendibile dal PC. Continuare e passare alla pagina Web.

Screenshot dell'app Di esempio di voto di Service Fabric in esecuzione in una finestra del browser con l'URL https://localhost/.

Installare il certificato nei nodi del cluster

Prima di distribuire l'applicazione in Azure, installare il certificato nell'archivio Cert:\LocalMachine\My di tutti i nodi del cluster remoto. I servizi possono essere spostati in nodi diversi del cluster. All'avvio del servizio Web front-end in un nodo del cluster, lo script di avvio cercherà il certificato e configurerà le autorizzazioni di accesso.

Per prima cosa, esportare il certificato in un file PFX. Aprire l'applicazione certlm.msc e passare a Personale>Certificati. Fare clic con il pulsante destro del mouse sul certificato di mytestcert e scegliere Tutte le attività>Esporta.

Esportare il certificato

Nella procedura guidata di esportazione scegliere Sì, esporta la chiave privata e quindi il formato PFX (Personal Information Exchange). Esportare il file in C:\Users\sfuser\votingappcert.pfx.

Installare quindi il certificato nel cluster remoto usando questi script di PowerShell forniti.

Avviso

Per le applicazioni di sviluppo e test è sufficiente un certificato autofirmato. Per le applicazioni di produzione, usare invece un certificato di un'autorità di certificazione (CA).

Aprire la porta 443 nel servizio di bilanciamento del carico di Azure e nella rete virtuale

Aprire la porta 443 nel servizio di bilanciamento del carico, se non è già aperta.

$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

Eseguire la stessa operazione per la rete virtuale associata.

$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

Distribuzione dell'applicazione in Azure

Salvare tutti i file, passare da Debug a Release e premere F6 per ricompilare. In Esplora soluzioni fare clic con il pulsante destro del mouse su Voting e scegliere Pubblica. Selezionare l'endpoint di connessione del cluster creato in Distribuire un'applicazione in un cluster oppure selezionare un altro cluster. Fare clic su Pubblica per pubblicare l'applicazione nel cluster remoto.

Al termine della distribuzione dell'applicazione, aprire un Web browser e passare a https://mycluster.region.cloudapp.azure.com:443, aggiornando l'URL con l'endpoint di connessione del cluster. Se si usa un certificato autofirmato, verrà visualizzato un avviso che informa che la sicurezza del sito Web non è considerata attendibile dal PC. Continuare e passare alla pagina Web.

Screenshot dell'app Di esempio di voto di Service Fabric in esecuzione in una finestra del browser con l'URL https://mycluster.region.cloudapp.azure.com:443.

Passaggi successivi

In questa parte dell'esercitazione si è appreso come:

  • Definire un endpoint HTTPS nel servizio
  • Configurare Kestrel per l'uso di HTTPS
  • Installare il certificato TLS/SSL nei nodi del cluster remoto
  • Concedere a NETWORK SERVICE l'accesso alla chiave privata del certificato
  • Aprire la porta 443 nel servizio di bilanciamento del carico di Azure
  • Distribuire l'applicazione in un cluster remoto

Passare all'esercitazione successiva: