Compartir vía


Tutorial: agregar un punto de conexión HTTPS para una aplicación de Service Fabric mediante Kestrel

Este tutorial es la tercera parte de una serie. Más información sobre cómo agregar un punto de conexión HTTPS en un servicio de ASP.NET Core que se ejecuta en Azure Service Fabric. Cuando haya terminado tendrá una aplicación de votación con un front-end web de ASP.NET Core habilitado para HTTPS que escucha en el puerto 443. Si no desea crear manualmente la aplicación de votación de la primera parte de la serie de tutoriales, puede descargar el código fuente para obtener la aplicación terminada.

En este tutorial, aprenderá a:

  • Definir un punto de conexión HTTPS en el servicio
  • Configurar Kestrel para usar HTTPS
  • Instalar el certificado TLS/SSL en los nodos del clúster remoto
  • Conceder a NetworkService acceso a la clave privada del certificado
  • Abrir el puerto 443 en Azure Load Balancer
  • Implementar la aplicación en un clúster remoto

En esta serie de tutoriales se muestra cómo realizar las siguientes acciones:

Nota:

Se recomienda usar el módulo Azure Az de PowerShell para interactuar con Azure. Para comenzar, consulte Instalación de Azure PowerShell. Para más información sobre cómo migrar al módulo Az de PowerShell, consulte Migración de Azure PowerShell de AzureRM a Az.

Prerrequisitos

Antes de empezar este tutorial:

Obtener un certificado o crear de un certificado de desarrollo autofirmado

Para aplicaciones de producción, use un certificado de una entidad de certificación (CA). Con fines de desarrollo y pruebas, puede crear y usar un certificado autofirmado. El SDK de Service Fabric incluye el script CertSetup.ps1. El script crea un certificado autofirmado y lo importa al almacén de certificados Cert:\LocalMachine\My. Abra una ventana del símbolo del sistema como administrador y ejecute el siguiente comando para crear un certificado con el asunto "CN=mytestcert":

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

Si ya tiene un archivo de intercambio de información personal (PFX) certificado, ejecute lo siguiente para importar el certificado en el almacén 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

Definición de un punto de conexión HTTPS en el manifiesto de servicio

Abra Visual Studio mediante la opción Ejecutar como administrador y, a continuación, abra la solución Voting. En el Explorador de soluciones, abra VotingWeb/PackageRoot/ServiceManifest.xml. El manifiesto del servicio define los puntos de conexión del servicio. Busque la sección Endpoints y edite el valor del punto de conexión ServiceEndpoint. Cambie el nombre a EndpointHttps, establezca el protocolo en https, el tipo en Inputy el puerto en 443. Guarde los cambios.

<?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 Kestrel para que use HTTPS

En el Explorador de soluciones, abra el archivo VotingWeb/VotingWeb.cs. Configure Kestrel para usar HTTPS y buscar el certificado en el almacén Cert:\LocalMachine\My. Agregue las instrucciones siguientes using :

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

Actualice el valor de ServiceInstanceListener para que use el nuevo punto de conexión EndpointHttps y escuche en el puerto 443. Al configurar el host de web para usar el servidor Kestrel, debe configurar Kestrel para escuchar las direcciones IPv6 en todas las interfaces de red: 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();
        }))

Luego, agregue el método siguiente para que Kestrel pueda encontrar el certificado en el almacén Cert:\LocalMachine\My mediante el asunto.

Reemplace <your_CN_value> por mytestcert si ha creado un certificado autofirmado usando el comando de PowerShell anterior o use el nombre CN del certificado.

Si usa una implementación local para localhost, se recomienda usar CN=localhost para evitar excepciones de autenticación.

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 acceso al Servicio de red a la clave privada del certificado

En un paso anterior, importó el certificado al almacén Cert:\LocalMachine\My en el equipo de desarrollo.

Ahora, proporcione explícitamente a la cuenta que ejecuta el servicio (servicio de red, de forma predeterminada) acceso a la clave privada del certificado. Puede hacerlo manualmente (mediante la herramienta certlm.msc), pero es mejor ejecutar un script de PowerShell realizando la configuración de un script de inicio en el SetupEntryPoint del manifiesto de servicio.

Nota:

Service Fabric admite la declaración de certificados de punto de conexión por huella digital o nombre común del firmante. En ese caso, el runtime configura el enlace y la asignación de la clave privada del certificado para la identidad en la que se ejecuta el servicio. El runtime también supervisa el certificado en busca de cambios y renovaciones y actualizaciones de asignación de la clave privada correspondiente.

Configuración del punto de entrada de instalación del servicio

En el Explorador de soluciones, abra VotingWeb/PackageRoot/ServiceManifest.xml. En la sección CodePackage, agregue el nodo SetupEntryPoint y, a continuación, agregue un nodo ExeHost. En ExeHost, establezca Program en Setup.bat y establezca WorkingFolder en CodePackage. Cuando se inicia el servicio VotingWeb, el script Setup.bat se ejecuta en la carpeta CodePackage antes de que se inicie 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>

Incorporación del lote y de los scripts de configuración de PowerShell

