Configure mTLS Between APISIX and Upstream
Mutual TLS (mTLS) is a two-way TLS where the client and the server authenticate each other. It is typically implemented in high-security environments to prevent unauthorized access and harden security.
This guide will walk you through how to configure mTLS between APISIX and an upstream service, using NGINX as a sample upstream service.
Prerequisite(s)
- Install Docker.
- Install cURL to send requests to the service for testing.
- Install jq to safely encode PEM certificates in JSON.
- Install OpenSSL to generate the sample certificates.
- Follow the Getting Started tutorial to start a new APISIX instance in Docker or on Kubernetes.
Generate Certificates and Keys
Generate the Certificate Authority (CA) key and certificate:
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 36500 -sha256 \
-key ca.key \
-out ca.crt \
-subj "/CN=MyTestCA" \
-extensions v3_ca \
-config <(printf "[req]\ndistinguished_name=req\n[ v3_ca ]\nbasicConstraints=critical,CA:TRUE\nkeyUsage=critical,keyCertSign,cRLSign\nsubjectKeyIdentifier=hash\nauthorityKeyIdentifier=keyid:always,issuer")Generate the key and certificate signing request (CSR):
openssl genrsa -out server.key 2048
openssl req -new -sha256 \
-key server.key \
-out server.csr \
-subj "/CN=test.com"Sign the server CSR with the CA certificate to generate the server certificate:
openssl x509 -req -days 36500 -sha256 \
-in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt \
-extensions v3_req \
-extfile <(printf "[v3_req]\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth\nsubjectAltName=DNS:test.com")Generate the key and certificate signing request (CSR) for the client:
openssl genrsa -out client.key 2048
openssl req -new -sha256 \
-key client.key \
-out client.csr \
-subj "/CN=CLIENT"Sign the client CSR with the CA certificate to generate the client certificate:
openssl x509 -req -days 36500 -sha256 \
-in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt \
-extensions v3_req \
-extfile <(printf "[v3_req]\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=clientAuth")Configure Upstream Service
Start an NGINX server as a sample upstream service in the same Docker network as APISIX:
docker run -d \
--name quickstart-nginx \
--network=apisix-quickstart-net \
-p 8443:8443 \
nginxCopy CA certificate, server certificate public and private keys into NGINX:
docker cp ca.crt quickstart-nginx:/var/ca.crt
docker cp server.crt quickstart-nginx:/var/server.crt
docker cp server.key quickstart-nginx:/var/server.keyConfigure an HTTPS server listening on /hello and port 8443 in NGINX configuration file:
http {
# ...
server {
listen 8443 ssl;
server_name test.com;
ssl_certificate /var/server.crt;
ssl_certificate_key /var/server.key;
ssl_client_certificate /var/ca.crt;
ssl_verify_client on;
location /hello {
return 200 "Hello APISIX!";
}
}
}❶ server_name matches the test.com hostname used in the direct and APISIX requests.
❷ ssl_certificate and ssl_certificate_key configure the NGINX server identity.
❸ ssl_client_certificate loads the CA that NGINX trusts for client certificates.
❹ ssl_verify_client requires each client to present a trusted certificate.
Reload the NGINX server to apply the configuration changes:
docker exec quickstart-nginx nginx -s reloadTo verify that the NGINX instance is properly configured, send a request to the NGINX service's route with client certificate and key:
curl -i "https://test.com:8443/hello" \
--resolve "test.com:8443:127.0.0.1" --cacert ca.crt \
--cert client.crt --key client.keyYou should receive an HTTP/1.1 200 OK response and see the following message:
Hello APISIX!If you send a request to the NGINX service's route without any client certificate or key:
curl -i "https://test.com:8443/hello" \
--resolve "test.com:8443:127.0.0.1" --cacert ca.crtYou should receive an HTTP/1.1 400 Bad Request response.
Configure mTLS for APISIX
Encode the certificate files as JSON strings so that their line breaks are preserved in the request body:
client_cert=$(jq -Rs . < client.crt)
client_key=$(jq -Rs . < client.key)
ca_cert=$(jq -Rs . < ca.crt)Create a route that presents the client certificate and verifies the upstream server's certificate. The CA establishes trust in the server, while the client certificate and key authenticate APISIX to NGINX:
curl -i "http://127.0.0.1:9180/apisix/admin/routes/mtls-nginx" \
-X PUT -H "X-API-KEY: ${ADMIN_API_KEY}" --data-binary @- <<EOF
{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"scheme": "https",
"nodes": {
"quickstart-nginx:8443": 1
},
"pass_host": "rewrite",
"upstream_host": "test.com",
"tls": {
"client_cert": ${client_cert},
"client_key": ${client_key},
"verify": true,
"ca_certs": [${ca_cert}]
}
}
}
EOF❶ pass_host and upstream_host use test.com for the upstream Host header and TLS name while the node address selects the Docker service.
❷ client_cert and client_key authenticate APISIX to NGINX.
❸ verify enables server verification, and ca_certs trusts the sample CA. This per-upstream CA list replaces the shared trust store for this upstream.
Verify mTLS between APISIX and Upstream Service
Send a request to the route:
curl -i "http://127.0.0.1:9080/hello"You should receive an HTTP/1.1 200 OK response and see the following message:
Hello APISIX!This verifies the successful establishment of mTLS between APISIX and the upstream service.
Next Steps
You have learned how to set up mTLS between APISIX and upstream services. APISIX also supports mTLS between clients and APISIX. See Configure mTLS between Client and APISIX to learn more.