Partager via


Tutoriel : Ajouter un point de terminaison HTTPS pour une application Service Fabric avec Kestrel

Ce tutoriel est la troisième partie d’une série. Découvrez comment ajouter un point de terminaison HTTPS dans un service ASP.NET Core s’exécutant dans Azure Service Fabric. À la fin du tutoriel, vous avez une application de vote avec un service web de front-end ASP.NET Core HTTPS écoutant sur le port 443. Si vous ne voulez pas créer manuellement l’application de vote en suivant la première partie de la série de tutoriels, vous pouvez télécharger le code source pour obtenir l’application terminée.

Dans ce tutoriel, vous allez apprendre à :

  • Définir un point de terminaison HTTPS dans le service
  • Configurer Kestrel pour utiliser HTTPS
  • Installer le certificat TLS/SSL sur les nœuds du cluster distant
  • Accorder l’accès NetworkService à la clé privée du certificat
  • Ouvrir le port 443 dans l’équilibreur de charge Azure
  • Déployer l’application sur un cluster distant

La série de tutoriels vous montre comment :

Notes

Nous vous recommandons d’utiliser le module Azure Az PowerShell pour interagir avec Azure. Pour bien démarrer, consultez Installer Azure PowerShell. Pour savoir comment migrer vers le module Az PowerShell, consultez Migrer Azure PowerShell depuis AzureRM vers Az.

Prérequis

Avant de commencer ce tutoriel :

Obtenir un certificat ou créer un certificat de développement auto-signé

Pour les applications de production, utilisez un certificat délivré par une autorité de certification (AC). Dans le cadre d’opérations de développement et de test, vous pouvez créer et utiliser un certificat auto-signé. Le SDK Service Fabric contient le script CertSetup.ps1. Le script crée un certificat auto-signé et l’importe dans le magasin de certificats Cert:\LocalMachine\My. Ouvrez une fenêtre d’invite de commandes en tant qu’administrateur, puis exécutez la commande suivante pour créer un certificat avec le sujet « CN=mytestcert » :

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

Si vous avez déjà un fichier PFX (Personal Information Exchange) de certificat, exécutez la commande suivante pour importer le certificat dans le magasin de certificats 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

Définir un point de terminaison HTTPS dans le manifeste de service

Ouvrez Visual Studio en utilisant l’option Exécuter en tant qu’administrateur, et ouvrez la solution Voting. Dans l’Explorateur de solutions, ouvrez VotingWeb/PackageRoot/ServiceManifest.xml. Le manifeste de service définit les points de terminaison de service. Recherchez la section Endpoints et modifiez la valeur du point de terminaison ServiceEndpoint. Remplacez le nom par EndpointHttps, définissez le protocole sur https, le type sur Input et le port sur 443. Enregistrez vos modifications.

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

Configurer Kestrel pour l’utilisation de HTTPS

Dans l’Explorateur de solutions, ouvrez le fichier VotingWeb/VotingWeb.cs. Configurez Kestrel pour utiliser HTTPS et recherchez le certificat dans le magasin Cert:\LocalMachine\My. Ajoutez les instructions using suivantes :

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

Mettez à jour la valeur de ServiceInstanceListener pour utiliser le nouveau point de terminaison EndpointHttps et écouter sur le port 443. Quand vous configurer l’hôte web pour utiliser le serveur Kestrel, vous devez configurer Kestrel pour écouter les adresses IPv6 sur toutes les interfaces réseau : 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();
        }))

Ensuite, ajoutez la méthode suivante pour que Kestrel puisse trouver le certificat dans le magasin Cert:\LocalMachine\My en utilisant l’objet.

Remplacez <your_CN_value> par mytestcert si vous avez créé un certificat auto-signé avec la commande PowerShell précédente, ou utilisez le CN de votre certificat.

Si vous utilisez un déploiement local sur localhost, nous vous recommandons d’utiliser CN=localhost pour éviter les exceptions d’authentification.

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


Accorder l’accès NetworkService à la clé privée du certificat

Dans une étape antérieure, vous avez importé le certificat dans le magasin Cert:\LocalMachine\My sur l’ordinateur de développement.

Maintenant, donnez explicitement au compte exécutant le service un accès (NetworkService, par défaut) à la clé privée du certificat. Vous pouvez effectuer cette étape manuellement (avec l’outil certlm.msc), mais il vaut mieux exécuter un script PowerShell en configurant un script de démarrage dans la section SetupEntryPoint du manifeste de service.

Remarque

Service Fabric prend en charge la déclaration des certificats de point de terminaison par empreinte ou par nom commun d’objet. Dans ce cas, le runtime configure la liaison et l’allocation de la clé privée du certificat à l’identité sous laquelle le service s’exécute. Le runtime monitore également le certificat pour détecter les changements, les renouvellements et les mises à jour d’allocation pour la clé privée correspondante.

Configurer le point d’entrée d’installation du service

Dans l’Explorateur de solutions, ouvrez VotingWeb/PackageRoot/ServiceManifest.xml. Dans la section CodePackage, ajoutez le nœud SetupEntryPoint, puis un nœud ExeHost. Dans ExeHost, définissez Program sur Setup.bat, puis WorkingFolder sur CodePackage. Au démarrage du service VotingWeb, le script Setup.bat s’exécute dans le dossier CodePackage avant que VotingWeb.exe ne démarre.

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