Para ejecutar PowerShell desde el valor de SetupEntryPoint, puede ejecutar PowerShell.exe en un archivo por lotes que apunte al archivo de PowerShell.

En primer lugar, agregue el archivo por lotes al proyecto de servicio. En el Explorador de soluciones, haga clic con el botón derecho en VotingWeb y seleccione Agregar>Nuevo elemento. Agregue un nuevo archivo denominado Setup.bat. Edite el archivo Setup.bat y agregue el siguiente comando:

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

Modifique las propiedades del archivo Setup.bat para establecer la opción Copiar en el directorio de salida en Copiar si es posterior.

Captura de pantalla que muestra la configuración de las propiedades del archivo.

En el Explorador de soluciones, haga clic con el botón derecho en VotingWeb. Luego, seleccione Agregar>Nuevo elemento y agregue un nuevo archivo denominado SetCertAccess.ps1. Edite el archivo SetCertAccess.ps1 para agregar el siguiente 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 las propiedades del archivo SetCertAccess.ps1 para establecer Copiar en el directorio de salida en Copiar si es posterior.

Ejecutar el script de instalación como administrador

De forma predeterminada, el ejecutable de punto de entrada del programa de instalación del servicio se ejecuta usando las mismas credenciales que Service Fabric (normalmente la cuenta de servicio de red). El archivo SetCertAccess.ps1 requiere permisos de administrador. En el manifiesto de aplicación, puede cambiar los permisos de seguridad para ejecutar el script de inicio con una cuenta de administrador local.

En el Explorador de soluciones, abra Voting/ApplicationPackageRoot/ApplicationManifest.xml. En primer lugar, cree una sección Principals y agregue un nuevo usuario (por ejemplo, SetupAdminUser). Agregue la cuenta de usuario SetupAdminUser al grupo de administradores del sistema.

A continuación, en la sección ServiceManifestImport de VotingWebPkg, configure una directiva RunAsPolicy para aplicar la entidad de seguridad SetupAdminUser al punto de entrada de la instalación. Esta directiva indica a Service Fabric que el archivo Setup.bat se debe ejecutar como SetupAdminUser (con permisos 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>

Ejecutar la aplicación localmente

En el Explorador de soluciones, seleccione la aplicación Voting y establezca la propiedad Application URL en https://localhost:443.

Guarde todos los archivos y presione la tecla F5 para ejecutar la aplicación localmente. Después de que se implementa la aplicación, se abre un explorador en https://localhost:443. Si va a usar un certificado autofirmado, aparecerá una advertencia que le indica que el PC no confía en la seguridad de este sitio web. Continúe con la página web.

Captura de pantalla que muestra la aplicación de ejemplo Voting de Service Fabric que se ejecuta en un explorador y la URL del localhost.

Instalación del certificado en los nodos de clúster

Antes de implementar la aplicación en Azure, instale el certificado en el almacén Cert:\LocalMachine\My de todos los nodos de clúster remotos. Los servicios se pueden mover a diferentes nodos del clúster. Cuando se inicia el servicio web de front-end en un nodo de clúster, el script de inicio busca el certificado y configura los permisos de acceso.

Para instalar el certificado en los nodos del clúster, primero exporte el certificado como un archivo PFX. Abra el archivo de aplicación certlm.msc y vaya a Personal>Certificados. Haga clic con el botón derecho en el certificado mytestcert y seleccione Todas las tareas>Exportar.

Captura de pantalla que muestra la exportación del certificado.

En el Asistente para exportar, seleccione Sí, exportar la clave privada y, a continuación, seleccione el formato PFX. Exporte el archivo a C:\Users\sfuser\votingappcert.pfx.

A continuación, instale el certificado en el clúster remoto mediante scripts de PowerShell.

Advertencia

Un certificado autofirmado es suficiente para desarrollar y probar aplicaciones. Para aplicaciones de producción, use un certificado de una entidad de certificación (CA) en vez de usar un certificado autofirmado.

Apertura del puerto 443 en Azure Load Balancer y la red virtual

Abra el puerto 443 en el equilibrador de carga si no está abierto:

$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

Haga lo mismo para la red virtual asociada:

$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 la aplicación en Azure

Guarde todos los archivos, cambie de depurar a liberar y seleccione F6 para recompilar. En el Explorador de soluciones, haga clic con el botón derecho en Voting y seleccione Publicar. Seleccione el punto de conexión del clúster creado en Implementar una aplicación en un clúster., o seleccione otro clúster. Seleccione Publicar para publicar la aplicación en el clúster remoto.

Cuando se implemente la aplicación, abra un explorador web y vaya a https://mycluster.region.cloudapp.azure.com:443 (actualice la dirección URL con el punto de conexión del clúster). Si va a usar un certificado autofirmado, aparecerá una advertencia que le indica que el PC no confía en la seguridad de este sitio web. Continúe con la página web.

Captura de pantalla que muestra la aplicación de ejemplo Voting de Service Fabric que se ejecuta en una ventana del explorador.

Paso siguiente

Avance hasta el siguiente tutorial: