Secure service remoting communications in a Java service
Security is one of the most important aspects of communication. The Reliable Services application framework provides a few prebuilt communication stacks and tools that you can use to improve security. This article discusses how to improve security when you're using service remoting in a Java service. It builds on an existing example that explains how to set up remoting for reliable services written in Java.
To help secure a service when you're using service remoting with Java services, follow these steps:
Create an interface,
HelloWorldStateless
, that defines the methods that will be available for a remote procedure call on your service. Your service will useFabricTransportServiceRemotingListener
, which is declared in themicrosoft.serviceFabric.services.remoting.fabricTransport.runtime
package. This is anCommunicationListener
implementation that provides remoting capabilities.public interface HelloWorldStateless extends Service { CompletableFuture<String> getHelloWorld(); } class HelloWorldStatelessImpl extends StatelessService implements HelloWorldStateless { @Override protected List<ServiceInstanceListener> createServiceInstanceListeners() { ArrayList<ServiceInstanceListener> listeners = new ArrayList<>(); listeners.add(new ServiceInstanceListener((context) -> { return new FabricTransportServiceRemotingListener(context,this); })); return listeners; } public CompletableFuture<String> getHelloWorld() { return CompletableFuture.completedFuture("Hello World!"); } }
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. For services running on Linux, the certificate must be available as a PEM-formmatted file; either a
.pem
file that contains the certificate and private key or a.crt
file that contains the certificate and a.key
file that contains the private key. 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:
Provide them by using a config package:
Add a named
TransportSettings
section in the settings.xml file.<!--Section name should always end with "TransportSettings".--> <!--Here we are using a prefix "HelloWorldStateless".--> <Section Name="HelloWorldStatelessTransportSettings"> <Parameter Name="MaxMessageSize" Value="10000000" /> <Parameter Name="SecurityCredentialsType" Value="X509_2" /> <Parameter Name="CertificatePath" Value="/path/to/cert/BD1C71E248B8C6834C151174DECDBDC02DE1D954.crt" /> <Parameter Name="CertificateProtectionLevel" Value="EncryptandSign" /> <Parameter Name="CertificateRemoteThumbprints" Value="BD1C71E248B8C6834C151174DECDBDC02DE1D954" /> </Section>
In this case, the
createServiceInstanceListeners
method will look like this:protected List<ServiceInstanceListener> createServiceInstanceListeners() { ArrayList<ServiceInstanceListener> listeners = new ArrayList<>(); listeners.add(new ServiceInstanceListener((context) -> { return new FabricTransportServiceRemotingListener(context,this, FabricTransportRemotingListenerSettings.loadFrom(HelloWorldStatelessTransportSettings)); })); return listeners; }
If you add a
TransportSettings
section in the settings.xml file without any prefix,FabricTransportListenerSettings
will load all the settings from this section by default.<!--"TransportSettings" section without any prefix.--> <Section Name="TransportSettings"> ... </Section>
In this case, the
CreateServiceInstanceListeners
method will look like this:protected List<ServiceInstanceListener> createServiceInstanceListeners() { ArrayList<ServiceInstanceListener> listeners = new ArrayList<>(); listeners.add(new ServiceInstanceListener((context) -> { return new FabricTransportServiceRemotingListener(context,this); })); return listeners; }
When you call methods on a secured service by using the remoting stack, instead of using the
microsoft.serviceFabric.services.remoting.client.ServiceProxyBase
class to create a service proxy, usemicrosoft.serviceFabric.services.remoting.client.FabricServiceProxyFactory
.If the client code is running as part of a service, you can load
FabricTransportSettings
from the settings.xml file. Create a TransportSettings section that is similar to the service code, as shown earlier. Make the following changes to the client code:FabricServiceProxyFactory serviceProxyFactory = new FabricServiceProxyFactory(c -> { return new FabricTransportServiceRemotingClientFactory(FabricTransportRemotingSettings.loadFrom("TransportPrefixTransportSettings"), null, null, null, null); }, null) HelloWorldStateless client = serviceProxyFactory.createServiceProxy(HelloWorldStateless.class, new URI("fabric:/MyApplication/MyHelloWorldService")); CompletableFuture<String> message = client.getHelloWorld();