Ajouter le fichier de commandes et les scripts d’installation PowerShell

Pour exécuter PowerShell à partir de la valeur de SetupEntryPoint, vous pouvez exécuter PowerShell.exe dans un fichier de commandes qui pointe vers un fichier PowerShell.

Commencez par ajouter le fichier de commandes au projet de service. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur VotingWeb, puis sélectionnez Ajouter>Nouvel élément. Ajoutez un nouveau fichier nommé Setup.bat. Modifiez le fichier Setup.bat et ajoutez la commande suivante :

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

Modifiez les propriétés du fichier Setup.bat en définissant Copier dans le répertoire de sortie sur Copier si plus récent.

Capture d’écran montrant la configuration des propriétés de fichier.

Dans l’Explorateur de solutions, cliquez avec le bouton droit sur VotingWeb. Ensuite, sélectionnez Ajouter>Nouvel élément, puis ajoutez un nouveau fichier nommé SetCertAccess.ps1. Modifiez le fichier SetCertAccess.ps1 pour ajouter le script suivant :

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

Modifiez les propriétés du fichier SetCertAccess.ps1 pour définir Copier dans le répertoire de sortie sur Copier si plus récent.

Exécuter le script de configuration an tant qu’administrateur

Par défaut, l’exécutable du point d’entrée de configuration du service s’exécute avec les mêmes informations d’identification que Service Fabric (généralement, le compte NetworkService). SetCertAccess.ps1 nécessite des autorisations d’administrateur. Dans le manifeste de l’application, vous pouvez changer les autorisations de sécurité de manière à exécuter le script de démarrage sous un compte d’administrateur local.

Dans l’Explorateur de solutions, ouvrez Voting/ApplicationPackageRoot/ApplicationManifest.xml. Commencez par créer une section Principals et ajoutez un nouvel utilisateur (par exemple, SetupAdminUser). Ajoutez le compte d’utilisateur SetupAdminUser au groupe des administrateurs système.

Ensuite, dans VotingWebPkg, dans la section ServiceManifestImport, configurez un RunAsPolicy pour appliquer le principal SetupAdminUser au point d’entrée de configuration. Cette stratégie indique à Service Fabric que le fichier Setup.bat s’exécute en tant que SetupAdminUser (avec des autorisations d’administrateur).

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

Exécuter localement l’application

Dans l’Explorateur de solutions, sélectionnez l’application Voting et définissez la propriété URL de l’application sur https://localhost:443.

Enregistrez tous les fichiers, puis sélectionnez F5 pour exécuter l’application localement. Une fois l’application déployée, un navigateur s’ouvre sur https://localhost:443. Si vous utilisez un certificat auto-signé, vous voyez un avertissement indiquant que votre PC n’approuve pas la sécurité de ce site web. Accédez à la page web.

Capture d’écran montrant l’exemple d’application Voting Service Fabric s’exécutant dans un navigateur et l’URL localhost.

Installer le certificat sur les nœuds de cluster

Avant de déployer l’application dans Azure, installez le certificat dans le magasin Cert:\LocalMachine\My de tous les nœuds du cluster distant. Vous pouvez déplacer les services d’un nœud à l’autre du cluster. Quand le service web de front-end démarre sur un nœud de cluster, le script de démarrage recherche le certificat et configure les autorisations d’accès.

Pour installer le certificat sur les nœuds de cluster, exportez d’abord le certificat sous forme de fichier PFX. Ouvrez le fichier d'application certlm.msc, puis accédez à Personnel>Certificats. Cliquez avec le bouton droit sur le certificat mytestcert, puis sélectionnez Toutes les tâches>Exporter.

Capture d’écran montrant l’exportation du certificat.

Dans l’Assistant Exportation, sélectionnez Oui, exporter la clé privée, puis sélectionnez le format PFX. Exportez le fichier vers C:\Users\sfuser\votingappcert.pfx.

Ensuite, installez le certificat sur le cluster distant en utilisant les scripts PowerShell fournis.

Avertissement

Un certificat auto-signé suffit pour les applications de développement et de test. Pour les applications de production, utilisez un certificat d’une autorité de certification plutôt qu’un certificat auto-signé.

Ouvrir le port 443 dans l’équilibreur de charge et le réseau virtuel Azure

Ouvrez le port 443 dans l’équilibreur de charge s’il n’est pas déjà ouvert :

$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

Faites la même chose pour le réseau virtuel associé :

$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

Déployer l’application sur Azure

Enregistrez tous les fichiers, basculez du mode Débogage vers le mode Mise en production, puis sélectionnez F6 pour regénérer l’application. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur Voting, puis sélectionnez Publier. Sélectionnez le point de terminaison de connexion du cluster créé au cours du didacticiel Déployer une application sur un cluster, ou sélectionnez un autre cluster. Sélectionnez Publier pour publier l’application sur le cluster distant.

Quand l’application est déployée, ouvrez un navigateur web et accédez à https://mycluster.region.cloudapp.azure.com:443 (mettez à jour l’URL avec le point de terminaison de connexion de votre cluster). Si vous utilisez un certificat auto-signé, vous voyez un avertissement indiquant que votre PC n’approuve pas la sécurité de ce site web. Accédez à la page web.

Capture d’écran montrant l’exemple d’application Voting Service Fabric s’exécutant dans une fenêtre de navigateur.

Étape suivante

Passez au tutoriel suivant :