Move your mTLS configuration from the codebase to a service mesh!

Posted on

Have you ever wondered if you can move the implementation related to communication over mTLS from the code level to a higher layer of abstraction? In this article, I will try to show you one approach on how to make your codebase lighter.

Note: The code samples will be based on Java, Spring Boot and Istio

The case!

You’ve got service with business logic and some external system for which communication has to be established over mTLS connection.


In the picture above, there is an external service (the “red” one) managed by a third party to which we should perform requests using mTLS. On the other hand, service “blue” is managed by your team where you are responsible for providing the implementation of a secured connection to the external system.

First approach

The first solution that comes to our mind is to handle such a connection directly from the code. In this case, you create a dedicated HTTP client and supply it with all required data – such as keystore (with client certificate and private key), truststore (with server certificates), or a protocol version.

Sample solution of RestTemplate for handling mTLS connection can look like:

RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    return restTemplateBuilder

private ClientHttpRequestFactory requestFactory() {
    try {
        final KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(keyStoreResource.getInputStream(), keyStorePassword.toCharArray());
        KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(trustStoreResource.getInputStream(), trustStorePassword.toCharArray());

        final SSLConnectionSocketFactory socketFactory =
                new SSLConnectionSocketFactory(
                        new SSLContextBuilder()
                                .loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
                                    .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())

        final HttpClient httpClient = HttpClients.custom()
        final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return requestFactory;
    } catch (Exception e) {
        throw new IllegalStateException("Could not create client request factory");
Enter fullscreen mode

Exit fullscreen mode

As you can see there is a lot of data such as files and passphrases which is needed to be passed directly to your source code (e.g. via K8S secrets, FlexVolumes, AWS Secret Manager, etc.).

Then calling the destination service is based on wiring a correct instance of RestTemplate and performing the HTTPS request.

private RestTemplate restTemplate;

public ResponseEntity<String> clientEndpoint() {"Received request. Calling server via mTLS");
    String response = restTemplate.getForObject("", String.class);"Received response from external server {response={}}", response);
    return ResponseEntity.ok(response);
Enter fullscreen mode

Exit fullscreen mode

Service mesh to the rescue!

In this article I will not dive into the concept of what Service Mesh is. If you are not familiar with that term, I recommend you this article by William Morgan

The example below is based on Istio, which is one of the leading open source service mesh implementations. If you are interested in Istio jump to their documentation and get familiar with sample BookInfo application

The idea behind this solution is to move the responsibility for establishing a connection via mTLS to the layer outside our application. Let’s let Istio do it!

The figure below shows a simplified explanation of how it will work. We’ve got our service, in front of it there is an istio-proxy container. It will be responsible for handling a mTLS communication in our case.

Our service will call the external system as a normal HTTP system and the proxy will upgrade the connection to HTTPS and handle stuff related to mTLS.


The configuration is as simple as it can.

Follow these steps:

  1. Create a secret in K8S cluster, which contains required data. The structure of secret should look like:

    apiVersion: v1
      kind: Secret
      type: Opaque
      name: external-system-mtls-data
      namespace: $YOUR_NAMESPACE
      key: $CLIENT_PEM_KEY_IN_BASE64

    and then apply it:


  2. Add resources specific for Istio:

    • ServiceEntry – describes the API which is external to the mesh
    • VirtualService – routes the traffic to external system on port 80 (http) to port 443 (http to https)
    • DestinationRule – defines a policy to apply mTLS when traffic is routed to the external system and specifies the directory of certificates to use
    kind: ServiceEntry
      name: external-system-service-entry
      namespace: project
      location: MESH_EXTERNAL
        - number: 80
          name: http-port
          protocol: HTTP
        - number: 443
          name: https-port
          protocol: HTTPS
      resolution: DNS
    kind: DestinationRule
      name: external-system-dest-rule
      namespace: project
          - port:
              number: 443
              mode: MUTUAL
              clientCertificate: /etc/mtlsfiles/cert
              privateKey: /etc/mtlsfiles/key
              caCertificates: /etc/mtlsfiles/cacert
    kind: VirtualService
      name: external-system-virtual-service
      namespace: project
        - match:
            - port: 80
            - destination:
                  number: 443


  3. Adjust your deployment to mount files with certificates. For these purposes, the annotations and are used.
    Below you can find a sample deployment with applied changes.

    apiVersion: apps/v1
    kind: Deployment
      namespace: devel
      name: sample-app
        app: sample-app
      replicas: 1
          app: sample-app
            app: sample-app
          - name: sample-app
            image: your-application-image:latest
                memory: 300Mi
                cpu: 500m
                memory: 450Mi
                cpu: 750m

    Notice that in the name of secret created in Step 1 has to be used. In path for volume mount must be consistent with one provided in definition of DestinationRule.

  4. Verify the configuration.

    First of all you can verify that your certificates are correctly mounted. To do that, just go inside your pod and verify files under the configured directory.


    Next, it is time to verify the handling of mTLS connection. Now execute the HTTP request to the external system inside an application container.


    VoilĂ ! HTTP call is proxied via sidecar and mTLS connection established successfully on top of that.

  5. Adjust your application code. In my example application code will be simplified to:

     private RestTemplate restTemplate;
     public ResponseEntity<String> clientEndpoint() {"Received request. Calling server via mTLS");
         String response = restTemplate.getForObject("", String.class);"Received response from external server {response={}}", response);
         return ResponseEntity.ok(response);

    Without any special configuration of RestTemplate – just simple HTTP call to destination service.


It was quite simple to achieve moving the implementations from the application code to the service mesh. Thanks to that, it is transparent for our application to know how the connection is secured. The application’s codebase is easier to understand and independent from external system security requirements.

Istio provides also an easy way to implement other patterns typical for microservices such as retry policies, circuit breaking, or API versioning.

If you have any thoughts or questions feel free to comment. The code samples can be found on my GitHub. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *