Skip to content

Instantly share code, notes, and snippets.

@bzp2010
Created September 17, 2022 06:11
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bzp2010/6ce0bf7c15c191029ed54724547195b4 to your computer and use it in GitHub Desktop.
Save bzp2010/6ce0bf7c15c191029ed54724547195b4 to your computer and use it in GitHub Desktop.
APISIX mTLS for client to APISIX

APISIX mTLS for client to APISIX

Introduction

This gist provides a complete example of client-side authentication for Apache APISIX.

It includes the process of:

  • generating a certificate
  • configuring the certificate in APISIX
  • configuring the route in APISIX
  • testing

It also demonstrates how to pass some information about the client certificate upstream, which includes serial, fingerprint, and common name.

Step

Generate certificates

Just follow these commands, it will generate certificates by OpenSSL.

# For ROOT CA
openssl genrsa -out ca.key 2048
openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CN=ROOTCA"
openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cer

# For server certificate
openssl genrsa -out server.key 2048
openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=test.com"
openssl x509 -req -days 36500 -sha256 -extensions v3_req  -CA  ca.cer -CAkey ca.key  -CAserial ca.srl  -CAcreateserial -in server.csr -out server.cer

# For client certificate
openssl genrsa -out client.key 2048
openssl req -new -sha256 -key client.key  -out client.csr -subj "/CN=CLIENT"
openssl x509 -req -days 36500 -sha256 -extensions v3_req  -CA  ca.cer -CAkey ca.key  -CAserial ca.srl  -CAcreateserial -in client.csr -out client.cer

# Convert client certificate to pkcs12 for Windows usage
openssl pkcs12 -export -clcerts -in client.cer -inkey client.key -out client.p12

Configure the certificate in APISIX

Use curl to request APISIX Admin API to setting up SSL for specify SNI. Note that line feeds in the certificate need to be replaced with \n.

curl -XPUT 'http://127.0.0.1:9180/apisix/admin/ssls/1' \
--header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "sni": "test.com",
    "cert": "<content of server.cer>",
    "key": "<content of server.key>",
    "client": {
        "ca": "<content of ca.cer>"
    }
}'

The sni field specifies the domain name (CN) of the certificate, when the client tries to handshake with APISIX via TLS, APISIX will match the SNI data in ClientHello with this field to find the corresponding server certificate for handshake.

The cert and the key fields are the public and private keys of the server certificate.

The client.ca is the CA public key of the client certification certificate, which in theory can use a different public key from the server certificate, and this example uses the same CA for the convenience of demonstration purposes.

Configure the root in APISIX

Next, let's create a route.

curl -XPUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \
--header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "uri": "/anything",
    "plugins": {
        "proxy-rewrite": {
            "headers": {
                "X-Ssl-Client-Fingerprint": "$ssl_client_fingerprint",
                "X-Ssl-Client-Serial": "$ssl_client_serial",
                "X-Ssl-Client-S-DN": "$ssl_client_s_dn"
            }
        }
    },
    "upstream": {
        "nodes": {
            "httpbin.org":1
        },
        "type":"roundrobin"
    }
}'

As you can see, we don't need to specify the hostname in the route (you can of course set it if you need to), APISIX will automatically handle the TLS handshake based on the SNI and the ssl resource created in the previous step.

In addition to this, you will see that we have set up the proxy-rewrite plugin, which will dynamically set the headers for the request, and the source of their values are Nginx variables, which you can see here, http://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables. We will see the effect of this plugin later.

Test

If you are using Windows, and try it in broswer, you will first need to install the client certificate into the system keyring. Double click on the client.p12 file to install it.

If you use curl, then you need to specify the location of the client certificate and key at request time.

Windows and broswer

Since we will be using test.com as the domain name for testing, you will have to add them to your DNS or local hosts file.

When everything is done, let's open a Chromium-like (it prefers to use system certificates keyring) browser and try to access https://test.com:9443/anything.

Firstly, you will see this, just click Advanced and Proceed to test.com (unsafe) to continue.

chrome_K9nBR8CkJn

Then, if you have just installed the certificate correctly, you will see this. You need to check the certificate and confirm it.

image

Finally, you will see these things, the API return JSON data from httpbin.org (we have just configured the upstream).

image

The response contains the three request headers we just configured via the proxy-rewrite plugin. Where we can determine from the last two images that the serial value of the client certificate was indeed received correctly upstream.

cURL

Use this command:

curl https://test.com:9443/anything -k --cert ./client.cer --key ./client.key

Then, you will recieve response:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "test.com",
    "User-Agent": "curl/7.81.0",
    "X-Amzn-Trace-Id": "Root=1-63256343-17e870ca1d8f72dc40b2c5a9",
    "X-Forwarded-Host": "test.com",
    "X-Ssl-Client-Fingerprint": "c1626ce3bca723f187d04e3757f1d000ca62d651",
    "X-Ssl-Client-S-Dn": "CN=CLIENT",
    "X-Ssl-Client-Serial": "5141CC6F5E2B4BA31746D7DBFE9BA81F069CF970"
  },
  "json": null,
  "method": "GET",
  "origin": "127.0.0.1",
  "url": "http://test.com/anything"
}

As you can see, the response body also contains the request body received upstream, which contains the correct data.

Conclusion

Apache APISIX does a good job of mTLS for clients to APISIX and sends key information from the certificate to the upstream service.

Also, implementing APISIX with mTLS for upstream services is very simple, and I will describe it later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment