PQC Status
Guide

Hybrid post-quantum TLS on a Kubernetes ingress

A practical path for platform teams: locate the real TLS termination point, set the key exchange groups conservatively, then prove it from the public internet.

· 7 min

In a Kubernetes cluster, hybrid post-quantum TLS is configured wherever the handshake actually ends, almost never in the application container. That point is usually the ingress controller pod, a cloud load balancer in front of it, or a service mesh sidecar. The work has four steps: find the real termination point, check the TLS library version running there, put a hybrid group such as X25519MLKEM768 ahead of a classical group in the preference list, then verify from outside the cluster.

Step 1: find where the handshake really ends

Teams often configure the wrong component and conclude that their controller does not support post-quantum key exchange. Map the path of a client packet first. Only the component that owns the private key and sends the ServerHello decides the key exchange group.

Termination pointWho chooses the key exchange groupWhat to check first
Ingress controller pod (nginx, Envoy based, HAProxy, Traefik)The controller, through its ConfigMap or its own configuration objectThe TLS library version inside the controller image
Cloud layer 7 load balancer terminating TLSThe provider, through a named TLS or security policyWhether the provider offers a policy that includes a hybrid group
Cloud layer 4 load balancer in passthrough or TCP modeThe ingress controller behind it, because the handshake travels throughThat the service really is TCP passthrough, not TLS terminating
CDN, WAF or reverse proxy in front of the clusterThat edge service, for every public hostname it frontsIts documented TLS configuration options per hostname
Service mesh sidecar (east to west mutual TLS)The mesh control plane and the sidecar proxy buildWhether internal traffic is in scope for this phase at all

Confirm the topology with read-only commands before changing anything:

kubectl get ingress -A -o wide
kubectl -n ingress-nginx get svc -o wide
kubectl -n ingress-nginx get svc ingress-nginx-controller -o jsonpath='{.metadata.annotations}'

Load balancer annotations on the controller Service tell you whether the cloud layer terminates TLS or passes it through. If it terminates, no ingress setting will change the negotiated group.

Step 2: check what the controller's TLS library supports

The hybrid groups are a property of the TLS stack compiled into the controller image, not of Kubernetes. For OpenSSL based builds, OpenSSL 3.5 or later is required for the ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) groups. BoringSSL based data planes such as Envoy use their own group naming and their own support matrix, so read the release notes for the exact version you run.

kubectl -n ingress-nginx exec deploy/ingress-nginx-controller -- openssl version

If that version is older than 3.5, stop here and plan a controller upgrade. No configuration key can enable a group the library does not implement.

The group names and IANA codepoints you may legitimately use are published in the TLS parameters registry (iana.org):

Hybrid key agreement for TLS 1.3 is specified in RFC 10024 (rfc-editor.org). ML-KEM itself is FIPS 203, final since 13 August 2024 (csrc.nist.gov). The pre-standard Kyber draft group X25519Kyber768Draft00 (0x6399) is obsolete: if it appears anywhere in your configuration, remove it.

Step 3: set the groups, with a classical fallback

Only one key exchange group is negotiated per connection, and the server picks from what the client offered. Listing a hybrid group first and a classical group after it is therefore safe by construction: clients that know the hybrid group use it, older clients fall back to X25519 or P-256.

ingress-nginx

The controller exposes the ConfigMap key ssl-ecdh-curve, which maps to the nginx directive ssl_ecdh_curve and is passed to the TLS library as an ordered group list (its default value is auto). Confirm the key in the ConfigMap reference for the controller version you run, then apply:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  ssl-protocols: "TLSv1.3 TLSv1.2"
  ssl-ecdh-curve: "X25519MLKEM768:X25519:prime256v1"

Here prime256v1 is the OpenSSL name for secp256r1. Keeping two classical groups after the hybrid one preserves TLS 1.2 clients and any TLS 1.3 client that does not implement ML-KEM. The same ordering logic applies to a standalone web server, as described in our guide to enabling ML-KEM on nginx, Apache, HAProxy and Caddy.

Other controllers and service meshes

Do not copy annotations between controllers. An annotation the controller does not recognise is silently ignored, which is the most dangerous failure mode here: the rollout looks successful and nothing changed. The principle is identical everywhere, the surface differs:

Leave the certificate chain alone for now

