البرنامج التعليمي: إضافة نقطة نهاية HTTPS لتطبيق Service Fabric باستخدام Kestrel

هذا البرنامج التعليمي هو الجزء الثالث في سلسلة. تعرف على كيفية إضافة نقطة نهاية HTTPS في خدمة ASP.NET Core قيد التشغيل في Azure Service Fabric. عند الانتهاء، يكون لديك تطبيق تصويت يحتوي على واجهة ويب ASP.NET Core التي تدعم HTTPS والتي تستمع على المنفذ 443. إذا كنت لا تريد إنشاء تطبيق التصويت يدويا في الجزء الأول من سلسلة البرامج التعليمية، يمكنك تنزيل التعليمات البرمجية المصدر للحصول على التطبيق المكتمل.

في هذا البرنامج التعليمي، تتعلم كيفية:

  • تعريف نقطة نهاية HTTPS في الخدمة
  • إعداد Kestrel لاستخدام HTTPS
  • تثبيت شهادة TLS/SSL على عقد نظام المجموعة البعيدة
  • منح NetworkService حق الوصول إلى المفتاح الخاص للشهادة
  • فتح منفذ 443 في موازن تحميل Azure
  • نشر التطبيق على نظام المجموعة البعيدة

توضح لك سلسلة البرامج التعليمية كيفية:

إشعار

نوصي باستخدام الوحدة النمطية Azure Az PowerShell للتفاعل مع Azure. للبدء، راجع تثبيت Azure PowerShell. لمعرفة كيفية الترحيل إلى الوحدة النمطية Az PowerShell، راجع ترحيل Azure PowerShell من AzureRM إلى Az.

المتطلبات الأساسية

قبل أن تبدأ هذا البرنامج التعليمي:

الحصول على شهادة أو إنشاء شهادة تطوير موقعة ذاتيا

بالنسبة لتطبيقات الإنتاج، استخدم شهادة من مرجع مصدق (CA). لأغراض التطوير والاختبار، يمكنك إنشاء شهادة موقعة ذاتياً واستخدامها. يتضمن Service Fabric SDK البرنامج النصي CertSetup.ps1 . يقوم البرنامج النصي بإنشاء شهادة موقعة ذاتيا واستيرادها إلى مخزن الشهادات Cert:\LocalMachine\My . افتح نافذة موجه الأوامر كمسؤول وقم بتشغيل الأمر التالي لإنشاء شهادة تحتوي على الموضوع "CN=mytestcert":

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

إذا كان لديك بالفعل ملف Certificate Personal Information Exchange (PFX)، فقم بتشغيل ما يلي لاستيراد الشهادة إلى Cert:\LocalMachine\My certificate store:


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

تعريف نقطة نهاية HTTPS في بيان الخدمة

افتح Visual Studio باستخدام الخيار تشغيل كمسؤول ، ثم افتح حل التصويت. في مستكشف الحلول، يرجى فتح VotingWeb/PackageRoot/ServiceManifest.xml. يُعرّف بيان الخدمة نقاط نهاية الخدمة. ابحث عن Endpoints المقطع وقم بتحرير قيمة ServiceEndpoint نقطة النهاية. غير الاسم إلى EndpointHttps، واضبط البروتوكول على https، والنوع إلى Input، والمنفذ إلى 443. احفظ تغييراتك.

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

تكوين Kestrel لاستخدام HTTPS

فتح ملف VotingWeb/VotingWeb.cs في مستكشف الحلول. قم بتكوين Kestrel لاستخدام HTTPS والبحث عن الشهادة في Cert:\LocalMachine\My store. أضف عبارات using التالية:

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

قم بتحديث قيمة ServiceInstanceListener لاستخدام نقطة النهاية الجديدة EndpointHttps والاستماع على المنفذ 443. عند إعداد مضيف الويب لاستخدام خادم Kestrel، يجب تكوين Kestrel للاستماع إلى عناوين IPv6 على جميع واجهات الشبكة: 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();
        }))

بعد ذلك، أضف الأسلوب التالي بحيث يمكن ل Kestrel العثور على الشهادة في Cert:\LocalMachine\My store باستخدام الموضوع.

