Client certificates (mTLS)
GuideOperationsSince version 0.1.0
Operations guide: configure, deploy and observe the gateway. What the capability does, product side: product guide; calling it: developer guide.
For a partner server that presents a certificate. Two ways to verify it, either or both:
| Where the certificate is verified | Configuration | When |
|---|---|---|
The edge (Envoy, an ingress) terminates mTLS and vouches for it in x-forwarded-client-cert |
server.trusted_proxies = the edge's range |
The usual case in a cluster |
| The gateway itself, on its TLS listener | server.tls (cert_file, key_file, client_ca_file) |
No edge terminating TLS |
Each certificate is declared - by its URI SAN (preferred: it survives a renewal) or by its subject - and bound to a tenant and to operations:
server:
trusted_proxies: [10.0.0.0/8] # the edge, and only it: from anyone else the header is ignored
auth:
mtls:
clients:
- { id: bank-settler, uri: "spiffe://bank.example/settler", tenant: bank, operations: [settle] }
- { id: legacy-batch, subject: "CN=batch,O=Acme", tenant: acme, operations: [getSettlement] }The gateway's own TLS listener, when there is no edge (port FLEXWAY_TLS_ADDR, :8443 by default,
next to :8080):
server:
tls:
cert_file: /etc/flexway/tls/tls.crt # reloaded when it rotates, no restart
key_file: /etc/flexway/tls/tls.key
client_ca_file: /etc/flexway/tls/clients-ca.pem # the client certificates' authority, reloaded tooThe contract declares the mutualTLS alternative:
components:
securitySchemes:
partner: { type: mutualTLS }
paths:
/settlements:
post:
operationId: settle
security: [ { partner: [] } ] # a declared certificate, nothing elseCheck:
| Call | Response |
|---|---|
| A declared certificate, a bound operation | relayed; the certificate's X-Tenant; x-forwarded-client-cert never reaches the upstream |
| A declared certificate, an operation it does not name | 403 |
| A valid but undeclared certificate, or none | 401 "requires a client certificate" |
An x-forwarded-client-cert header from anywhere but the edge |
ignored - 401 |
| A certificate from another authority, on the TLS listener | the handshake fails |
A token on an operation that accepts mutualTLS only |
401; the token is not even verified |
Reading order: a certificate verified by the listener, then the edge's
x-forwarded-client-cert, then an API key, then a token. The contract decides which mechanism satisfies which alternative: a token never stands in for a key, nor the reverse. The MCP surface accepts OAuth tokens only (Agents and MCP).server.tlsis not reloadable: adding or removing it needs a restart; its files are re-read on their own.