Enabling Mesh-wide Strict mTLS

Introduction

When using mutual Transport Layer Security (mTLS), client and server can verify each other’s identity and encrypt traffic using distinct certificates for both parties. This is different from 'simple' TLS, where usually only the server will provide a certificate.

By default, Maistra is configured in permissive mode: the sidecars in a Maistra mesh will accept both plain-text traffic and connections that are encrypted using mTLS. For outgoing traffic, the sidecars will default to plain-text, unencrypted connections. By using Istio configuration objects, the behaviour can be fine-tuned to your needs. One of the possible configurations is strict mTLS mode. This means that sidecars only accept incoming traffic if it is encrypted, and they will default to using encrypted connections to talk to other services in the mesh.

This article explains the role and setup of the required Istio and Maistra resources to enable strict mTLS across your service mesh. If you just want to set strict mode quickly, you can achieve the same result by setting .Spec.istio.global.mtls.enabled to true in your ServiceMeshControlPlane resource. The operator will then create the required resources for you.

There are separate configuration objects for incoming and outgoing connections, so this will entail two steps:

  1. Configuring sidecars to only accept incoming mTLS connections

  2. Configuring sidecars to always encrypt outgoing connections to other services in the mesh

Creating the ServiceMeshPolicy

To enforce mTLS encryption on incoming connections, you need to create the following ServiceMeshPolicy resource:

apiVersion: "maistra.io/v1"
kind: "ServiceMeshPolicy"
metadata:
  name: "default"
  namespace: <CONTROL_PLANE_NS>
spec:
  peers:
  - mtls: {}

This will instruct all sidecars in the mesh to only accept mTLS connections. Note that this resource has to be named 'default' and it has to be created in your control plane’s namespace.

This resource does not exist in upstream Istio, it has been added to Maistra. It replaces the upstream MeshPolicy, which is cluster-scoped. For more information, see the comparison of Maistra and upstream Istio.

Creating the DestinationRule

Now we just need to configure Envoy to use mTLS when sending requests to other services in the mesh. This can be achieved by creating a DestinationRule resource:

apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
  name: "default"
  namespace: <CONTROL_PLANE_NS>
spec:
  host: "*.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL

As you can see, using the wildcard "*.local" in spec.host we are matching all services in the same cluster. By creating the resource in the control plane’s namespace, we’re making sure it is applied to all services in the mesh. It is called 'default' again, but this time the name is not important; you can create as many DestinationRules as you like, as opposed to the singleton ServiceMeshPolicy. The spec.traffiPolicy.tls.mode of ISTIO_MUTUAL instructs Envoy to use mTLS for outgoing requests, and to use the certificates that are deployed by Istio’s Citadel component.