Proxies·8 min read·

Zero-Trust Edge Gateway: Implementing Mutual TLS (mTLS) with Envoy Proxy

Establish cryptographic identity verification between services by deploying Envoy Proxy configured for strict mutual TLS (mTLS) authentication.

NB

Netbay Cloud Team

Netbay Engineering

On this page

Traditional network architectures rely on perimeter defenses: once traffic passes the outer firewall, internal communication between microservices often travels unencrypted in plaintext. Zero-trust security models reject this assumption. Under zero trust, every network request must be cryptographically authenticated and authorized regardless of source IP address.

Mutual TLS (mTLS) enforces two-way verification: the server authenticates itself to the client, and the client must present a valid x509 certificate signed by an approved internal Certificate Authority (CA) before any HTTP data is transmitted. Envoy Proxy is purpose-built for zero-trust mTLS gateway deployments.

Envoy Proxy was designed from the ground up by Lyft and CNCF to serve as a high-performance communication bus for modern microservice architectures. When configured for mTLS, Envoy handles the complex cryptographic verification steps in C++, validating client certificate chains, checking Subject Alternative Names (SANs) or SPIFFE IDs, and extracting client metadata into standardized HTTP headers before forwarding verified traffic to backend services.

Expand
Envoy Proxy mTLS Gateway
Envoy Proxy zero trust mutual TLS authentication gateway

Generating Internal CA and Service Certificates

Generate a private root Certificate Authority and issue signed certificates for Envoy and authorized clients:

Envoy Mutual TLS (mTLS) Cryptographic Handshake Client Application Presents client.crt Signed by Private CA SAN: spiffe://org/api Envoy Edge Gateway require_client_cert: true Validates client against CA Rejects untrusted certs Protected Backend Local unix socket / tcp Authenticated caller Header: X-Client-Cert Zero-Trust Security Boundary: Network possession is no longer assumed safe. Even if an attacker controls adjacent internal IP addresses, they cannot issue a single API request without a valid cryptographically signed x509 client certificate.
bash
# Create dedicated directory for PKI assets
mkdir -p /etc/envoy/certs && cd /etc/envoy/certs

# Generate private Root CA key and certificate
openssl req -x509 -newkey rsa:4096 -days 3650 -nodes   -keyout ca.key -out ca.crt -subj "/CN=Internal-ZeroTrust-Root-CA"

# Generate Envoy server key and CSR
openssl req -newkey rsa:2048 -nodes   -keyout server.key -out server.csr -subj "/CN=gateway.internal"

# Sign server certificate with Root CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key   -CAcreateserial -out server.crt -days 365

# Generate client certificate
openssl req -newkey rsa:2048 -nodes   -keyout client.key -out client.csr -subj "/CN=trusted-service-1"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key   -CAcreateserial -out client.crt -days 365

Configuring Envoy Proxy for Strict Client Certificate Verification

Configure envoy.yaml with a DownstreamTlsContext enforcing require_client_certificate: true:

yaml
static_resources:
  listeners:
  - name: mtls_listener
    address:
      socket_address: { address: 0.0.0.0, port_value: 8443 }
    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: true
          common_tls_context:
            tls_certificates:
            - certificate_chain: { filename: "/etc/envoy/certs/server.crt" }
              private_key: { filename: "/etc/envoy/certs/server.key" }
            validation_context:
              trusted_ca: { filename: "/etc/envoy/certs/ca.crt" }
      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:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: internal_backend }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: internal_backend
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: internal_backend
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: 127.0.0.1, port_value: 8080 }

In this configuration, if an incoming TCP handshake fails to present a client certificate or if the presented certificate is not signed by ca.crt, Envoy immediately terminates the TLS handshake with an 'unknown_ca' or 'bad_certificate' alert. The application backend on port 8080 never receives a single byte of untrusted data.

Validating mTLS Enforcement

Test connecting with and without client certificates:

bash
# Connection without client certificate fails during handshake
curl -k https://127.0.0.1:8443/

# Connection with valid client certificate succeeds
curl --cacert /etc/envoy/certs/ca.crt      --cert /etc/envoy/certs/client.crt      --key /etc/envoy/certs/client.key      https://127.0.0.1:8443/

Deploying Envoy Proxy with strict mTLS guarantees that unauthorized network callers are terminated at the TLS layer before reaching application code.

Envoy Proxy Architecture and mTLS
YouTube

You can spin up an Ubuntu 24.04 instance on Netbay in under 60 seconds with Intel Xeon Platinum compute and high-speed SSD storage in Lucknow — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance