quic: support client certificate authentication on QUIC listeners#45981
Open
bpalermo wants to merge 6 commits into
Open
quic: support client certificate authentication on QUIC listeners#45981bpalermo wants to merge 6 commits into
bpalermo wants to merge 6 commits into
Conversation
QuicSslConnectionInfo stubbed every peer certificate accessor with empty values (TODO envoyproxy#23809), so peer certificate details of HTTP/3 connections (e.g. upstream access log fields) were always empty. The X509-based BoringSSL getters used by ConnectionInfoImplBase are not usable on QUIC's CRYPTO_BUFFER-based SSL object, so the peer certificate accessors in the base class are now routed through two protected virtual hooks (peerCertificate/peerCertificateChain) whose default implementation preserves the existing TCP TLS behavior. QuicSslConnectionInfo overrides the hooks by converting the CRYPTO_BUFFER peer chain to X509 once and caching it, and drops the stubs so all base class accessors work. Local certificate accessors remain unsupported on QUIC. This is also groundwork for QUIC client certificate authentication (envoyproxy#23809): once client certs are requested, XFCC and RBAC principals need these fields populated. Signed-off-by: Bruno Palermo <b@palermo.dev>
The client certificates configured in a cluster's upstream TLS context were silently not sent over HTTP/3: they are loaded into the Envoy ClientContextImpl but were never installed on the QUICHE client SSL context, so upstream QUIC servers requesting a client certificate got none. The certificate chain and private key are now installed on the QUICHE SSL context (via SSL_CTX_set_chain_and_key, since QUICHE uses the CRYPTO_BUFFER-based SSL method) when the crypto config is created or refreshed. Client certificates using a private key provider are not supported over QUIC and are skipped with a warning. Guarded by envoy.reloadable_features.quic_upstream_client_certificates (default true) since this changes the wire behavior of existing accepted configurations. Signed-off-by: Bruno Palermo <b@palermo.dev>
Honors require_client_certificate in the QUIC downstream TLS context (previously rejected at config load time with 'TLS Client Authentication is not supported over QUIC'). Fixes envoyproxy#23809. - EnvoyQuicServerSession::GetSSLConfig() maps require_client_certificate to quic::ClientCertMode::kRequire per filter chain, making QUICHE send a CertificateRequest during the handshake. - EnvoyTlsServerHandshaker overrides VerifyCertChain to validate the presented client chain against the pinned per-connection ServerContextImpl via customVerifyCertChainForQuic, with full support for asynchronous cert validators (Pending -> QUIC_PENDING -> ProofVerifierCallback), mirroring the client-side proof verifier. - OnProofVerifyDetailsAvailable marks the connection SSL info validated so peerCertificateValidated() and XFCC reflect the handshake result. - The Envoy crypto stream factory now creates EnvoyTlsServerHandshaker whenever client certs are required, independent of the session ticket runtime flag (resumption stays disabled in that case since the ticket callback was not installed). - Third-party crypto stream factories fail closed: connections on filter chains requiring client certs are rejected unless the factory declares supportsClientCertificateAuthentication(), because QUICHE would otherwise accept the certificate without validation. Signed-off-by: Bruno Palermo <b@palermo.dev>
Signed-off-by: Bruno Palermo <b@palermo.dev>
QUICHE's async proof-verify completion assumes a client connection and resumes with a plain AdvanceHandshake(), which asserts server-side that a packet flusher is attached. Route async validation completion through the handshaker, attaching a ScopedPacketFlusher and notifying the delegate, mirroring TlsServerHandshaker::AdvanceHandshakeFromCallback. The pending callback is cancelled if the handshaker is destroyed first. Also unsequence the transportSocketFactory() mock expectation in the active QUIC listener test: the dispatcher now queries it before creating the network filter chain for the client certificate fail-closed check. Signed-off-by: Bruno Palermo <b@palermo.dev>
|
Hi @bpalermo, welcome and thank you for your contribution. We will try to review your Pull Request as quickly as possible. In the meantime, please take a look at the contribution guidelines if you have not done so already. |
|
CC @envoyproxy/runtime-guard-changes: FYI only for changes made to |
When client certificates are required, QUIC does not resume sessions: every connection performs a full handshake and re-validates the client certificate (BoringSSL declines to resume a session carrying a client credential in the QUIC configuration). This is stricter than TCP TLS and means no client identity is ever attached to a resumed or 0-RTT connection without a fresh validation. Adds an integration test pinning that a second mTLS connection does a full handshake (not resumption, not 0-RTT) and re-validates the client certificate, and documents the behavior on QuicDownstreamTransport. Signed-off-by: Bruno Palermo <b@palermo.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Commit Message
quic: support client certificate authentication on QUIC listeners
Additional Description
Part 3 of re-introducing QUIC client certificate authentication. Fixes #23809; supersedes the
stale #40017 (and #39766). Stacked on #45978 (connection info) and #45980 (upstream client
certs) — the first two commits are theirs; this PR will be rebased as they land. Only the last
two commits are new here.
This honors
require_client_certificatein the QUIC downstream TLS context, previously rejectedat configuration load time with "TLS Client Authentication is not supported over QUIC".
How it works (directly addressing the review questions on #39766 about where verification
happens and how it wires into Envoy's async cert validators):
EnvoyQuicServerSession::GetSSLConfig()maps the filter chain'srequire_client_certificateto
quic::ClientCertMode::kRequire, making QUICHE send a CertificateRequest and require thecertificate during the handshake.
EnvoyTlsServerHandshaker::VerifyCertChain(QUICHE'sTlsHandshaker::VerifyCertChainis virtual). This handshaker already exists for session-ticketsupport and pins the exact filter chain's
ServerContextImplat connection creation, so nofilter-chain re-lookup is needed (unlike quic: enable client certificate authentication support #40017, which re-found the filter chain with a
synthetic localhost peer address at the crypto-config level). The chain is validated via the
existing
customVerifyCertChainForQuic(..., is_server=true, ...), so default, SPIFFE andcustom cert validators behave exactly like TCP TLS.
Pending→QUIC_PENDING, and completion resumes theserver handshake with a packet flusher attached (mirroring
TlsServerHandshaker::AdvanceHandshakeFromCallback; QUICHE's own proof-verify completion pathassumes a client connection). A pending validation is cancelled if the connection goes away
first.
OnProofVerifyDetailsAvailablemarks the connection's SSL info validated, sopeerCertificateValidated(), XFCC and RBAC principals reflect the handshake result (peer certfields come from quic: populate peer certificate details in QUIC connection info #45978).
EnvoyTlsServerHandshakerwhenever the filterchain requires client certificates, independent of the
envoy.reloadable_features.quic_session_ticket_supportruntime flag (resumption stays disabledin that case since the ticket callback was not installed on the SSL context). The non-mTLS,
flag-off path is unchanged.
supportsClientCertificateAuthentication()capability onEnvoyQuicCryptoServerStreamFactoryInterface(default false, true for the Envoy factory).Connections on filter chains requiring client certs are rejected when the configured factory
can't validate them, because QUICHE's
proof_verifier_ == nullptrpath would otherwise acceptthe requested certificate without any validation.
Decisions to confirm with maintainers:
require_client_certificatedoes NOT map tokRequest(TCP requests-and-validates-if-presented in that case): doing so would change thewire behavior of accepted, working configs. Documented in the proto comment; can be a
runtime-guarded follow-up.
load today, so the config itself is the opt-in. Happy to add a guard around the config-load
acceptance if preferred.
resumption after mTLS and 0-RTT+mTLS interactions may deserve explicit handling (e.g.
disabling early data on mTLS filter chains) — input welcome.
SSL_set0_client_CAsneedsCRYPTO_BUFFER plumbing); most clients select certificates without CA hints. Known gap.
possible because the crypto-stream factory is per-listener while the requirement is
per-filter-chain and filter chains update dynamically).
Risk Level
Medium — new handshake behavior is only reachable via configs that are rejected today; the
crypto-stream gating restructure preserves the existing non-mTLS paths.
Testing
New
test/integration/quic_mtls_integration_test.ccwith real QUIC handshakes: valid clientcert (200 + XFCC Hash/Subject forwarded), no client cert (handshake rejected,
PEER_DID_NOT_RETURN_A_CERTIFICATE), untrusted client cert (handshake rejected, unknown CA),
asynchronous validator (TimedCertValidator, pending → success), and no-mTLS regression. Unit
tests for config acceptance and
requireClientCertificate(). Full//test/common/quic/...suite passes.
Docs Changes
Proto docs for
QuicDownstreamTransportdescribing the supported behavior and the kRequestdivergence from TCP.
Release Notes
Added a new-feature changelog fragment.
Platform Specific Features
N/A