This episode secures communication between services: Envoy as a microservices gateway, TLS contexts and certificate validation, and authorization policies using Envoy's RBAC filter.

After securing the transport path in episode 6, it's time to secure who is allowed to talk. Episode 12 covers secure service-to-service communication: Envoy as a trusted gateway for microservices, TLS contexts and certificate validation, and Envoy's RBAC filter for authorization policies. By the end of this episode, you have a basic security pattern: encryption at the transport layer, verified identities, and role-based authorization that can be extended.
In a service mesh architecture, every service communicates through the Envoy in front of it. Envoy acts as a secure gateway: all incoming and outgoing traffic must pass through it, so security policy is enforced at a single point.
listeners:
- name: sidecar_inbound
address:
socket_address:
address: 0.0.0.0
port_value: 15006
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/service.pem
private_key:
filename: /etc/envoy/certs/service-key.pem
validation_context:
trusted_ca:
filename: /etc/envoy/certs/ca.pem
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: service_ingress
route_config:
name: service_routes
virtual_hosts:
- name: service_vh
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: local_service
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterThe require_client_certificate: true configuration forces every client to present a certificate when talking to this service. Traffic without a valid certificate is rejected before reaching the application.
With this pattern, security doesn't depend on every developer's discipline. A policy in one Envoy config applies to all traffic — the main reason service meshes attach a sidecar to every pod.
When Envoy talks to another service, it verifies the counterpart's certificate:
clusters:
- name: orders_service
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
validation_context:
trusted_ca:
filename: /etc/envoy/certs/ca.pem
match_subject_alt_names:
- exact: orders.internal
match_typed_subject_alt_names:
- san_type: URI
matcher:
exact: spiffe://example.org/ns/prod/sa/orders
load_assignment:
cluster_name: orders_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: orders.internal
port_value: 8443The match_typed_subject_alt_names part shows the SPIFFE pattern — the standard identity URI in the service mesh world. This verification ensures Envoy only talks to the orders service in the prod namespace.
The strongest pattern uses SAN identity rather than just checking the certificate chain. With SPIFFE, the service identity lives in the certificate itself, so TLS verification becomes identity verification at the same time.
RBAC (Role-Based Access Control) lets Envoy enforce attribute-based access policies:
http_filters:
- name: envoy.filters.http.rbac
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
rules:
action: ALLOW
policies:
admin_policy:
permissions:
- header:
name: ":method"
string_match:
exact: GET
principals:
- authenticated:
principal_name:
exact: spiffe://example.org/ns/prod/sa/admin
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterThe rbac filter with action ALLOW rejects all requests that don't match. The admin_policy policy allows only GET requests from the admin SPIFFE principal; everything else automatically receives a 403.
For protection at the connection layer before HTTP is processed:
filters:
- name: envoy.filters.network.rbac
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC
rules:
action: ALLOW
policies:
internal_only:
permissions:
- destination_port: 8443
principals:
- authenticated:
principal_name:
exact: spiffe://example.org/ns/prod/sa/*The network.rbac configuration rejects TCP connections from unknown principals even before an HTTP request is formed. It's a cheap first line of defense.
curl -s -o /dev/null -w "%{http_code}\n" \
--cert ~/envoy-lab/certs/client.pem --key ~/envoy-lab/certs/client-key.pem \
--cacert ~/envoy-lab/certs/ca.pem https://localhost:10443/orders
curl -s -o /dev/null -w "%{http_code}\n" \
--cacert ~/envoy-lab/certs/ca.pem https://localhost:10443/ordersA request without a client certificate should fail at the TLS layer, while the first request is evaluated by the RBAC filter. The curl -w "%{http_code}" command prints only the status code — a clean way to test policies.
Envoy records RBAC decisions in metrics:
curl -s localhost:9901/stats | grep "rbac"The rbac statistics show how many requests were allowed and denied per listener. If denials jump suddenly, a service's identity probably doesn't match — check the principal in match_typed_subject_alt_names.
When drafting a new policy, enable decision logging first. Envoy supports shadow_rules, which records decisions without actually rejecting requests — perfect for testing a policy in production risk-free.
Episode 12 assembled the service-to-service security layer: Envoy as each service's trusted gateway, TLS contexts with SAN and SPIFFE verification, and the RBAC filter enforcing access policies.
Key takeaways:
require_client_certificate forces clients to prove their identity.In the next episode, episode 13, we'll discuss API gateway patterns and edge proxy — Envoy as an edge proxy and API gateway, virtual hosts and rate limiting, authentication and CORS, and when to use a gateway versus an internal sidecar.