استبدل <your_CN_value> ب mytestcert إذا قمت بإنشاء شهادة موقعة ذاتيا باستخدام أمر PowerShell السابق، أو استخدم CN لشهادتك.

إذا كنت تستخدم نشرا محليا إلى localhost، نوصي باستخدام CN=localhost لتجنب استثناءات المصادقة.

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


منح خدمة الشبكة حق الوصول إلى المفتاح الخاص للشهادة

في خطوة سابقة، قمت باستيراد الشهادة إلى Cert:\LocalMachine\My store على كمبيوتر التطوير.

الآن، امنح الحساب الذي يقوم بتشغيل الخدمة (خدمة الشبكة، بشكل افتراضي) حق الوصول إلى المفتاح الخاص للشهادة. يمكنك القيام بهذه الخطوة يدويا (باستخدام أداة certlm.msc )، ولكن من الأفضل تشغيل برنامج نصي PowerShell عن طريق تكوين برنامج نصي لبدء التشغيل في SetupEntryPoint بيان الخدمة.

إشعار

يدعم Service Fabric الإعلان عن شهادات نقطة النهاية بواسطة بصمة الإبهام أو حسب الاسم الشائع للموضوع. في هذه الحالة، يقوم وقت التشغيل بإعداد الربط والتخصيص للمفتاح الخاص للشهادة للهوية التي تعمل بها الخدمة. يراقب وقت التشغيل أيضا الشهادة للتغييرات والتجديدات وتحديثات التخصيص للمفتاح الخاص المقابل.

تكوين نقطة إدخال لإعداد الخدمة

في مستكشف الحلول، يرجى فتح VotingWeb/PackageRoot/ServiceManifest.xml. في CodePackage القسم ، أضف العقدة SetupEntryPoint ، ثم أضف عقدة ExeHost . في ExeHost، قم بتعيين Program إلى Setup.bat، ثم قم بتعيين WorkingFolder إلى CodePackage. عند بدء تشغيل خدمة VotingWeb، يتم تنفيذ البرنامج النصي Setup.bat في مجلد CodePackage قبل بدء 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>

إضافة الدفعة والبرامج النصية لإعداد PowerShell

لتشغيل PowerShell من قيمة ل SetupEntryPoint، يمكنك تشغيل PowerShell.exe في ملف دفعي يشير إلى ملف PowerShell.

أولاً، إضافة ملف الدفعة لمشروع الخدمة. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق VotingWeb، ثم حدد إضافة>عنصر جديد. إضافة ملف جديد يسمى Setup.bat. تحرير ملف Setup.bat وإضافة الأمر التالي:

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

تعديل خصائص ملف Setup.bat لتعيين نسخ إلى دليل الإخراج إلى نسخ إذا كان أحدث.

لقطة شاشة تعرض إعداد خصائص الملف.

في مستكشف الحلول، انقر بزر الماوس الأيمن فوق VotingWeb. ثم حدد إضافة>عنصر جديد، وأضف ملفا جديدا باسم SetCertAccess.ps1. قم بتحرير ملف SetCertAccess.ps1 لإضافة البرنامج النصي التالي:

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

تعديل خصائص ملف SetCertAccess.ps1 لتعيين نسخ إلى دليل الإخراج إلى نسخ إذا كان أحدث.

تشغيل البرنامج النصي للإعداد كمسؤول

بشكل افتراضي، يتم تشغيل نقطة إدخال إعداد الخدمة القابلة للتنفيذ باستخدام نفس بيانات الاعتماد مثل Service Fabric (عادة، حساب خدمة الشبكة). يتطلب SetCertAccess.ps1 أذونات المسؤول. في بيان التطبيق، يمكنك تغيير أذونات الأمان لتشغيل برنامج بدء التشغيل النصي ضمن حساب مسؤول محلي.

في مستكشف الحلول، يمكنك فتح Voting/ApplicationPackageRoot/ApplicationManifest.xml. أولا، أنشئ قسما Principals وأضف مستخدما جديدا (على سبيل المثال، SetupAdminUser). إضافة حساب المستخدم SetupAdminUser إلى مجموعة نظام المسؤولين.

