Skip to main content

Version: 3.10.x

a7-recipe-mtls

Overview

Mutual TLS (mTLS) ensures both the client and server verify each other's identity via TLS certificates. Standard TLS only verifies the server; mTLS adds client certificate verification.

With API7 Enterprise Edition (API7 EE) and the a7 CLI, you can configure:

  1. Client → API7 EE mTLS: Require clients to present valid certificates.
  2. API7 EE → Upstream mTLS: Present client certificates when connecting to backends.
  3. End-to-end mTLS: Both directions simultaneously.

When to Use

  • Zero-trust networking between services.
  • Secure service-to-service communication in microservices.
  • Compliance requirements mandating mutual authentication.
  • Replace or supplement API key authentication with certificate-based auth.
  • Internal APIs that should only be accessible by authorized services.

Prerequisites

  • API7 EE Control Plane and at least one Gateway Group.
  • a7 CLI configured with a valid token and server address.
  • Certificates and private keys in PEM format.

Part 1: Client → API7 EE mTLS

Require clients to present a valid TLS certificate when connecting to API7 EE.

1. Create SSL resource with CA for client verification

a7 ssl create --gateway-group default -f - <<'EOF'
{
"id": "mtls-domain",
"cert": "<SERVER_CERTIFICATE_PEM>",
"key": "<SERVER_PRIVATE_KEY_PEM>",
"snis": ["api.example.com"],
"client": {
"ca": "<CA_CERTIFICATE_PEM>"
}
}
EOF

Fields:

  • cert / key: Server certificate and private key (presented to clients).
  • snis: Server Name Indications — domain names this certificate covers.
  • client.ca: CA certificate used to verify client certificates.
  • client.depth: (optional) Maximum certificate chain depth for verification.

2. Create a service for the protected backend

a7 service create --gateway-group default -f - <<'EOF'
{
"id": "secure-api-service",
"name": "secure-api-service",
"upstream": {
"type": "roundrobin",
"nodes": [
{
"host": "backend",
"port": 8080,
"weight": 1
}
]
}
}
EOF

3. Create a route on the protected domain

a7 route create --gateway-group default -f - <<'EOF'
{
"id": "secure-api",
"paths": ["/api/*"],
"host": "api.example.com",
"service_id": "secure-api-service"
}
EOF

4. Test with client certificate

# With valid client cert — succeeds
curl --cert client.crt --key client.key --cacert ca.crt \
https://api.example.com:9443/api/health

# Without client cert — fails with SSL handshake error
curl --cacert ca.crt https://api.example.com:9443/api/health

Part 2: API7 EE → Upstream mTLS

Configure API7 EE to present a client certificate when connecting to backends.

1. Prepare upstream client certificates

Create the upstream client SSL certificate and CA certificate in API7 EE first, then note their IDs/names. The recommended API7 Enterprise workflow is:

  1. Add an SSL Certificate using the certificate that API7 EE should present to the upstream.
  2. Add a CA Certificate used to verify the upstream server certificate.
  3. Select both certificates in the published service's upstream connection configuration.

If you use a7 ssl create to upload the client SSL certificate, keep the PEM material in a local file that is not committed, and pass the file with -f.

2. Create service with HTTPS upstream

a7 service create --gateway-group default -f - <<'EOF'
{
"id": "mtls-backend-service",
"name": "mtls-backend-service",
"upstream": {
"type": "roundrobin",
"scheme": "https",
"nodes": [
{
"host": "secure-backend",
"port": 443,
"weight": 1
}
],
"tls": {
"client_cert_id": "<UPSTREAM_CLIENT_CERTIFICATE_ID>"
}
}
}
EOF

Fields:

  • upstream.scheme: Must be "https" for TLS connections to upstream.
  • upstream.nodes: Backend nodes as [{ "host": "...", "port": 443, "weight": 1 }].
  • upstream.tls.client_cert_id: Client certificate object API7 EE presents to the upstream.
  • upstream.pass_host: Set to "pass" (default) or "rewrite" if upstream expects a specific Host header.

Configure the upstream CA certificate in the published service's upstream connection configuration if API7 EE should verify the upstream server certificate.

3. Create route using this service

a7 route create --gateway-group default -f - <<'EOF'
{
"id": "api",
"paths": ["/api/*"],
"service_id": "mtls-backend-service"
}
EOF

Part 3: End-to-End mTLS

Combine both: clients verify themselves to API7 EE, and API7 EE verifies itself to the upstream.

1. SSL for client → API7 EE mTLS

a7 ssl create --gateway-group default -f - <<'EOF'
{
"id": "frontend-mtls",
"cert": "<API7_SERVER_CERT>",
"key": "<API7_SERVER_KEY>",
"snis": ["api.example.com"],
"client": {
"ca": "<CLIENT_CA_CERT>"
}
}
EOF

2. Service for API7 EE → backend mTLS

a7 service create --gateway-group default -f - <<'EOF'
{
"id": "secure-backend-service",
"name": "secure-backend-service",
"upstream": {
"type": "roundrobin",
"scheme": "https",
"nodes": [
{
"host": "internal-service",
"port": 443,
"weight": 1
}
],
"tls": {
"client_cert_id": "<UPSTREAM_CLIENT_CERTIFICATE_ID>"
}
}
}
EOF

3. Route connecting both

a7 route create --gateway-group default -f - <<'EOF'
{
"id": "e2e-mtls-api",
"paths": ["/api/*"],
"host": "api.example.com",
"service_id": "secure-backend-service"
}
EOF

Common Patterns

Multiple domains with different CAs

# Domain A: internal services
a7 ssl create --gateway-group default -f - <<'EOF'
{
"id": "internal-mtls",
"cert": "<INTERNAL_CERT>",
"key": "<INTERNAL_KEY>",
"snis": ["internal.example.com"],
"client": {
"ca": "<INTERNAL_CA>"
}
}
EOF

# Domain B: partner services
a7 ssl create --gateway-group default -f - <<'EOF'
{
"id": "partner-mtls",
"cert": "<PARTNER_CERT>",
"key": "<PARTNER_KEY>",
"snis": ["partner.example.com"],
"client": {
"ca": "<PARTNER_CA>"
}
}
EOF

Using Secrets for certificate management

API7 EE supports certificate storage in external secret managers. Configure secrets via Dashboard or a7 CLI first.

# Create a secret reference
a7 secret create --gateway-group default -f - <<'EOF'
{
"id": "vault/mtls-certs",
"uri": "https://vault.example.com/v1/secret/data/mtls"
}
EOF

Config Sync Example

version: "1"
gateway_group: default
ssls:
- id: api-mtls
cert: |
-----BEGIN CERTIFICATE-----
<server certificate>
-----END CERTIFICATE-----
key: |
-----BEGIN RSA PRIVATE KEY-----
<server private key>
-----END RSA PRIVATE KEY-----
snis:
- api.example.com
client:
ca: |
-----BEGIN CERTIFICATE-----
<CA certificate for client verification>
-----END CERTIFICATE-----
services:
- id: secure-backend-service
name: secure-backend-service
upstream:
type: roundrobin
scheme: https
nodes:
- host: backend
port: 443
weight: 1
tls:
client_cert_id: upstream-client-cert
routes:
- id: mtls-api
paths:
- /api/*
host: api.example.com
service_id: secure-backend-service

Troubleshooting

SymptomCauseFix
SSL handshake failure (client side)Client cert not signed by the CA in client.caVerify CA chain; check that client cert is signed by the correct CA
"no required SSL certificate"Client didn't send a certificateConfigure client to present cert (--cert in curl)
502 to upstreamUpstream rejects API7 EE's client certVerify upstream.tls.client_cert_id points to a certificate signed by the upstream's trusted CA
Certificate expiredTLS cert past validity dateRotate certificate with a7 ssl update
SNI mismatchDomain doesn't match snis listAdd the domain to the snis array
Command failed with 401Invalid tokenRefresh your token using a7 context create
Service not foundDifferent gateway groupEnsure --gateway-group matches where resources were created

This page is generated from a7-recipe-mtls/SKILL.md in the api7/a7 repository. Browse all skills on the AI Agent Skills page.

API7.ai Logo

The digital world is connected by APIs,
API7.ai exists to make APIs more efficient, reliable, and secure.

Sign up for API7 newsletter

Product

API7 Gateway

SOC2 Type IIISO 27001HIPAAGDPRRed Herring

Copyright © APISEVEN PTE. LTD 2019 – 2026. Apache, Apache APISIX, APISIX, and associated open source project names are trademarks of the Apache Software Foundation