Skip to content

Last updated: 2026-05-15

Envoy TLS Configuration

Envoy handles TLS in two directions: downstream (terminating TLS from clients, configured on listeners) and upstream (originating TLS to backends, configured on clusters). Both use a common_tls_context block with the same structure; only the wrapper type differs.

Envoy's config is Protobuf-defined; the YAML below is the standard static file format.

Downstream TLS (Listener)

To terminate TLS on a listener, add a transport_socket with DownstreamTlsContext:

static_resources:
  listeners:
  - name: listener_https
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    filter_chains:
    - transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          require_client_certificate: false
          common_tls_context:
            tls_params:
              tls_minimum_protocol_version: TLSv1_2
              tls_maximum_protocol_version: TLSv1_3
              cipher_suites:
              - ECDHE-ECDSA-AES128-GCM-SHA256
              - ECDHE-RSA-AES128-GCM-SHA256
              - ECDHE-ECDSA-AES256-GCM-SHA384
              - ECDHE-RSA-AES256-GCM-SHA384
              - ECDHE-ECDSA-CHACHA20-POLY1305
              - ECDHE-RSA-CHACHA20-POLY1305
            tls_certificates:
            - certificate_chain:
                filename: /etc/envoy/ssl/server.crt
              private_key:
                filename: /etc/envoy/ssl/server.key
            validation_context:
              trusted_ca:
                filename: /etc/envoy/ssl/ca.crt

Setting require_client_certificate: true enables mutual TLS: clients must present a certificate signed by trusted_ca.

cipher_suites applies only to TLS 1.2. TLS 1.3 ciphers are fixed by BoringSSL (AES-256-GCM-SHA384, CHACHA20-POLY1305-SHA256, AES-128-GCM-SHA256) and are not configurable.

Upstream TLS (Cluster)

To originate TLS to a backend, add a transport_socket with UpstreamTlsContext on the cluster. Always configure validation_context; skipping it means Envoy encrypts the connection but doesn't verify the backend's certificate.

  clusters:
  - name: backend
    type: STRICT_DNS
    load_assignment:
      cluster_name: backend
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: backend.internal
                port_value: 8443
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        common_tls_context:
          tls_params:
            tls_minimum_protocol_version: TLSv1_2
          tls_certificates:
          - certificate_chain:
              filename: /etc/envoy/ssl/client.crt
            private_key:
              filename: /etc/envoy/ssl/client.key
          validation_context:
            trusted_ca:
              filename: /etc/envoy/ssl/ca.crt
            match_typed_subject_alt_names:
            - san_type: DNS
              matcher:
                exact: backend.internal

match_typed_subject_alt_names pins the expected SAN on the backend's certificate. Without it, Envoy verifies the CA chain but not the hostname, equivalent to verify-ca rather than verify-full.

Mutual TLS

For mTLS in both directions, combine the settings:

  • Listener: require_client_certificate: true + validation_context with the client CA
  • Cluster: tls_certificates with the client certificate and key + validation_context for the server

In service mesh deployments where a single CA (Vault, cert-manager, etc.) issues all certificates, the same certificate pair is often used for both client and server roles on each workload.

See RFC 8446 ยง4.3.2 for the TLS Certificate Request specification, and Wikipedia: Mutual authentication for a general overview.

Security Notes

Envoy uses BoringSSL, Google's fork of OpenSSL, which was created after Heartbleed was patched and has never been vulnerable to it. The cipher suite and protocol configuration in this guide addresses the following known TLS vulnerabilities:

  • POODLE (CVE-2014-3566, 2014): SSL 3.0 is not supported in BoringSSL.
  • BEAST (CVE-2011-3389, 2011): Mitigated by recommending TLS 1.2 as the minimum; AEAD-only ciphers eliminate the CBC padding oracle.
  • CRIME (CVE-2012-4929, 2012): TLS compression is not supported in BoringSSL.
  • Lucky13 (2013): AEAD-only cipher list eliminates CBC padding timing side-channels entirely.
  • FREAK (CVE-2015-0204, 2015): EXPORT-grade ciphers are not supported in BoringSSL.
  • LOGJAM (CVE-2015-4000, 2015): Short-key DHE is excluded; only ECDHE key exchange is recommended.
  • Sweet32 (CVE-2016-2183, 2016): 3DES is excluded from the cipher list; deprecated in BoringSSL.
  • ROBOT (CVE-2017-13099, 2017): Static RSA key exchange is excluded; only ECDHE is recommended.
  • Downgrade attacks: TLS_FALLBACK_SCSV is supported in BoringSSL.

The following are not addressable through TLS configuration alone:

  • Heartbleed (CVE-2014-0160, 2014): Not applicable. BoringSSL forked from OpenSSL after Heartbleed was fixed and has never been vulnerable.
  • BREACH (CVE-2013-3587, 2013): Exploits HTTP-level response compression. Mitigated at the application or service mesh layer; TLS configuration cannot prevent it.
  • DROWN (CVE-2016-0800, 2016): Not applicable. BoringSSL does not support SSLv2.

Verification

The Envoy admin interface shows all loaded certificates:

curl http://localhost:9901/certs

This returns certificate details including SANs and expiry dates. Test the TLS handshake directly:

openssl s_client -connect localhost:443 -CAfile /etc/envoy/ssl/ca.crt

Related Guides

View all Web Servers & Proxies guides →

Configured TLS? Now Monitor It.

Generator Labs alerts you before certificates expire, get revoked, or fail chain validation — across HTTPS, SMTPS, IMAPS, LDAPS, and more.

Certificate Monitoring →