Proteger comunicações remotas de serviço num serviço C#

A segurança é um dos aspetos mais importantes da comunicação. A arquitetura da aplicação Reliable Services fornece algumas pilhas e ferramentas de comunicação pré-criadas que pode utilizar para melhorar a segurança. Este artigo aborda como melhorar a segurança quando está a utilizar a comunicação remota do serviço num serviço C#. Baseia-se num exemplo existente que explica como configurar comunicações remotas para serviços fiáveis escritos em C#.

Para ajudar a proteger um serviço quando estiver a utilizar a comunicação remota de serviços com serviços C#, siga estes passos:

  1. Crie uma interface, IHelloWorldStateful, que defina os métodos que estarão disponíveis para uma chamada de procedimento remoto no seu serviço. O seu serviço utilizará FabricTransportServiceRemotingListener, que é declarado no Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime espaço de nomes. Esta é uma implementação ICommunicationListener que fornece capacidades remotas.

    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. Adicione as definições do serviço de escuta e as credenciais de segurança.

    Certifique-se de que o certificado que pretende utilizar para ajudar a proteger a comunicação do serviço está instalado em todos os nós do cluster.

    Nota

    Nos nós do Linux, o certificado tem de estar presente como ficheiros formatados em PEM no diretório /var/lib/sfcerts . Para saber mais, veja Localização e formato dos certificados X.509 nos nós do Linux.

    Existem duas formas de fornecer as definições do serviço de escuta e as credenciais de segurança:

    1. Forneça-os diretamente no código de serviço:

      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. Forneça-os através de um pacote de configuração:

      Adicione uma secção com nome TransportSettings no ficheiro settings.xml.

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

      Neste caso, o método terá o CreateServiceReplicaListeners seguinte aspeto:

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

      Se adicionar uma TransportSettings secção no ficheiro settings.xml , FabricTransportRemotingListenerSettings carregará todas as definições desta secção por predefinição.

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

      Neste caso, o método terá o CreateServiceReplicaListeners seguinte aspeto:

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new[]
          {
              return new[]{
                      new ServiceReplicaListener(
                          (context) => new FabricTransportServiceRemotingListener(context,this))};
          };
      }
      
  3. Quando chamar métodos num serviço seguro através da pilha remota, em vez de utilizar a Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy classe para criar um proxy de serviço, utilize Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactory. FabricTransportRemotingSettingsTransmita , que contém 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();
    
    

    Se o código de cliente estiver em execução como parte de um serviço, pode carregar FabricTransportRemotingSettings a partir do ficheiro de settings.xml. Crie uma secção HelloWorldClientTransportSettings semelhante ao código do serviço, conforme mostrado anteriormente. Efetue as seguintes alterações ao código de cliente:

    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();
    
    

    Se o cliente não estiver em execução como parte de um serviço, pode criar um ficheiro de client_name.settings.xml na mesma localização onde está o client_name.exe. Em seguida, crie uma secção TransportSettings nesse ficheiro.

    Semelhante ao serviço, se adicionar uma TransportSettings secção no cliente settings.xml/client_name.settings.xml, FabricTransportRemotingSettings carrega todas as definições desta secção por predefinição.

    Nesse caso, o código anterior é ainda mais simplificado:

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

Como passo seguinte, leia API Web com OWIN no Reliable Services.