Proteggere le comunicazioni per i servizi in Azure Service FabricHelp secure communication for services in Azure Service Fabric

La sicurezza è uno degli aspetti essenziali delle comunicazioni.Security is one of the most important aspects of communication. Il framework di applicazioni di Reliable Services offre alcuni stack e strumenti predefiniti che è possibile usare per migliorare la sicurezza.The Reliable Services application framework provides a few prebuilt communication stacks and tools that you can use to improve security. In questo articolo viene illustrato come migliorare la sicurezza quando si usa la comunicazione remota dei servizi e lo stack di comunicazione di Windows Communication Foundation (WCF).This article talks about how to improve security when you're using service remoting and the Windows Communication Foundation (WCF) communication stack.

Proteggere un servizio quando si usa la comunicazione remota dei serviziHelp secure a service when you're using service remoting

Verrà usato un esempio esistente che spiega come configurare la comunicazione remota per Reliable Services.We are using an existing example that explains how to set up remoting for reliable services. Per proteggere un servizio quando si usa la comunicazione remota, seguire questa procedura:To help secure a service when you're using service remoting, follow these steps:

  1. Creare un'interfaccia, IHelloWorldStateful, che definisce i metodi che saranno disponibili per la Remote Procedure Call del servizio.Create an interface, IHelloWorldStateful, that defines the methods that will be available for a remote procedure call on your service. Il servizio userà il metodo FabricTransportServiceRemotingListener, dichiarato nello spazio dei nomi Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime.Your service will use FabricTransportServiceRemotingListener, which is declared in the Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime namespace. Si tratta di un'implementazione ICommunicationListener che fornisce funzionalità di accesso remoto.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. Aggiungere le impostazioni del listener e le credenziali di sicurezza.Add listener settings and security credentials.

    Assicurarsi che il certificato da usare per proteggere le comunicazioni dei servizi sia installato in tutti i nodi del cluster.Make sure that the certificate that you want to use to help secure your service communication is installed on all the nodes in the cluster. Esistono due modi per specificare le impostazioni del listener e le credenziali di sicurezza:There are two ways that you can provide listener settings and security credentials:

    1. Specificarle direttamente nel codice del servizio: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. Specificarle tramite un pacchetto di configurazione:Provide them by using a config package:

      Aggiungere una sezione TransportSettings nel file settings.xml.Add a 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>
      

      In questo caso il metodo CreateServiceReplicaListeners avrà un aspetto analogo al seguente: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")))
          };
      }
      

      Se si aggiunge una sezione TransportSettings al file settings.xml, FabricTransportRemotingListenerSettings caricherà tutte le impostazioni da questa sezione per impostazione predefinita.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>
      

      In questo caso il metodo CreateServiceReplicaListeners avrà un aspetto analogo al seguente: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. Quando si chiamano metodi in un servizio protetto tramite lo stack di comunicazione remota, anziché usare la classe Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy per creare un proxy del servizio, usare Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactory.When 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. Specificare FabricTransportRemotingSettings, che contiene SecurityCredentials.Pass 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();
    

    Se il codice client viene eseguito come parte del servizio, è possibile caricare FabricTransportRemotingSettings dal file settings.xml.If the client code is running as part of a service, you can load FabricTransportRemotingSettings from the settings.xml file. Creare una sezione HelloWorldClientTransportSettings simile al codice del servizio, come illustrato in precedenza.Create a HelloWorldClientTransportSettings section that is similar to the service code, as shown earlier. Apportare le modifiche seguenti al codice client: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();
    

    Se il client non è in esecuzione come parte di un servizio, è possibile creare un file client_name.settings.xml nello stesso percorso del file client_name.exe.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. Creare quindi una sezione TransportSettings in tale file.Then create a TransportSettings section in that file.

    Come con il servizio, se si aggiunge una sezione TransportSettings nel file settings.xml/client_name.settings.xml del client, FabricTransportRemotingSettings carica tutte le impostazioni da questa sezione per impostazione predefinita.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 questo caso il codice precedente risulta ancora più semplice: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();
    

Proteggere un servizio quando si usa uno stack di comunicazione basato su WCFHelp secure a service when you're using a WCF-based communication stack

