TLS
Envoy supports both TLS termination in listeners as well as TLS origination when making connections to upstream clusters. Support is sufficient for Envoy to perform standard edge proxy duties for modern web services as well as to initiate connections with external services that have advanced TLS requirements (TLS1.2, SNI, etc.). Envoy supports the following TLS features:
Configurable ciphers: Each TLS listener and client can specify the ciphers that it supports.
Client certificates: Upstream/client connections can present a client certificate in addition to server certificate verification.
Certificate verification and pinning: Certificate verification options include basic chain verification, subject name verification, and hash pinning.
Certificate revocation: Envoy can check peer certificates against a certificate revocation list (CRL) if one is provided.
ALPN: TLS listeners support ALPN. The HTTP connection manager uses this information (in addition to protocol inference) to determine whether a client is speaking HTTP/1.1 or HTTP/2.
SNI: SNI is supported for both server (listener) and client (upstream) connections.
Session resumption: Server connections support resuming previous sessions via TLS session tickets (see RFC 5077). Resumption can be performed across hot restarts and between parallel Envoy instances (typically useful in a front proxy configuration).
BoringSSL private key methods: TLS private key operations (signing and decrypting) can be performed asynchronously from an extension. This allows extending Envoy to support various key management schemes (such as TPM) and TLS acceleration. This mechanism uses BoringSSL private key method interface.
OCSP Stapling: Online Certificate Stapling Protocol responses may be stapled to certificates.
Underlying implementation
Currently Envoy is written to use BoringSSL as the TLS provider.
FIPS 140-2
BoringSSL can be built in a
FIPS-compliant mode,
following the build instructions from the Security Policy for BoringCrypto module,
using --define boringssl=fips
Bazel option. Currently, this option is only available on Linux-x86_64.
The correctness of the FIPS build can be verified by checking the presence of BoringSSL-FIPS
in the --version
output.
It’s important to note that while using FIPS-compliant module is necessary for FIPS compliance, it’s not sufficient by itself, and depending on the context, additional steps might be necessary. The extra requirements may include using only approved algorithms and/or using only private keys generated by a module operating in FIPS-approved mode. For more information, please refer to the Security Policy for BoringCrypto module and/or an accredited CMVP laboratory.
Please note that the FIPS-compliant build is based on an older version of BoringSSL than the non-FIPS build, and it doesn’t support the most recent QUIC APIs.
Enabling certificate verification
Certificate verification of both upstream and downstream connections is not enabled unless the validation context specifies one or more trusted authority certificates.
Example configuration
static_resources:
listeners:
- name: listener_0
address: {socket_address: {address: 127.0.0.1, port_value: 10000}}
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
virtual_hosts:
- name: default
domains: ["*"]
routes:
- match: {prefix: "/"}
route:
cluster: some_service
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: {filename: "certs/servercert.pem"}
private_key: {filename: "certs/serverkey.pem"}
validation_context:
trusted_ca:
filename: certs/cacert.pem
clusters:
- name: some_service
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 1234
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: {"filename": "certs/servercert.pem"}
private_key: {"filename": "certs/serverkey.pem"}
ocsp_staple: {"filename": "certs/server_ocsp_resp.der"}
validation_context:
match_typed_subject_alt_names:
- san_type: DNS
matcher:
exact: "foo"
trusted_ca:
filename: /etc/ssl/certs/ca-certificates.crt
/etc/ssl/certs/ca-certificates.crt is the default path for the system CA bundle on Debian systems. trusted_ca along with match_typed_subject_alt_names makes Envoy verify the server identity of 127.0.0.1:1234 as “foo” in the same way as e.g. cURL does on standard Debian installations. Common paths for system CA bundles on Linux and BSD are:
/etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo etc.)
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7)
/etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6)
/etc/ssl/ca-bundle.pem (OpenSUSE)
/usr/local/etc/ssl/cert.pem (FreeBSD)
/etc/ssl/cert.pem (OpenBSD)
See the reference for UpstreamTlsContexts and DownstreamTlsContexts for other TLS options.
Attention
If only trusted_ca is specified, Envoy will verify the certificate chain of the presented certificate, but not its subject name, hash, etc. Other validation context configuration is typically required depending on the deployment.
Custom Certificate Validator
The configuration explained above is used by the “default” certificate validator.
Envoy also supports custom validators in envoy.tls.cert_validator
extension category which can be
configured on CertificateValidationContext.
For example, Envoy can be configured to verify peer certificates following the SPIFFE specification with multiple trust bundles in a single listener or cluster. For more detail, please refer to the documentation of custom_validator_config field.
Certificate selection
DownstreamTlsContexts support multiple TLS certificates. These may be a mix of RSA and P-256 ECDSA certificates for multiple server name patterns.
Certificate config/loading rules:
DNS SANs or Subject Common Name is extracted as server name pattern to match SNI during handshake. Subject Common Name is not used if DNS SANs are present in the certificate.
FQDN like “test.example.com” and wildcard like “*.example.com” are valid at the same time, which will be loaded as two different server name patterns.
If multiple certificates of a particular type (RSA or ECDSA) are specified for the same name or name pattern, the first one loaded is used for that name.
Non-P-256 server ECDSA certificates are rejected.
Static and SDS certificates may not be mixed in a given DownstreamTlsContext.
Certificate selection rules:
If the client supports SNI, e.g. SNI is “test.example.com”, it looks for a cert that exactly matches to the SNI. If the certificate adheres to the OCSP policy and matches to key type, it is selected for handshake. If the certificate adheres to the OCSP policy, but key type is RSA while client is ECDSA capable, it is marked as as the candidate and continues searching until a cert is selected with perfect match or certs exhausted. Candidate will be selected for handshake if there is no perfect match.
If the client supports SNI, but no cert is selected from certs that exactly matches to SNI, it matches on wildcard server name. e.g. if SNI is “test.example.com”, a certificate with “test.example.com” will be preferred over “*.example.com”. And wildcard matching only works for 1 level of depth, so “*.com” will not be a match for “test.example.com”. Afterwards, it execuates OCSP and key type checking on each cert which is the same as what happens after exact SNI matching.
If no cert is selected from certs that matches wildcard name, the candidate cert is selected for handshake if it is present. If there is no candidate, check full_scan_certs_on_sni_mismatch, go to full scan all certificates if it is enabled, otherwise pick the first certificate for handshake.
If the client does not provide SNI at all, go to full scan no matter full_scan_certs_on_sni_mismatch is false or true.
Full scan execuates OCSP and key type checking on each cert which is the same as described above in exact SNI matching. It falls back to the first cert in the whole list if there is no cert selected.
Currently only two kinds of key type are supported, RSA or ECDSA. If the client supports P-256 ECDSA, the P-256 ECDSA certificate is preferred over RSA. The certificate that it falls back to might result in a failed handshake. For instance, a client only supports RSA certificates and the certificate only support ECDSA.
The final selected certificate must adhere to the OCSP policy. If no such certificate is found, the connection is refused.
Note
With the support of SNI-based certificate selection, it allows configuring large number of certificates for multiple hostnames. full_scan_certs_on_sni_mismatch is introduced to determine if we continue full scan on SNI mismatch when the client provides SNI. SNI mismatch contains two cases in this context, one is there is no cert that matches to SNI, another one is there are certs matches to SNI while OCSP policy fails on those certs. The full_scan_certs_on_sni_mismatch defaults to false, so full scan is disabled by default. If full scan is enabled, it will look for the cert from the whole cert list on SNI mismatch, this could be a problem for a potential DoS attack because of O(n) complexity.
Only a single TLS certificate is supported today for UpstreamTlsContexts.
Secret discovery service (SDS)
TLS certificates can be specified in the static resource or can be fetched remotely. Certificate rotation is supported for static resources by sourcing SDS configuration from the filesystem or by pushing updates from the SDS server. Please see SDS for details.
OCSP Stapling
DownstreamTlsContexts support
stapling an Online Certificate Status Protocol (OCSP) response to a TLS certificate during the handshake. The
ocsp_staple
field allows the operator to supply a pre-computed OCSP response per-certificate in the context.
A single response may not pertain to multiple certificates. If provided, OCSP responses must be valid and
affirm the certificate has not been revoked. Expired OCSP responses are accepted, but may cause downstream
connection errors depending on the OCSP staple policy.
DownstreamTlsContexts
support an ocsp_staple_policy
field to control whether Envoy should stop using a certificate or
continue without stapling when its associated OCSP response is missing or expired.
Certificates marked as must-staple require a
valid OCSP response regardless of the OCSP staple policy. In practice, a must-staple certificate causes
cEnvoy to behave as if the OCSP staple policy is MUST_STAPLE.
Envoy will not use a must-staple certificate for new connections after its OCSP response expires.
OCSP responses are never stapled to TLS requests that do not indicate support for OCSP stapling
via the status_request
extension.
The following runtime flags are provided to adjust the requirements of OCSP responses and override
the OCSP policy. These flags default to true
.
envoy.reloadable_features.require_ocsp_response_for_must_staple_certs
: Disabling this allows the operator to omit an OCSP response for must-staple certs in the config.envoy.reloadable_features.check_ocsp_policy
: Disabling this will disable OCSP policy checking. OCSP responses are stapled when available if the client supports it, even if the response is expired. Stapling is skipped if no response is present.
OCSP responses are ignored for UpstreamTlsContexts.
Authentication filter
Envoy provides a network filter that performs TLS client authentication via principals fetched from a REST VPN service. This filter matches the presented client certificate hash against the principal list to determine whether the connection should be allowed or not. Optional IP allowlisting can also be configured. This functionality can be used to build edge proxy VPN support for web infrastructure.
Client TLS authentication filter configuration reference.
Custom handshaker extension
The CommonTlsContext
has a custom_handshaker
extension which can be used to override SSL handshake
behavior entirely. This is useful for implementing any TLS behavior which is
difficult to express with callbacks. It is not necessary to write a custom
handshaker to use private key methods, see the
private key method interface described above.
To avoid reimplementing all of the Ssl::ConnectionInfo interface, a custom implementation might choose to extend Envoy::Extensions::TransportSockets::Tls::SslHandshakerImpl.
Custom handshakers need to explicitly declare via HandshakerCapabilities which TLS features they are responsible for. The default Envoy handshaker will manage the remainder.
A useful example handshaker, named SslHandshakerImplForTest
, lives in
this test
and demonstrates special-case SSL_ERROR
handling and callbacks.
Trouble shooting
When Envoy originates TLS when making connections to upstream clusters, any errors will be logged into UPSTREAM_TRANSPORT_FAILURE_REASON field or AccessLogCommon.upstream_transport_failure_reason field.
When Envoy listener gets connection and perform TLS with downstream, any errors will be logged into DOWNSTREAM_TRANSPORT_FAILURE_REASON field or AccessLogCommon.downstream_transport_failure_reason field.
Common errors are:
Secret is not supplied by SDS
: Envoy is still waiting SDS to deliver key/cert or root CA.SSLV3_ALERT_CERTIFICATE_EXPIRED
: Peer certificate is expired and not allowed in config.SSLV3_ALERT_CERTIFICATE_UNKNOWN
: Peer certificate is not in config specified SPKI.SSLV3_ALERT_HANDSHAKE_FAILURE
: Handshake failed, usually due to upstream requires client certificate but not presented.TLSV1_ALERT_PROTOCOL_VERSION
: TLS protocol version mismatch.TLSV1_ALERT_UNKNOWN_CA
: Peer certificate CA is not in trusted CA.
More detailed list of error that can be raised by BoringSSL can be found here