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:
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 noMicrosoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime
espaço de nomes. Esta é uma implementaçãoICommunicationListener
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!"); } }
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:
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; }
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))}; }; }
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, utilizeMicrosoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactory
.FabricTransportRemotingSettings
Transmita , que contémSecurityCredentials
.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.