TT Lab
Get started
Learn Learning paths Courses

Istio Service Mesh

Pushing From PERMISSIVE Up to STRICT

Continue in TT Lab

Goal

You work by hand with the three scopes and the priority of mTLS policies, and leave as a plan the procedure for raising a running mesh to STRICT without cutting it off.

Why it matters

Moving to STRICT is technically changing one field, but in operations it is one of the tasks with the most frequent incidents. The reason is simple — every path that was coming in over plaintext is cut off without warning. Crons without sidecars, batch jobs outside the mesh, and custom probes do not show up well on dashboards, and then appear as an outage the moment you turn on STRICT. That is why the standard is to keep PERMISSIVE, do a full survey of the sources of plaintext traffic, and then raise from the narrow scopes first.

Another thing you must learn is the distinction of direction. PeerAuthentication handles the receiving side (inbound), and trafficPolicy.tls of a DestinationRule handles the sending side (outbound). If these two are out of step, the connection fails, and in the application log it just looks like a 503. On top of that, istioctl analyze does not catch this combination — there used to be a dedicated analyzer, but it is not in the analyzer list of the current version. So the habit of putting the two resources side by side as a pair and cross-checking them by hand becomes the safeguard itself.

In this environment no real handshake happens, so grading looks at the names, scopes, and fields of policies and the static analysis results.

Steps

Preparation before starting. Lab Pods come up fresh for each lab, so the mesh configuration from the earlier lab does not remain. If kubectl get crd peerauthentications.security.istio.io is empty, run istioctl manifest generate --set profile=minimal > /root/istio/manifest.yaml and then kubectl apply -f /root/istio/manifest.yaml twice. This manifest also creates the istio-system namespace, which must exist because step 2 puts the mesh-wide policy there. Then prepare the working namespace with kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabled.

  1. Create /root/istio/mtls/pa-permissive.yaml — a single-document PeerAuthentication with metadata.name as default, metadata.namespace as mesh-lab, and spec.mtls.mode as PERMISSIVE, and do not put a selector (it is a whole-namespace policy). After creating it, apply it.
  2. Apply a PeerAuthentication named default to the istio-system namespace with spec.mtls.mode: STRICT and without a selector. And in /root/istio/mtls/out/scope-note.txt, write the rule that among the three layers of mesh-wide / namespace / workload, the narrower, more specific scope takes priority.
  3. Create the payments workload in mesh-lab — a ServiceAccount payments, a Service payments (port names http 8080 and http-metrics 9090), and a Deployment payments (Pod label app: payments, serviceAccountName: payments). Then create a PeerAuthentication legacy-metrics in mesh-lab: selector.matchLabels.app is payments, spec.mtls.mode is STRICT, and in portLevelMtls put only the single port 9090 as mode: DISABLE.
  4. Create a DestinationRule payments-mtls in mesh-lab. spec.host is payments and spec.trafficPolicy.tls.mode is ISTIO_MUTUAL. And in /root/istio/mtls/out/direction-note.txt, write the difference that PeerAuthentication handles the receiving side (inbound) and a DestinationRule handles the sending side (client/outbound).
  5. In /root/istio/mtls/identity.txt, write the payments workload's SPIFFE identity on exactly one line: spiffe://cluster.local/ns/mesh-lab/sa/payments. And in /root/istio/mtls/out/identity-note.txt, write the point that the mesh's identity is a service account, not an IP address.
  6. Create a RequestAuthentication jwt-payments in mesh-lab. selector.matchLabels.app is payments, jwtRules[0].issuer is https://idp.labhub.example/, and jwtRules[0].jwksUri is https://idp.labhub.example/.well-known/jwks.json. And in /root/istio/mtls/out/jwt-note.txt, write the fact that this resource alone cannot block requests without a token and an AuthorizationPolicy is needed together.
  7. Change the default PeerAuthentication in mesh-lab to STRICT and apply it. In that state, apply a DestinationRule with spec.trafficPolicy.tls.mode: DISABLE (for example payments-plaintext) and save the result of istioctl analyze -n mesh-lab to /root/istio/mtls/out/analyze-conflict.txt. The analyzer list of this version has no analyzer that looks at mTLS combination conflicts, so that conflict does not appear in the output — append, in one line, what conflicts with what, and leave it in the record. Then delete that DestinationRule or change it to ISTIO_MUTUAL to remove the conflict, analyze again, and save it to /root/istio/mtls/out/analyze-fixed.txt. No DestinationRule with tls.mode: DISABLE may remain in mesh-lab.
  8. Write /root/istio/mtls/migration.md at 400 bytes or more. In the document, the discussion of PERMISSIVE and observation (metrics/monitoring) must come before (on an earlier line than) the word STRICT, and it must include staged application per namespace and a rollback (reverting) method. Finally, finish with the default PeerAuthentication in mesh-lab in the STRICT state.