بعد ذلك، في VotingWebPkg، في ServiceManifestImport القسم ، قم بتكوين RunAsPolicy لتطبيق الإعداد مسؤول المستخدم الأساسي على نقطة إدخال الإعداد. يخبر هذا النهج Service Fabric بأن ملف Setup.bat يعمل ك Setup مسؤول User (بأذونات المسؤول).

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

تشغيل تطبيق ويب محليًا

في مستكشف الحلول، حدد تطبيق التصويت وضبط خاصية عنوان URL للتطبيق على https://localhost:443.

احفظ جميع الملفات، ثم حدد F5 لتشغيل التطبيق محليا. بعد نشر التطبيق، يفتح مستعرض على https://localhost:443. إذا كنت تستخدم شهادة موقعة ذاتيا، فسترى تحذيرا بأن جهاز الكمبيوتر الخاص بك لا يثق في أمان موقع الويب هذا. تابع إلى صفحة الويب.

لقطة شاشة تعرض تطبيق Service Fabric Voting Sample الذي يعمل في مستعرض وعنوان URL المضيف المحلي.

تثبيت الشهادة على عقد نظام المجموعة

قبل نشر التطبيق إلى Azure، قم بتثبيت الشهادة في Cert:\LocalMachine\My store لجميع عقد نظام المجموعة البعيدة. يمكن نقل الخدمات إلى عقد مختلفة بنظام المجموعة. عند بدء تشغيل خدمة الويب الأمامية على عقدة نظام المجموعة، يبحث البرنامج النصي لبدء التشغيل عن الشهادة وتكوين أذونات الوصول.

لتثبيت الشهادة على عقد نظام المجموعة، قم أولا بتصدير الشهادة كملف PFX. افتح ملف تطبيق certlm.msc وانتقل إلى الشهادات الشخصية>. انقر بزر الماوس الأيمن فوق شهادة mytestcert، ثم حدد تصدير كافة المهام>.

لقطة شاشة توضح تصدير الشهادة.

في معالج التصدير، حدد نعم، وقم بتصدير المفتاح الخاص، ثم حدد تنسيق PFX. تصدير الملف إلى C:\Users\sfuser\votingappcert.pfx.

بعد ذلك، قم بتثبيت الشهادة على نظام المجموعة البعيد باستخدام البرامج النصية PowerShell.

تحذير

شهادة موقعة ذاتياً كافية لتطوير التطبيقات واختبارها. بالنسبة لتطبيقات الإنتاج، استخدم شهادة من مرجع مصدق (CA) بدلا من استخدام شهادة موقعة ذاتيا.

فتح منفذ 443 في موازن تحميل Azure والشبكة الظاهرية

افتح المنفذ 443 في موازن التحميل إذا لم يكن مفتوحا:

$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

قم بنفس الشيء للشبكة الظاهرية المقترنة:

$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

نشر التطبيق على Azure

احفظ جميع الملفات، والتبديل من Debug إلى Release، وحدد F6 لإعادة البناء. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق التصويت وحدد نشر. يمكن تحديد نقطة نهاية الاتصال بنظام المجموعة التي تم إنشاؤها في نشر تطبيق بنظام المجموعةأو تحديد نظام مجموعة أخرى. حدد Publish لنشر التطبيق إلى نظام المجموعة البعيد.

عند نشر التطبيق، افتح مستعرض ويب وانتقل إلى https://mycluster.region.cloudapp.azure.com:443 (تحديث عنوان URL بنقطة نهاية الاتصال للمجموعة). إذا كنت تستخدم شهادة موقعة ذاتيا، فسترى تحذيرا بأن جهاز الكمبيوتر الخاص بك لا يثق في أمان موقع الويب هذا. تابع إلى صفحة الويب.

لقطة شاشة تعرض تطبيق Service Fabric Voting Sample قيد التشغيل في نافذة المستعرض.

الخطوة التالية

تقدم إلى البرنامج التعليمي الآتي: