Frontend mTLS for AI Services¶
The current active mTLS path can verify client certificates on the frontend of
a FullProxy service. Configure it through the mtls_frontend object nested in
serviceArguments; there is no standalone /config/mtls endpoint.
Current implementation boundary
The OpenAPI model also declares backend-mTLS and inline certificate-data
fields. The current data-plane wiring applies only the frontend file-path
fields documented below. Do not depend on mtls_backend,
client_ca_cert_data, client_cert_data, or client_key_data for
enforcement. API acceptance or read-back does not prove those fields are
active.
No runnable AI-specific CICD scenario currently qualifies this path. Validate the exact immutable build with a real TLS client and backend before production.
Request flow¶
flowchart LR
C["Client certificate"] -->|"TLS handshake"| G["Gateway FullProxy<br/>frontend verification"]
G -->|"security 1: HTTP"| H["HTTP backend"]
G -->|"security 2: separate TLS connection"| T["TLS backend"]
style G fill:#e8f5e9,stroke:#43a047
style H fill:#fff3e0,stroke:#f57c00
style T fill:#fff3e0,stroke:#f57c00
Frontend mTLS requires mode: 4 and security: 1 or security: 2. Security
mode 1 terminates frontend TLS and uses HTTP to the backend; mode 2
re-originates a separate backend TLS connection. The frontend certificate
check does not prove backend-server identity under mode 2, because the
declared backend verification configuration is not currently wired.
Active frontend fields¶
| Field | Default | Current behavior |
|---|---|---|
client_cert_mode |
disabled |
optional verifies a presented certificate; required rejects a client without a valid certificate |
client_ca_path |
unset | Absolute path to the PEM CA bundle used for client-certificate verification |
require_client_cn |
false |
Enables the additional common-name pattern check |
client_cn_pattern |
unset | Pattern checked when require_client_cn is true |
client_crl_path |
unset | Path to an operator-supplied PEM CRL used for leaf-certificate revocation checks |
Mount certificate files read-only, grant access only to the Gateway process, and rotate them through the organization's secret-management procedure. Do not send PEM or private-key material in REST payloads.
Configure a staging service¶
Set GATEWAY_API to the protected management origin and obtain
GATEWAY_TOKEN through the approved identity workflow:
curl --fail-with-body --silent --show-error \
--request POST \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $GATEWAY_TOKEN" \
--data '{
"serviceArguments": {
"externalIP": "192.0.2.10",
"port": 443,
"protocol": "tcp",
"mode": 4,
"security": 1,
"mtls_frontend": {
"client_cert_mode": "required",
"client_ca_path": "/run/secrets/loxilb/client-ca.crt",
"require_client_cn": true,
"client_cn_pattern": "client.example.test",
"client_crl_path": "/run/secrets/loxilb/client.crl"
}
},
"endpoints": [
{"endpointIP": "198.51.100.20", "targetPort": 8080, "weight": 1}
]
}' \
"$GATEWAY_API/netlox/v1/config/loadbalancer"
The addresses are RFC 5737 documentation ranges; replace them with staging
addresses. Use security: 1 here so the recipe tests only the currently wired
frontend control and does not imply backend certificate verification.
Verify enforcement¶
Read-back confirms the control-plane object, not the TLS behavior:
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $GATEWAY_TOKEN" \
"$GATEWAY_API/netlox/v1/config/loadbalancer/all" | jq .
Then perform both handshake tests against the staging VIP:
# Expected to succeed with a valid, non-revoked certificate and matching CN.
curl --fail-with-body --silent --show-error \
--cert /run/secrets/client/client.crt \
--key /run/secrets/client/client.key \
--cacert /run/secrets/client/vip-ca.crt \
https://192.0.2.10/v1/models
# Expected to fail during TLS negotiation because no client certificate is sent.
curl --fail-with-body --silent --show-error \
--cacert /run/secrets/client/vip-ca.crt \
https://192.0.2.10/v1/models
Also test an untrusted issuer, expired certificate, mismatched CN, and revoked leaf certificate. Capture only sanitized outcomes; never record private keys or unredacted certificates in public evidence.
Troubleshooting¶
| Symptom | Check |
|---|---|
| Frontend settings appear in read-back but are not enforced | Confirm the build includes mTLS, mode is 4, security is 1 or 2, and the mounted paths exist inside the container |
| Every client is rejected | Verify the CA chain, client intermediates, file permissions, validity period, and clock |
| CN check rejects a valid certificate | Confirm require_client_cn and the exact certificate CN/pattern; SAN matching is not a substitute for this specific check |
| Revoked client is admitted | Confirm client_crl_path is readable and contains a current PEM CRL covering the leaf certificate |
| Backend certificate is not verified | Expected current limitation; mtls_backend is declared but not wired into the active data path |
Release qualification boundary¶
Production qualification requires frontend positive/negative handshake tests, certificate rotation, CRL refresh, restart, resource-load, and rollback tests on the exact image. Backend mTLS and inline certificate data remain documentation/API-contract surfaces until implementation and end-to-end tests prove enforcement.