在 Azure Service Fabric 中为自定义 TCP 服务加载 SSL 证书

Load SSL Certificate in Azure Service Fabric for Custom TCP service

我已经创建了一个 TCP 服务,它创建了一个我尝试在 Azure Service Fabric 群集中托管的安全 SSL 连接。虽然有关于如何为站点和 API 加载和使用 SSL 证书的文档,但我似乎找不到任何关于如何为我的服务加载证书的文档。我已将我的证书加载到密钥保管库,但现在需要创建一个 X509Certificate2 实例来保护我的 TCP 连接。

我们遇到了类似的问题(我们想将 Kestrel 与 HTTPS 结合使用)并找到了两种实现方法。

  1. 使用 ARM 脚本和 Keyvault 将证书转储到 VM 的密钥库中,然后使用 X509Store 将其读出。

此方法存在问题,因为 AFAIK 服务结构在 NETWORK_SERVICE 下运行并且没有读取私钥的权限。

  1. 我们的后备方案是从密钥库中读取证书和密码作为机密,然后创建一个新的 X509Certificate2。为此,我们授予 Azure AD 服务主体访问权限以读取机密,然后使用 KeyVaultClient 获取机密。

这里有更好的解释 - https://azure.microsoft.com/en-gb/documentation/articles/key-vault-use-from-web-application

如果有人可以建议在这种情况下什么是最佳实践,我会很高兴 - 我认为第二个选项存在问题,因为创建新的 X509Certificate2 还会在磁盘上未清理的临时位置创建证书,但我确定还有更多。

当我最初创建集群时,它是一个不安全的集群。只需将其重新创建为受保护的集群,我现在就可以使用我创建的以下辅助方法访问用于保护集群的保险库中的证书。

    private static X509Certificate GetServerCertificate(string thumbprint)
    {
        string thumbprint = CertificateThumbprint;
        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);
        var certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

        if (certificateCollection.Count == 0)
        {
            store.Close();
            string errorMessage = string.Format("Unable to load certificate with thumbprint {0}", thumbprint);
            throw new ApplicationException(errorMessage);
        }
        else
        {
            var certificate = new X509Certificate2(certificateCollection[0]);
            store.Close();
            return certificate;
        }
    }

有关创建安全集群的说明可在此处找到:https://azure.microsoft.com/en-us/documentation/articles/service-fabric-cluster-creation-via-arm/

一个选项是 运行 作为 SYSTEM 的 Service Fabric 设置任务(只有 SYSTEM 有权更改证书上的 ACL)并提供 NETWORK SERVICE读取您的证书的权限

下面的完整示例,注意我在所有 LocalMachine / 我的证书上设置了 ACL,修改 PS 脚本以仅获取您需要的证书

ServiceManifest.xml

  <CodePackage Name="Code" Version="1.0.0">
    <SetupEntryPoint>
      <ExeHost>
        <Program>setup.cmd</Program>
        <WorkingFolder>CodePackage</WorkingFolder>
      </ExeHost>
    </SetupEntryPoint>
    <EntryPoint>
      <ExeHost>
        <Program>XXX.exe</Program>
        <WorkingFolder>CodePackage</WorkingFolder>
      </ExeHost>
    </EntryPoint>
  </CodePackage>

setup.cmd

powershell.exe -NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File configure-certs.ps1

configure-certs.ps1

$store = New-Object System.Security.Cryptography.X509Certificates.X509Store @(
    [System.Security.Cryptography.X509Certificates.StoreName]::My,
    [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)

try
{
    $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
    $certs = $store.Certificates

    $rule = New-Object System.Security.AccessControl.FileSystemAccessRule @(
        "NETWORK SERVICE",
        [System.Security.AccessControl.FileSystemRights]::Read,
        [System.Security.AccessControl.AccessControlType]::Allow)

    foreach ($cert in $certs)
    {
        $privateKeyPath = "$($ENV:ProgramData)\Microsoft\Crypto\RSA\MachineKeys$($cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
        $privateKeyInfo = New-Object System.IO.FileInfo @($privateKeyPath)

        $privateKeyAccessControl = $privateKeyInfo.GetAccessControl()
        $privateKeyAccessControl.AddAccessRule($rule)

        $privateKeyInfo.SetAccessControl($privateKeyAccessControl)
    }
}
finally
{
    $store.Close()
}

ApplicationManifest.xml

ConfigOverrides 作为您要添加设置的包的 ServiceManifestImport 的子项之后

...
    <Policies>
      <RunAsPolicy CodePackageRef="Code" UserRef="LocalSystem" EntryPointType="Setup" />
    </Policies>
...

DefaultServices 之后作为 ApplicationManifest

的子项
...
      <Principals>
        <Users>
          <User Name="LocalSystem" AccountType="LocalSystem" />
        </Users>
      </Principals>
...

实际上有一种更简单的方法来确保 Service Fabric(例如默认 Network_Service)有权访问您的证书。

我 运行 一个本地 Service Fabric 集群,过去我不得不做类似的事情,因为我使用证书来加密应用程序机密。

基本上,您只需将应用程序清单更新为

  1. <Principals>
  2. 中定义网络帐户(用于访问证书)
  3. 添加 <SecurityAccessPolicy> 将网络帐户与证书相关联
  4. <Certificates>
  5. 中识别证书

示例ApplicationManifest.xml 摘录:

<Principals>
  <Users>
    <User Name="DefaultServiceAccount" AccountType="NetworkService" />
  </Users>
</Principals>
<Policies>
  <SecurityAccessPolicies>
    <SecurityAccessPolicy ResourceRef="AppSecretsCert" PrincipalRef="DefaultServiceAccount" ResourceType="Certificate" />
  </SecurityAccessPolicies>
</Policies>
<Certificates>
  <SecretsCertificate X509FindValue="YOUR CERT THUMPRINT" Name="AppSecretsCert" />
</Certificates>