保护 C# 服务的服务远程处理通信Secure service remoting communications in a C# service

安全是通信最为重视的要素之一。Security is one of the most important aspects of communication. Reliable Services 应用程序框架提供了一些预先生成的通信堆栈和工具供你用来提高安全性。The Reliable Services application framework provides a few prebuilt communication stacks and tools that you can use to improve security. 本文介绍如何在 C# 服务中使用服务远程处理时提高安全性。This article discusses how to improve security when you're using service remoting in a C# service. 它基于现有的示例构建,该示例解释了如何为使用 C# 编写的 Reliable Services 设置远程处理。It builds on an existing example that explains how to set up remoting for reliable services written in C#.

若要在 C# 服务中使用服务远程处理时帮助保护服务,请遵循以下步骤:To help secure a service when you're using service remoting with C# services, follow these steps:

  1. 创建接口 IHelloWorldStateful,用于定义可供服务的远程过程调用使用的方法。Create an interface, IHelloWorldStateful, that defines the methods that will be available for a remote procedure call on your service. 服务将使用 Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime 命名空间中声明的 FabricTransportServiceRemotingListenerYour service will use FabricTransportServiceRemotingListener, which is declared in the Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime namespace. 这是可以提供远程处理功能的 ICommunicationListener 实现。This is an ICommunicationListener implementation that provides remoting capabilities.

    public interface IHelloWorldStateful : IService
    {
        Task<string> GetHelloWorld();
    }
    
    internal class HelloWorldStateful : StatefulService, IHelloWorldStateful
    {
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            return new[]{
                    new ServiceReplicaListener(
                        (context) => new FabricTransportServiceRemotingListener(context,this))};
        }
    
        public Task<string> GetHelloWorld()
        {
            return Task.FromResult("Hello World!");
        }
    }
    
  2. 添加侦听器设置和安全凭据。Add listener settings and security credentials.

    确保要用来帮助保护服务通信的证书安装在群集中的所有节点上。Make sure the certificate that you want to use to help secure your service communication is installed on all the nodes in the cluster.

    备注

    在 Linux 节点上,该证书必须以 PEM 格式的文件形式存在于 /var/lib/sfcerts 目录中。On Linux nodes, the certificate must be present as PEM-formatted files in the /var/lib/sfcerts directory. 有关详细信息,请参阅 Linux 节点上的 X.509 证书的位置和格式To learn more, see Location and format of X.509 certificates on Linux nodes.

    有两种方式可用于提供侦听器设置和安全凭据:There are two ways that you can provide listener settings and security credentials:

    1. 在服务代码中直接提供:Provide them directly in the service code:

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          FabricTransportRemotingListenerSettings listenerSettings = new FabricTransportRemotingListenerSettings
          {
              MaxMessageSize = 10000000,
              SecurityCredentials = GetSecurityCredentials()
          };
          return new[]
          {
              new ServiceReplicaListener(
                  (context) => new FabricTransportServiceRemotingListener(context,this,listenerSettings))
          };
      }
      
      private static SecurityCredentials GetSecurityCredentials()
      {
          // Provide certificate details.
          var x509Credentials = new X509Credentials
          {
              FindType = X509FindType.FindByThumbprint,
              FindValue = "4FEF3950642138446CC364A396E1E881DB76B48C",
              StoreLocation = StoreLocation.LocalMachine,
              StoreName = "My",
              ProtectionLevel = ProtectionLevel.EncryptAndSign
          };
          x509Credentials.RemoteCommonNames.Add("ServiceFabric-Test-Cert");
          x509Credentials.RemoteCertThumbprints.Add("9FEF3950642138446CC364A396E1E881DB76B483");
          return x509Credentials;
      }
      
    2. 使用配置包提供:Provide them by using a config package:

      在 settings.xml 文件中添加名为 TransportSettings 的节。Add a named TransportSettings section in the settings.xml file.

      <Section Name="HelloWorldStatefulTransportSettings">
          <Parameter Name="MaxMessageSize" Value="10000000" />
          <Parameter Name="SecurityCredentialsType" Value="X509" />
          <Parameter Name="CertificateFindType" Value="FindByThumbprint" />
          <Parameter Name="CertificateFindValue" Value="4FEF3950642138446CC364A396E1E881DB76B48C" />
          <Parameter Name="CertificateRemoteThumbprints" Value="9FEF3950642138446CC364A396E1E881DB76B483" />
          <Parameter Name="CertificateStoreLocation" Value="LocalMachine" />
          <Parameter Name="CertificateStoreName" Value="My" />
          <Parameter Name="CertificateProtectionLevel" Value="EncryptAndSign" />
          <Parameter Name="CertificateRemoteCommonNames" Value="ServiceFabric-Test-Cert" />
      </Section>
      

      在这种情况下,CreateServiceReplicaListeners 方法如下所示:In this case, the CreateServiceReplicaListeners method will look like this:

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
         return new[]
         {
             new ServiceReplicaListener(
                 (context) => new FabricTransportServiceRemotingListener(
                     context,this,FabricTransportRemotingListenerSettings .LoadFrom("HelloWorldStatefulTransportSettings")))
         };
      }
      

      如果在 settings.xml 文件中添加 TransportSettings 节,则 FabricTransportRemotingListenerSettings 将按默认加载此节中的所有设置。If you add a TransportSettings section in the settings.xml file , FabricTransportRemotingListenerSettings will load all the settings from this section by default.

      <!--"TransportSettings" section .-->
      <Section Name="TransportSettings">
          ...
      </Section>
      

      在这种情况下,CreateServiceReplicaListeners 方法如下所示:In this case, the CreateServiceReplicaListeners method will look like this:

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new[]
          {
              return new[]{
                      new ServiceReplicaListener(
                          (context) => new FabricTransportServiceRemotingListener(context,this))};
          };
      }
      
  3. 在安全服务上使用远程堆栈(而不是使用 Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy 类)调用方法来创建服务代理时,请使用 Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactoryWhen you call methods on a secured service by using the remoting stack, instead of using the Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy class to create a service proxy, use Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactory. 传入包含 SecurityCredentialsFabricTransportRemotingSettingsPass in FabricTransportRemotingSettings, which contains SecurityCredentials.

    
    var x509Credentials = new X509Credentials
    {
        FindType = X509FindType.FindByThumbprint,
        FindValue = "9FEF3950642138446CC364A396E1E881DB76B483",
        StoreLocation = StoreLocation.LocalMachine,
        StoreName = "My",
        ProtectionLevel = ProtectionLevel.EncryptAndSign
    };
    x509Credentials.RemoteCommonNames.Add("ServiceFabric-Test-Cert");
    x509Credentials.RemoteCertThumbprints.Add("4FEF3950642138446CC364A396E1E881DB76B48C");
    
    FabricTransportRemotingSettings transportSettings = new FabricTransportRemotingSettings
    {
        SecurityCredentials = x509Credentials,
    };
    
    ServiceProxyFactory serviceProxyFactory = new ServiceProxyFactory(
        (c) => new FabricTransportServiceRemotingClientFactory(transportSettings));
    
    IHelloWorldStateful client = serviceProxyFactory.CreateServiceProxy<IHelloWorldStateful>(
        new Uri("fabric:/MyApplication/MyHelloWorldService"));
    
    string message = await client.GetHelloWorld();
    
    

    如果客户端代码正在作为服务一部分运行,可以从 settings.xml 文件中加载 FabricTransportRemotingSettingsIf the client code is running as part of a service, you can load FabricTransportRemotingSettings from the settings.xml file. 创建与服务代码类似的 HelloWorldClientTransportSettings 节,如上所示。Create a HelloWorldClientTransportSettings section that is similar to the service code, as shown earlier. 对客户端代码进行以下更改。Make the following changes to the client code:

    ServiceProxyFactory serviceProxyFactory = new ServiceProxyFactory(
        (c) => new FabricTransportServiceRemotingClientFactory(FabricTransportRemotingSettings.LoadFrom("HelloWorldClientTransportSettings")));
    
    IHelloWorldStateful client = serviceProxyFactory.CreateServiceProxy<IHelloWorldStateful>(
        new Uri("fabric:/MyApplication/MyHelloWorldService"));
    
    string message = await client.GetHelloWorld();
    
    

    如果客户端不是作为服务一部分运行,可以在 client_name.exe 所在的同一位置中创建 client_name.settings.xml 文件。If the client is not running as part of a service, you can create a client_name.settings.xml file in the same location where the client_name.exe is. 然后,在该文件中创建 TransportSettings 节。Then create a TransportSettings section in that file.

    类似于服务,如果在客户端 settings.xml/client_name.settings.xml 中添加 TransportSettings 节,则 FabricTransportRemotingSettings 将默认加载此节中的所有设置。Similar to the service, if you add a TransportSettings section in client settings.xml/client_name.settings.xml, FabricTransportRemotingSettings loads all the settings from this section by default.

    在此情况下,上述代码会进一步简化:In that case, the earlier code is even further simplified:

    
    IHelloWorldStateful client = ServiceProxy.Create<IHelloWorldStateful>(
                 new Uri("fabric:/MyApplication/MyHelloWorldService"));
    
    string message = await client.GetHelloWorld();
    
    

有关后续步骤,请阅读 Reliable Services 中使用 OWIN 的 Web APIAs a next step, read Web API with OWIN in Reliable Services.