Verrà usato un esempio esistente che spiega come configurare uno stack di comunicazione basato su WCF per Reliable Services.We are using an existing example that explains how to set up a WCF-based communication stack for reliable services. Per proteggere un sevizio quando si usa uno stack di comunicazione basato su WCF, seguire questa procedura:To help secure a service when you're using a WCF-based communication stack, follow these steps:

  1. Per il servizio, è necessario proteggere il listener di comunicazione WCF (WcfCommunicationListener) che viene creato.For the service, you need to help secure the WCF communication listener (WcfCommunicationListener) that you create. A tale scopo, modificare il metodo CreateServiceReplicaListeners .To do this, modify the CreateServiceReplicaListeners method.

    protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
    {
        return new[]
        {
            new ServiceReplicaListener(
                this.CreateWcfCommunicationListener)
        };
    }
    
    private WcfCommunicationListener<ICalculator> CreateWcfCommunicationListener(StatefulServiceContext context)
    {
       var wcfCommunicationListener = new WcfCommunicationListener<ICalculator>(
            serviceContext:context,
            wcfServiceObject:this,
            // For this example, we will be using NetTcpBinding.
            listenerBinding: GetNetTcpBinding(),
            endpointResourceName:"WcfServiceEndpoint");
    
        // Add certificate details in the ServiceHost credentials.
        wcfCommunicationListener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(
            StoreLocation.LocalMachine,
            StoreName.My,
            X509FindType.FindByThumbprint,
            "9DC906B169DC4FAFFD1697AC781E806790749D2F");
        return wcfCommunicationListener;
    }
    
    private static NetTcpBinding GetNetTcpBinding()
    {
        NetTcpBinding b = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
        b.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
        return b;
    }
    
  2. Nel client la classe WcfCommunicationClient creata nell' esempio precedente rimane invariata.In the client, the WcfCommunicationClient class that was created in the previous example remains unchanged. È però necessario eseguire l'override del metodo CreateClientAsync di WcfCommunicationClientFactory:But you need to override the CreateClientAsync method of WcfCommunicationClientFactory:

    public class SecureWcfCommunicationClientFactory<TServiceContract> : WcfCommunicationClientFactory<TServiceContract> where TServiceContract : class
    {
        private readonly Binding clientBinding;
        private readonly object callbackObject;
        public SecureWcfCommunicationClientFactory(
            Binding clientBinding,
            IEnumerable<IExceptionHandler> exceptionHandlers = null,
            IServicePartitionResolver servicePartitionResolver = null,
            string traceId = null,
            object callback = null)
            : base(clientBinding, exceptionHandlers, servicePartitionResolver,traceId,callback)
        {
            this.clientBinding = clientBinding;
            this.callbackObject = callback;
        }
    
        protected override Task<WcfCommunicationClient<TServiceContract>> CreateClientAsync(string endpoint, CancellationToken cancellationToken)
        {
            var endpointAddress = new EndpointAddress(new Uri(endpoint));
            ChannelFactory<TServiceContract> channelFactory;
            if (this.callbackObject != null)
            {
                channelFactory = new DuplexChannelFactory<TServiceContract>(
                this.callbackObject,
                this.clientBinding,
                endpointAddress);
            }
            else
            {
                channelFactory = new ChannelFactory<TServiceContract>(this.clientBinding, endpointAddress);
            }
            // Add certificate details to the ChannelFactory credentials.
            // These credentials will be used by the clients created by
            // SecureWcfCommunicationClientFactory.  
            channelFactory.Credentials.ClientCertificate.SetCertificate(
                StoreLocation.LocalMachine,
                StoreName.My,
                X509FindType.FindByThumbprint,
                "9DC906B169DC4FAFFD1697AC781E806790749D2F");
            var channel = channelFactory.CreateChannel();
            var clientChannel = ((IClientChannel)channel);
            clientChannel.OperationTimeout = this.clientBinding.ReceiveTimeout;
            return Task.FromResult(this.CreateWcfCommunicationClient(channel));
        }
    }
    

    Usare il metodo SecureWcfCommunicationClientFactory per creare un client di comunicazione WCF (WcfCommunicationClient).Use SecureWcfCommunicationClientFactory to create a WCF communication client (WcfCommunicationClient). Usare il client per richiamare i metodi del servizio.Use the client to invoke service methods.

    IServicePartitionResolver partitionResolver = ServicePartitionResolver.GetDefault();
    
    var wcfClientFactory = new SecureWcfCommunicationClientFactory<ICalculator>(clientBinding: GetNetTcpBinding(), servicePartitionResolver: partitionResolver);
    
    var calculatorServiceCommunicationClient =  new WcfCommunicationClient(
        wcfClientFactory,
        ServiceUri,
        ServicePartitionKey.Singleton);
    
    var result = calculatorServiceCommunicationClient.InvokeWithRetryAsync(
        client => client.Channel.Add(2, 3)).Result;
    

Passaggi successiviNext steps