Recorded traffic is protected only by the key exchange, which is the whole point of harvest now, decrypt later: an attacker who stores ciphertext today can decrypt it later if the key exchange was classical. Our article on harvest now, decrypt later explains why key exchange is first in the queue. Certificates can migrate later: public certificate authorities still issue ECDSA and RSA certificates, and ML-DSA signatures, standardized as FIPS 204, are not yet generally available in public TLS chains. Keep your existing cert-manager issuer unchanged in this phase.

Step 4: verify from outside the cluster

Test from a machine on the public internet, through the same path a browser takes. Never verify with kubectl port-forward: it bypasses the load balancer and the edge, so it can show a hybrid handshake that no real client will ever get. With OpenSSL 3.5 or later:

openssl s_client -connect www.example.com:443 -servername www.example.com \
  -groups X25519MLKEM768 </dev/null 2>/dev/null | grep -i 'Negotiated TLS1.3 group'

A successful result prints the negotiated group. Then prove the fallback still works, which is the test teams forget:

openssl s_client -connect www.example.com:443 -servername www.example.com \
  -groups X25519 </dev/null 2>/dev/null | grep -i 'Negotiated TLS1.3 group'

Repeat both tests for every public hostname, including the apex and any host served by a second ingress class or a different edge provider. A cluster with two ingress classes commonly ends up half migrated. What an external test can and cannot show is described in our scanner methodology.

Step 5: roll out, and keep the evidence

  1. Apply the change in staging first, on a controller running the same image tag as production.
  2. Promote to one production hostname or one canary ingress class, not the whole fleet.
  3. Watch TLS handshake errors and controller CPU for at least one full business cycle. ML-KEM key shares are larger than an X25519 share, so ClientHello and ServerHello messages grow.
  4. Keep the rollback ready: reverting the ConfigMap key to its previous value restores the old behaviour on the next reload.
  5. Record the result in your cryptographic inventory, with the date and the host tested.

That last step is where a platform change becomes compliance evidence. The mapping below is our reading of how the technical artifact serves the requirement, not a quotation of the texts. This is information, not legal advice.

RequirementWhat the text asks forArtifact from this change
PCI DSS v4.0.1 req. 12.3.3Documented inventory of cipher suites and protocols in use, reviewed at least every 12 monthsThe controller ConfigMap, under version control, plus the dated external test output
DORA RTS (EU) 2024/1774, Art. 6An encryption policy with provisions for changing cryptographic technology as cryptanalysis evolvesThe documented group ordering and the tested rollback procedure
DORA RTS (EU) 2024/1774, Art. 7Key management over the lifecycle and a register of certificates for critical assetsThe cert-manager issuer inventory for ingress hostnames

Next step

Once the ConfigMap is applied and your staging tests pass, confirm the externally visible result: scan your domain with PQC Status to see which key exchange group your ingress actually negotiates, which TLS versions it offers, and what the certificate chain looks like. Run the scan once per public hostname, before and after each controller image upgrade, because an image change can silently move you back to a classical group. A scan describes the externally visible TLS configuration only: it is not a certification and not a compliance attestation.

Frequently asked questions

Can I enable hybrid post-quantum TLS if a cloud load balancer terminates my TLS?

Not from Kubernetes. When a managed layer 7 load balancer terminates TLS, it chooses the key exchange group, and your only control is the named TLS policy the provider exposes. Check that provider's release notes for a policy that includes a hybrid ML-KEM group. If none exists, the alternative is to move termination into the cluster behind a layer 4 passthrough service, which is an architectural change worth reviewing separately.

Will adding X25519MLKEM768 break older clients?

It should not, provided you keep a classical group after it in the list, for example X25519 and then secp256r1. Only one group is negotiated per connection, and the server can only select from what the client offered, so clients that do not implement ML-KEM simply fall back. Always test the fallback explicitly in staging, not just the hybrid handshake.

Which OpenSSL version does the ingress controller image need?

For OpenSSL based builds, OpenSSL 3.5 or later is required for the ML-KEM TLS groups. Check it inside the running pod rather than assuming it from the chart version. BoringSSL based data planes such as Envoy have their own naming and support matrix, so consult the release notes for the exact version you run.

Do I also need post-quantum certificates on the ingress?

Not in this phase. Recorded traffic is protected by the key exchange alone, which is why hybrid key agreement comes first and signatures can migrate later. ML-DSA is a final NIST standard (FIPS 204) but is not yet generally available in public TLS certificate chains, so keep your existing ECDSA or RSA issuer for now and revisit when public certificate authorities offer post-quantum chains.