Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,6 @@ tmp/

# HLS config for haskell-tools plugin (Neovim)
hls.json

# Envoy AWS-LC build output (hack/envoy-aws-lc)
envoy-build.log
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,30 @@ upload-hoogle-image:
nix -v --show-trace -L build ".#wireServer.hoogleImage" --out-link $(HOOGLE_IMAGE_DIR)/image --fallback
./hack/bin/upload-image.sh $(HOOGLE_IMAGE_DIR)/image

# Envoy proxy image linked against AWS-LC instead of BoringSSL, so the Gateway
# can offer the SecP256r1MLKEM768 / SecP384r1MLKEM1024 post-quantum key
# agreement groups named by BSI TR-02102-2, which BoringSSL does not implement.
#
# Unlike every other image here this is not built by nix: the AWS-LC genrule
# needs Bazel's own downloaded LLVM toolchain plus pinned cmake/ninja/go, which
# nixpkgs' Envoy derivation patches out. It drives Envoy's build container
# instead — expect a multi-hour build and ~60G of disk.
#
# ENVOY_VERSION must match the Envoy your Envoy Gateway ships, since Envoy
# Gateway generates bootstrap config for a specific version.
# See hack/envoy-aws-lc/README.md and charts/wire-ingress/README.md.
#
# make build-envoy-aws-lc-image
# make build-envoy-aws-lc-image PUSH=1 ENVOY_VERSION=v1.39.0
ENVOY_VERSION ?= v1.38.3

.PHONY: build-envoy-aws-lc-image
build-envoy-aws-lc-image:
ENVOY_VERSION=$(ENVOY_VERSION) \
IMAGE=$(DOCKER_USER)/envoy-aws-lc \
PUSH=$(PUSH) \
./hack/envoy-aws-lc/build.sh

#################################
## cassandra / postgres management

Expand Down
18 changes: 18 additions & 0 deletions changelog.d/2-features/wire-ingress-tr-02102-2-tls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
The `wire-ingress` chart now constrains the TLS parameters Envoy negotiates
(`gateway.tls.*`: TLS versions, TLS 1.2 cipher suites, key agreement groups,
signature algorithms), restoring the BSI TR-02102-2 conformance the nginx
ingress provided via `ssl-protocols` / `ssl-ciphers`. Key agreement now prefers
the hybrid post-quantum group X25519MLKEM768, falling back to P-256/P-384/P-521.

ALPN, TLS and PROXY protocol settings are now rendered into a single
Gateway-wide `ClientTrafficPolicy`, because Envoy Gateway rejects a second
policy targeting the same Gateway as `Conflicted` instead of merging it.

`gateway.tls.ecdhCurves` is validated against the crypto library named in the
new `gateway.tls.sslLibrary`, so a group the proxy image cannot offer fails at
template time rather than silently breaking the listener at runtime.

See the chart README for the conformance gaps that remain: TLS 1.3 cipher suites
are fixed by the crypto library and cannot be restricted by Envoy, and the
hybrid groups TR-02102-2 intends to recommend (SecP256r1MLKEM768 /
SecP384r1MLKEM1024) need an Envoy built against AWS-LC rather than BoringSSL.
243 changes: 238 additions & 5 deletions charts/wire-ingress/README.md

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions charts/wire-ingress/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,88 @@ Call with a dict: {https, ssl, base, websockets (bool)}.
{{- $csp = printf "%s upgrade-insecure-requests" $csp -}}
{{- $csp -}}
{{- end -}}