Notes

Set the namespace default policy to PERMISSIVE

Create /root/istio/mtls/pa-permissive.yaml — a single-document PeerAuthentication with metadata.name as default, metadata.namespace as mesh-lab, and spec.mtls.mode as PERMISSIVE, and do not put a selector (it is a whole-namespace policy). After creating it, apply it.

A policy that applies to a whole namespace has a fixed name and has no selector. The transition starts from a state that accepts both plaintext and ciphertext.

Raise the mesh-wide default to STRICT

Apply a PeerAuthentication named default to the istio-system namespace with spec.mtls.mode: STRICT and without a selector. And in /root/istio/mtls/out/scope-note.txt, write the rule that among the three layers of mesh-wide / namespace / workload, the narrower, more specific scope takes priority.

A mesh-wide policy goes in the root namespace. And summarize in a note which of the three scopes wins.

Open only one port of one workload as an exception

Create the payments workload in mesh-lab — a ServiceAccount payments, a Service payments (port names http 8080 and http-metrics 9090), and a Deployment payments (Pod label app: payments, serviceAccountName: payments). Then create a PeerAuthentication legacy-metrics in mesh-lab: selector.matchLabels.app is payments, spec.mtls.mode is STRICT, and in portLevelMtls put only the single port 9090 as mode: DISABLE.

Keep exceptions as narrow as possible. Select the workload with a selector, keep the default still STRICT, and specify separately only the port that is a problem.

Declare mTLS on the sending side

Create a DestinationRule payments-mtls in mesh-lab. spec.host is payments and spec.trafficPolicy.tls.mode is ISTIO_MUTUAL. And in /root/istio/mtls/out/direction-note.txt, write the difference that PeerAuthentication handles the receiving side (inbound) and a DestinationRule handles the sending side (client/outbound).

The receiving side and the sending side are handled by different resources. There is a separate mode that means using the certificate issued by the mesh instead of specifying a certificate directly.

Write down the workload's identity

In /root/istio/mtls/identity.txt, write the payments workload's SPIFFE identity on exactly one line: spiffe://cluster.local/ns/mesh-lab/sa/payments. And in /root/istio/mtls/out/identity-note.txt, write the point that the mesh's identity is a service account, not an IP address.

Three pieces go in, in order. And the object that is the basis of that identity must actually exist in the cluster.

Create an end-user JWT validation policy

Create a RequestAuthentication jwt-payments in mesh-lab. selector.matchLabels.app is payments, jwtRules[0].issuer is https://idp.labhub.example/, and jwtRules[0].jwksUri is https://idp.labhub.example/.well-known/jwks.json. And in /root/istio/mtls/out/jwt-note.txt, write the fact that this resource alone cannot block requests without a token and an AuthorizationPolicy is needed together.

To verify a signature you need both the issuer and the location of the public keys. And write in the note what this resource alone cannot block.

Create and resolve a conflict between STRICT and DISABLE

Change the default PeerAuthentication in mesh-lab to STRICT and apply it. In that state, apply a DestinationRule with spec.trafficPolicy.tls.mode: DISABLE (for example payments-plaintext) and save the result of istioctl analyze -n mesh-lab to /root/istio/mtls/out/analyze-conflict.txt. The analyzer list of this version has no analyzer that looks at mTLS combination conflicts, so that conflict does not appear in the output — append, in one line, what conflicts with what, and leave it in the record. Then delete that DestinationRule or change it to ISTIO_MUTUAL to remove the conflict, analyze again, and save it to /root/istio/mtls/out/analyze-fixed.txt. No DestinationRule with tls.mode: DISABLE may remain in mesh-lab.

If the receiving side requires encryption but the sending side insists on plaintext, the connection fails. Keep the analysis result separately before and after the fix, and do not leave any inconsistent configuration behind.

Write the staged migration plan and finish the migration

Write /root/istio/mtls/migration.md at 400 bytes or more. In the document, the discussion of PERMISSIVE and observation (metrics/monitoring) must come before (on an earlier line than) the word STRICT, and it must include staged application per namespace and a rollback (reverting) method. Finally, finish with the default PeerAuthentication in mesh-lab in the STRICT state.

The order in which words appear in the document is the order of the stages. Write observation and PERMISSIVE first and STRICT after, and include the way to revert too.