Skip to content

Cloud and reverse-proxy peer identity

VGI treats a load balancer or reverse proxy as an evidence adapter, not as an authorization authority. Every header adapter requires an exact list of immediate proxy addresses, a backend that cannot be reached around that proxy, and proxy configuration that replaces or strips client-supplied copies of all identity headers. The resulting assurance is configured_proxy.

These adapters produce provider spiffe and stable workload subjects only when the evidence contains exactly one canonical SPIFFE ID in an allowed trust domain. Certificate names, fingerprints, source IPs, and load-balancer names remain attributes rather than principals.

HTTP ingress profiles

nginx

Use nginx_spiffe_provider. Configure nginx to verify the client chain, pass $ssl_client_escaped_cert as X-SSL-Client-Cert, and pass $ssl_client_verify as X-SSL-Client-Verify. The adapter requires the exact value SUCCESS and validates the forwarded leaf as an X.509-SVID.

AWS Application Load Balancer

Use aws_alb_spiffe_provider only with an ALB listener in mTLS verify mode. AWS forwards the URL-encoded leaf certificate in X-Amzn-Mtls-Clientcert-Leaf; unlike nginx and the other profiles below, ALB does not provide a per-request boolean verification header in verify mode. The operator declaration that the listener is in verify mode is therefore part of the trust boundary. Passthrough mode is not accepted by this adapter.

AWS documents the two modes and their headers in Mutual authentication with TLS in Application Load Balancer.

Google Cloud Application Load Balancer

Use gcp_load_balancer_spiffe_provider. Configure frontend mTLS and map the following official custom-header variables to the adapter's default names (or configure matching names in the factory):

GCP variable Default header
client_cert_present X-Client-Cert-Present
client_cert_chain_verified X-Client-Cert-Chain-Verified
client_cert_spiffe_id X-Client-Cert-Spiffe-Id
client_cert_error X-Client-Cert-Error

The adapter requires present=true, chain_verified=true, no validation error, and one allowed canonical SPIFFE ID. Google documents these variables in Create custom headers in backend services.

Azure Application Gateway

Use azure_application_gateway_spiffe_provider only with mTLS strict mode. Configure rewrite rules that replace the default adapter headers with client_certificate and client_certificate_verification. The adapter requires verification value SUCCESS and validates the forwarded certificate as an X.509-SVID. Azure documents the variables and strict-mode behavior in Rewrite HTTP headers and URL.

Azure App Service's X-ARR-ClientCert is deliberately not treated as verified evidence: App Service forwards the certificate but requires the application to validate it. A future direct-bundle adapter may do that cryptographic work; until then, use Application Gateway strict mode or a trusted Envoy/nginx tier.

Envoy and service mesh

Use envoy_xfcc_spiffe_provider with an adjacent Envoy that validates mTLS and sets forward_client_cert_details: SANITIZE_SET in the HTTP connection manager, using text format and set_current_client_cert_details.uri: true. Envoy then removes a caller's XFCC value and creates one element for the authenticated downstream certificate. The adapter requires exactly one URI, Envoy's SHA-256 Hash, an allowed SPIFFE trust domain, and one XFCC element; append/forward chains, duplicate singleton fields, malformed quoting/escaping, and unknown fields fail closed. Envoy's HTTP connection manager reference defines these modes.

The older mtls_authenticate_xfcc callback remains application authentication and does not by itself create PeerIdentity evidence. Multi-hop meshes should have the final adjacent Envoy reset XFCC from its verified downstream mTLS connection; the worker never guesses which element in a forwarded chain is the caller.

TCP/L4 ingress

NLBs and cloud TCP load balancers provide connectivity and flow balancing, not portable end-user or workload identity. VGI can recover the asserted source and destination from trusted PROXY v2, then run a provider such as Tailscale LocalAPI against that source. An asserted IP address alone never becomes a principal. TLS passthrough can instead let the worker verify a client SVID directly; TLS termination at the load balancer needs a provider-specific authenticated evidence channel.

Serializable HTTP state tokens are safe under ordinary request-level load balancing because they are authenticated and identity-bound. Process-local sticky sessions additionally require load-balancer affinity to their owning worker (or an explicitly shared registry); identity binding is not a routing mechanism. Stateful TCP and Iroh connections are balanced only when the connection is established; all stream turns remain on that worker. Draining must preserve sticky affinity and existing connections, and new connections must target compatible replicas.

Direct TLS and SPIFFE

Python raw TCP can terminate mutual TLS in the worker and snapshot the verified client X.509-SVID for the connection:

import ssl

from vgi_rpc.rpc import peer_identity_primary, serve_tcp

tls = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tls.load_cert_chain("worker.pem", "worker.key")
tls.load_verify_locations(cafile="example-org-bundle.pem")
tls.verify_mode = ssl.CERT_REQUIRED

serve_tcp(
    server,
    "0.0.0.0",
    9400,
    threaded=True,
    tls_context=tls,
    spiffe_trust_domains=("example.org",),
    peer_authentication_policy=peer_identity_primary("spiffe"),
)

The TLS stack verifies the chain first; VGI then enforces the leaf X.509-SVID profile and allowed trust domain. The resulting evidence uses cryptographic_peer assurance and is immutable for the connection. The client tcp_connect accepts a client tls_context and can independently require a server SVID with server_spiffe_trust_domains. One setup budget covers direct TCP or SOCKS, TLS negotiation, and certificate/profile validation.

This API accepts already-issued SVID material through the platform TLS context; automatic SPIFFE Workload API rotation is still an open release gate. An application can rotate by replacing/draining listeners as its SVID source updates the context. Because the verified SVID is snapshotted at the TLS handshake, a long-lived connection can outlive the leaf certificate's NotAfter or a trust-bundle rotation. Production must cap and drain connection lifetimes at or before the declared SVID/revocation bound; Workload API hot rotation alone affects only new handshakes.