{{/*
TLS parameters shared by every ClientTrafficPolicy this chart renders.

These constrain what Envoy will negotiate with clients, and exist so the
deployment can stay conformant with BSI TR-02102-2 ("Cryptographic Mechanisms:
Recommendations and Key Lengths — Part 2: Use of Transport Layer Security"),
which the nginx-based ingress used to enforce via the `ssl-protocols`,
`ssl-ciphers` and `ssl_conf_command Ciphersuites` settings of
`charts/ingress-nginx-controller`.

Emits the `minVersion` / `maxVersion` / `ciphers` / `ecdhCurves` /
`signatureAlgorithms` keys of an Envoy Gateway `ClientTrafficPolicy`
`spec.tls`, unindented. Call with the root context and `nindent` the result.

Renders nothing when `gateway.tls.enabled` is false; callers guard on that so
they do not emit a stray blank line.
*/}}
{{- define "wire-ingress.tlsParameters" -}}
{{- $tls := .Values.gateway.tls -}}
{{- if $tls.enabled -}}
{{- $minVersion := $tls.minVersion | default "" | toString -}}
{{- $lib := $tls.sslLibrary | default "boringssl" -}}
{{- if not (has $lib (list "boringssl" "aws-lc" "openssl")) -}}
{{- fail (printf "gateway.tls.sslLibrary: %q is not one of boringssl, aws-lc, openssl" $lib) -}}
{{- end -}}
{{- if $minVersion }}
minVersion: {{ $minVersion | quote }}
{{- end }}
{{- if $tls.maxVersion }}
maxVersion: {{ $tls.maxVersion | toString | quote }}
{{- end }}
{{- /*
`ciphers` only applies to TLS 1.0-1.2 — TLS 1.3 suites are fixed by BoringSSL
and cannot be selected. Envoy Gateway rejects the resource outright (CEL
validation) if `ciphers` is set alongside `minVersion: "1.3"`, so drop it.
*/ -}}
{{- if and $tls.ciphers (ne $minVersion "1.3") }}
ciphers:
{{- range $tls.ciphers }}
- {{ . | quote }}
{{- end }}
{{- end }}
{{- if $tls.ecdhCurves }}
{{- /*
Envoy joins this list with ":" and hands it to SSL_CTX_set1_curves_list; if the
linked crypto library does not know a name, the whole listener is rejected and
the only trace is a line in the proxy log. Which names exist depends on how the
proxy image was built, so validate against the library named in
gateway.tls.sslLibrary. Unknown names are passed through — new groups appear
faster than this chart is updated — but a name that some OTHER library supports
is almost certainly a mismatch between the value and the running image.
*/ -}}
{{- $groups := dict
"boringssl" (list "P-224" "P-256" "P-384" "P-521" "X25519" "X25519Kyber768Draft00" "X25519MLKEM768" "MLKEM1024")
"aws-lc" (list "P-224" "P-256" "P-384" "P-521" "X25519" "SecP256r1MLKEM768" "X25519MLKEM768" "SecP384r1MLKEM1024" "MLKEM512" "MLKEM768" "MLKEM1024") -}}
{{- if hasKey $groups $lib }}
{{- $ok := index $groups $lib }}
{{- $anyLib := concat (index $groups "boringssl") (index $groups "aws-lc") }}
{{- range $curve := $tls.ecdhCurves }}
{{- if and (has $curve $anyLib) (not (has $curve $ok)) }}
{{- fail (printf "gateway.tls.ecdhCurves: %q is not implemented by %s, which gateway.tls.sslLibrary says the proxy image links against — Envoy would reject the listener at config load. SecP256r1MLKEM768 and SecP384r1MLKEM1024, the hybrid groups TR-02102-2 intends to recommend, exist only in AWS-LC: run an Envoy built with `--config=aws-lc-fips` and set gateway.tls.sslLibrary: aws-lc. On the stock BoringSSL image the only hybrid available is X25519MLKEM768. See the chart README." $curve $lib) }}
{{- end }}
{{- if not (has $curve $anyLib) }}
{{- range $known := $anyLib }}
{{- if eq (lower $curve) (lower $known) }}
{{- fail (printf "gateway.tls.ecdhCurves: %q is spelled wrong — these are crypto-library group names and are case sensitive. Use %q. (Note the NIST curves are P-256 / P-384 / P-521, not secp256r1 / secp384r1 / secp521r1.)" $curve $known) }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
ecdhCurves:
{{- range $tls.ecdhCurves }}
- {{ . | quote }}
{{- end }}
{{- end }}
{{- if $tls.signatureAlgorithms }}
signatureAlgorithms:
{{- range $tls.signatureAlgorithms }}
- {{ . | quote }}
{{- end }}
{{- end }}
{{- end -}}
{{- end -}}
24 changes: 0 additions & 24 deletions charts/wire-ingress/templates/clienttrafficpolicy-alpn.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
{{- if .Values.federator.enabled }}
{{/* Envoy Gateway-specific (gateway.envoyproxy.io/v1alpha1).
Enforces mTLS client certificate validation on the federator listener only. */}}
Enforces mTLS client certificate validation on the federator listener only.

A section-scoped policy fully REPLACES the Gateway-wide one for its
listener (Envoy Gateway marks the Gateway-wide policy `Overridden` for this
section rather than merging field-by-field), so the ALPN and TLS parameters
from clienttrafficpolicy-gateway.yaml are repeated here. Without this the
federator listener would silently fall back to Envoy's defaults, which
include cipher suites that BSI TR-02102-2 does not recommend. */}}
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
Expand All @@ -25,4 +32,13 @@ spec:
caCertificateRefs:
- name: federator-ca
kind: ConfigMap
{{- if .Values.gateway.alpn.enabled }}
alpnProtocols:
{{- range .Values.gateway.alpn.protocols }}
- {{ . }}
{{- end }}
{{- end }}
{{- if .Values.gateway.tls.enabled }}
{{- include "wire-ingress.tlsParameters" . | trim | nindent 4 }}
{{- end }}
{{- end }}
48 changes: 48 additions & 0 deletions charts/wire-ingress/templates/clienttrafficpolicy-gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{{- $alpn := .Values.gateway.alpn.enabled -}}
{{- $tls := .Values.gateway.tls.enabled -}}
{{- $proxyProtocol := .Values.gateway.proxyProtocol.enabled -}}
{{- if or $alpn $tls $proxyProtocol }}
{{/* Envoy Gateway-specific (gateway.envoyproxy.io/v1alpha1).

Gateway-wide client-side settings: ALPN, TLS parameters and PROXY protocol.

These all live in ONE resource on purpose. Envoy Gateway attaches at most a
single ClientTrafficPolicy per target: a second policy targeting the same
Gateway is rejected with `Conflicted` rather than merged. Splitting these
into separate resources would silently drop whichever one lost the race.

Listeners that have their own section-scoped ClientTrafficPolicy (the
federator listener) do NOT inherit this one — Envoy Gateway marks this
policy `Overridden` for those sections — so that policy repeats the same
ALPN and TLS settings. */}}
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: {{ include "wire-ingress.gatewayName" . }}-client-traffic
namespace: {{ .Release.Namespace }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: {{ include "wire-ingress.gatewayName" . | quote }}
{{- if $proxyProtocol }}
proxyProtocol:
optional: {{ .Values.gateway.proxyProtocol.optional }}
{{- end }}
{{- if or $alpn $tls }}
tls:
{{- if $alpn }}
alpnProtocols:
{{- range .Values.gateway.alpn.protocols }}
- {{ . }}
{{- end }}
{{- end }}
{{- if $tls }}
{{- include "wire-ingress.tlsParameters" . | trim | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}

This file was deleted.

94 changes: 94 additions & 0 deletions charts/wire-ingress/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,100 @@ gateway:
# Optional hostname restriction for the HTTP listener. Set alongside
# listeners.https.hostname when using mergeGateways.
hostname: ""
# TLS parameters applied to every HTTPS listener of the Gateway (including the
# federator listener). Rendered into the ClientTrafficPolicy resources.
#
# The defaults restrict Envoy to the mechanisms recommended by BSI TR-02102-2
# ("Cryptographic Mechanisms: Recommendations and Key Lengths — Part 2: Use of
# Transport Layer Security"), matching what the nginx ingress enforced via
# ssl-protocols / ssl-ciphers in charts/ingress-nginx-controller.
#
# CAVEAT: Envoy cannot restrict TLS 1.3 cipher suites — they are fixed by
# BoringSSL and `ciphers` below only applies to TLS 1.0-1.2. A stock Envoy
# build therefore also offers TLS_CHACHA20_POLY1305_SHA256, which is not on
# the TR-02102-2 list. See the "TR-02102-2 conformance" section of the README
# for the two ways to close that gap.
tls:
# Set to false to leave all TLS parameters at Envoy's defaults.
enabled: true
# TR-02102-2 Table 2: TLS 1.3 (2032+) and TLS 1.2 (until end of 2031) are
# recommended; TLS 1.0/1.1 are not. Valid: Auto, "1.0", "1.1", "1.2", "1.3".
minVersion: "1.2"
maxVersion: "1.3"
# TR-02102-2 Table 3/4: recommended cipher suites for TLS 1.2.
# Ignored when minVersion is "1.3" (Envoy Gateway rejects the combination).
# The AES-128 counterparts (ECDHE-{ECDSA,RSA}-AES128-GCM-SHA256) are equally
# conformant and can be added here for broader client compatibility.
ciphers:
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
# Key agreement groups, in server preference order. These are BoringSSL
# group names, NOT IANA names — Envoy passes the list straight to
# SSL_CTX_set1_curves_list and refuses to start the listener if any name is
# unknown.
#
# X25519MLKEM768 first makes the handshake post-quantum: it is a hybrid of
# X25519 and ML-KEM-768, so it is at least as strong as X25519 alone and
# protects today's traffic against harvest-now-decrypt-later. PQ-capable
# clients (current Chrome and Firefox) send an X25519MLKEM768 key share in
# the first ClientHello, so this costs no extra round trip for them; clients
# that do not offer it simply fall back to the NIST curves below.
#
# The two groups TR-02102-2 names as its intended future recommendation —
# SecP256r1MLKEM768 and SecP384r1MLKEM1024 — are NOT available on the stock
# envoyproxy/envoy image, because BoringSSL does not implement them. They do
# exist in AWS-LC, which Envoy supports as an alternative crypto library
# (`bazel build --config=aws-lc-fips`). To use them, build that image, point
# gateway.envoyProxy.spec at it, and set sslLibrary: aws-lc below. See the
# README for what that costs. Otherwise the chart fails rendering with an
# explanatory message if you put those names here.
#
# For strict TR-02102-2 conformance today, drop X25519MLKEM768 and keep only
# the NIST curves: Tables 6 and 10 list secp256r1/384r1/521r1 (and the
# brainpool and ffdhe groups, which BoringSSL also does not implement), and
# no PQ group. X25519 — an Envoy default — is not on the BSI list either,
# which is why it is absent below as a standalone group.
ecdhCurves:
- X25519MLKEM768
- P-256
- P-384
- P-521
# Crypto library the Envoy proxy image is built against. Purely a chart-side
# validation hint: it decides which ecdhCurves names are accepted, and has
# no effect on the rendered ClientTrafficPolicy. Leave at boringssl unless
# you deploy a custom proxy image.
# boringssl - stock envoyproxy/envoy (default)
# aws-lc - built with --config=aws-lc-fips; adds SecP256r1MLKEM768,
# SecP384r1MLKEM1024, MLKEM512/768; note HTTP/3 is disabled in
# AWS-LC builds (irrelevant here, the Gateway serves h2 and
# http/1.1) and that this build is not covered by the Envoy
# project's own test matrix
# openssl - built with --config=openssl; curve names are not validated
sslLibrary: boringssl
# TR-02102-2 Tables 11 and 12, intersected with what BoringSSL accepts.
# Dropping Envoy's rsa_pkcs1_* defaults is the point: TR-02102-2 only
# recommended PKCS #1 v1.5 padding until the end of 2025.
#
# This list covers both certificates this chart can issue — ECDSA P-384 for
# the main cert (tls.privateKey below) and ECDSA P-256 for the federator
# cert — plus RSA-PSS for externally supplied RSA certificates.
#
# Only these names are valid; anything else makes Envoy reject the listener
# config at load time. In particular the TR also lists rsa_pss_pss_* and the
# ecdsa_brainpool* algorithms, which BoringSSL does not implement.
#
# Two things to check before changing this list:
# - it must cover the key type of your server certificate;
# - it also constrains the federator mTLS handshake, so remote backends
# presenting RSA client certificates over TLS 1.2 need rsa_pkcs1_sha256
# added back unless their certificates support PSS.
signatureAlgorithms:
- ecdsa_secp256r1_sha256
- ecdsa_secp384r1_sha384
- ecdsa_secp521r1_sha512
- rsa_pss_rsae_sha256
- rsa_pss_rsae_sha384
- rsa_pss_rsae_sha512
# ALPN configuration for HTTP/2 support with multiple listeners.
# When using multiple listeners with overlapping certificate SANs,
# Envoy Gateway may downgrade to HTTP/1.1. This ClientTrafficPolicy
Expand Down
24 changes: 24 additions & 0 deletions hack/envoy-aws-lc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Envoy proxy image linked against AWS-LC instead of BoringSSL.
#
# Built by ./build.sh, which produces the `envoy-static` binary in this
# directory first. See charts/wire-ingress/README.md for why.
#
# Layout mirrors what Envoy Gateway expects of the stock
# envoyproxy/envoy:distroless-* image: the binary must be reachable as `envoy`
# on PATH (Envoy Gateway sets `command: ["envoy"]`), and the container runs as
# uid/gid 65532 (`runAsNonRoot: true`, `runAsUser: 65532`).
#
# distroless `base` gives glibc (envoy-static is static apart from libc),
# ca-certificates and tzdata. The `nonroot` tag already defaults to 65532.
ARG BASE_IMAGE=gcr.io/distroless/base-debian12:nonroot
FROM ${BASE_IMAGE}

COPY --chown=65532:65532 envoy-static /usr/local/bin/envoy

# Set explicitly rather than relying on the base image's PATH: Envoy Gateway
# invokes the bare name `envoy`, so an unset PATH would be a CrashLoopBackOff
# with a confusing message.
ENV PATH=/usr/local/bin:/usr/bin:/bin

USER 65532:65532
ENTRYPOINT ["/usr/local/bin/envoy"]
Loading