·2 min read·← All posts
mTLS Envoy SPIRE Service Mesh

The pattern

Without a service mesh, every service has to:

That’s per-service code. It drifts. It’s the most-skipped part of zero-trust deployments.

With Envoy + SPIRE, all of the above moves into the sidecar. The application speaks plaintext HTTP to its sidecar; the sidecar handles TLS + mTLS + cert rotation transparently.

The pieces

┌─────────────────────────┐    ┌─────────────────────────┐
│ Service A pod           │    │ Service B pod           │
│ ┌─────────┐ ┌─────────┐ │    │ ┌─────────┐ ┌─────────┐ │
│ │ app     │→│ envoy   │ │ ←──┼─│ envoy   │→│ app     │ │
│ │ (Go)    │←│ sidecar │ │ ───┼─│ sidecar │←│ (Go)    │ │
│ └─────────┘ └────┬────┘ │    │ └────┬────┘ └─────────┘ │
└──────────────────┼──────┘    └──────┼──────────────────┘
                   │                  │
              ┌────┴──────────────────┴────┐
              │ SPIRE Workload API socket  │
              │ (mounted from node DaemonSet)
              └────────────────────────────┘

Each Envoy fetches its own SVID via the SPIRE Workload API. SVIDs rotate every ~hour. Envoy reloads them transparently without dropping connections.

The Envoy config

The interesting bits in the Envoy bootstrap:

clusters:
- name: spire_agent
  type: STATIC
  load_assignment:
    cluster_name: spire_agent
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            pipe:
              path: /run/spire/sockets/agent.sock
  http2_protocol_options: {}

# Per-listener mTLS using a SPIFFE-aware transport socket
listeners:
- 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
        common_tls_context:
          tls_certificate_sds_secret_configs:
          - name: "spiffe://genie.example/ns/prod/sa/kyc-orchestrator"
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                - envoy_grpc: { cluster_name: spire_agent }

SDS (Secret Discovery Service) is the gRPC API Envoy uses to ask SPIRE for the current cert. Envoy refreshes automatically.

What the application doesn’t have to do

The Go application speaks plaintext to localhost on a sidecar-controlled port. The sidecar does the rest.

What it still has to do

The combination is the right shape: SPIFFE for identity, your application policy stack for authorisation, audit log for after-the-fact.

For Genie’s deployment plan, this is the target end-state for the agent-to-MCP-server traffic — mTLS via Envoy + SPIRE rather than per-service TLS code. The roadmap item; the patterns translate cleanly once the SPIRE control plane is up.

← Back to all posts