Condividi tramite


Esercitazione: Aggiungere un endpoint HTTPS per un'applicazione di Service Fabric usando Kestrel

Questa esercitazione è la terza parte di una serie. Informazioni su come aggiungere un endpoint HTTPS in un servizio ASP.NET Core in esecuzione in Azure Service Fabric. Al termine, si dispone di un'applicazione di voto con un front-end Web di ASP.NET Core abilitato per HTTPS in ascolto sulla porta 443. Se non si vuole creare manualmente l'applicazione di voto nella prima parte della serie di esercitazioni, è possibile scaricare il codice sorgente per ottenere l'applicazione completata.

In questa esercitazione apprenderai a:

  • 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 NetworkService 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

La serie di esercitazioni mostra 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 include lo script CertSetup.ps1 . Lo script crea un certificato autofirmato e lo importa nell'archivio certificati Cert:\LocalMachine\My . Aprire una finestra del prompt dei comandi come amministratore ed eseguire il comando seguente per creare un certificato con l'oggetto "CN=mytestcert":

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

Se si dispone già di un file PFX (Personal Information Exchange), eseguire quanto segue 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

Aprire Visual Studio usando l'opzione Esegui come amministratore e quindi aprire la soluzione Voting. In Esplora soluzioni aprire VotingWeb/PackageRoot/ServiceManifest.xml. Il manifesto del servizio definisce gli endpoint di servizio. Trovare la Endpoints sezione e modificare il valore per ServiceEndpoint l'endpoint. Modificare il nome in EndpointHttps, impostare il protocollo su https, il tipo su Inpute la porta su 443. Salva 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 l'uso di HTTPS e per 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 il valore per per ServiceInstanceListener l'uso del nuovo EndpointHttps endpoint e per l'ascolto sulla porta 443. Quando si configura l'host Web per l'uso del server Kestrel, è necessario configurare Kestrel per l'ascolto degli indirizzi IPv6 in 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 quindi il metodo seguente in modo che Kestrel possa trovare il certificato nell'archivio Cert:\LocalMachine\My usando l'oggetto .

Sostituire <your_CN_value> con mytestcert se è stato creato un certificato autofirmato usando il comando di PowerShell precedente o usare il nome comune del certificato.

Se si usa una distribuzione locale in localhost, è consigliabile 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 al servizio di rete l'accesso alla chiave privata del certificato

In un passaggio precedente il certificato è stato importato nell'archivio Cert:\LocalMachine\My nel computer di sviluppo.

A questo punto, assegnare in modo esplicito all'account che esegue il servizio (servizio di rete, per impostazione predefinita) l'accesso alla chiave privata del certificato. È possibile eseguire questo passaggio manualmente (usando lo strumento certlm.msc ), ma è preferibile eseguire uno script di PowerShell configurando uno script di avvio nel SetupEntryPoint manifesto del servizio.

Nota

Service Fabric supporta la dichiarazione di certificati endpoint tramite identificazione personale o nome comune del soggetto. In tal caso, il runtime configura l'associazione e l'allocazione per la chiave privata del certificato sull'identità in cui è in esecuzione il servizio. Il runtime monitora anche il certificato per le modifiche, i rinnovi e gli aggiornamenti di allocazione per la chiave privata corrispondente.

Configurare il punto di ingresso dell'installazione del servizio

In Esplora soluzioni aprire VotingWeb/PackageRoot/ServiceManifest.xml. CodePackage Nella sezione aggiungere il SetupEntryPoint nodo e quindi aggiungere un ExeHost nodo. In ExeHostimpostare su Program Setup.bate su WorkingFolder CodePackage. All'avvio del servizio VotingWeb, lo script Setup.bat viene eseguito nella cartella CodePackage prima dell'avvio 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 valore per 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 quindi scegliere Aggiungi>nuovo elemento. 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à per il file Setup.bat per impostare Copia nella directory di output su Copia se più recente.

Screenshot che mostra la configurazione delle proprietà del file.

In Esplora soluzioni fare clic con il pulsante destro del mouse su VotingWeb. Selezionare quindi Aggiungi>nuovo elemento e aggiungere un nuovo file denominato SetCertAccess.ps1. Modificare il file SetCertAccess.ps1 per 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à per il file SetCertAccess.ps1 per impostare Copia nella directory di output su Copia se più recente.

Eseguire lo script di installazione come amministratore

Per impostazione predefinita, l'eseguibile del punto di ingresso dell'installazione del servizio viene eseguito usando le stesse credenziali di Service Fabric(in genere, l'account del servizio di rete). SetCertAccess.ps1 richiede autorizzazioni 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. Creare prima di tutto una Principals sezione e aggiungere un nuovo utente , ad esempio SetupAdminUser. Aggiungere l'account utente SetupAdminUser al gruppo di sistema Administrators.

Successivamente, nella ServiceManifestImport sezione VotingWebPkg configurare runAsPolicy per applicare l'entità SetupAdminUser al punto di ingresso dell'installazione. Questo criterio indica a Service Fabric che il file Setup.bat viene eseguito come SetupAdminUser (con autorizzazioni 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à APPLICATION URL su https://localhost:443.

Salvare tutti i file e quindi selezionare F5 per eseguire l'applicazione in locale. Dopo la distribuzione dell'applicazione, viene aperto un browser su https://localhost:443. Se si usa un certificato autofirmato, viene visualizzato un avviso che indica che il PC non considera attendibile la sicurezza di questo sito Web. Continuare con la pagina Web.

Screenshot che mostra l'app Service Fabric Voting Sample in esecuzione in un browser e l'URL 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. Quando il servizio Web front-end viene avviato in un nodo del cluster, lo script di avvio cerca il certificato e configura le autorizzazioni di accesso.

Per installare il certificato nei nodi del cluster, esportare prima di tutto il certificato come file PFX. Aprire il file dell'applicazione certlm.msc e passare a Certificati personali>. Fare clic con il pulsante destro del mouse sul certificato mytestcert e quindi scegliere Tutte le attività>esportate.

Screenshot che mostra l'esportazione del certificato.

Nella procedura guidata di esportazione selezionare Sì, esportare la chiave privata e quindi selezionare il formato PFX. Esportare il file in C:\Users\sfuser\votingappcert.pfx.

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

Avviso

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

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

Distribuire l'applicazione in Azure

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

Quando l'applicazione viene distribuita, aprire un Web browser e passare a https://mycluster.region.cloudapp.azure.com:443 (aggiornare l'URL con l'endpoint di connessione per il cluster). Se si usa un certificato autofirmato, viene visualizzato un avviso che indica che il PC non considera attendibile la sicurezza di questo sito Web. Continuare con la pagina Web.

Screenshot che mostra l'app Service Fabric Voting Sample in esecuzione in una finestra del browser.

Passaggio successivo

Passare all'esercitazione successiva: