How to Use Kong ACME Plugin

One of my viewers requested me to talk about Kong’s ACME plugin. I did not cover this plugin because function-wise it is quite limited. If you are running Kong on Kubernetes, I suggest you to use cert-manager instead to handle TLS certificate. You can find my related post here.

As there is no other choice if you are using Kong on VM or docker, I decide to write this post to help you use this plugin.

Before we start, we need to understand where certificates are stored. Kong supports storing certificate in a few places. Although you can store your certificate in memory, you really should store your certificate in a database. If you are using a DB-backed instance, you can choose to store your certificate in kong database

Preparation

Prerequisite :

Kong running with DB or Redis.

You can find plugin documentation here.

First of all, you MUST include lua_ssl_trusted_certificate to start Kong. This is critical otherwise you will get below error when kong requests certificate.

1
2021/08/13 01:34:46 [error] 27#0: *10433 [kong] handler.lua:109 failed to update certificate: acme directory request failed: 20: unable to get local issuer certificate, context: ngx.timer, client: 218.215.244.189, server: 0.0.0.0:8443

I am using kong:2.5-ubuntu image so I can use "KONG_LUA_SSL_TRUSTED_CERTIFICATE=system" to start Kong as below.

Secondly, you must expose Kong proxy at port 80. Kong ACME plugin only supports HTTP-01 validation which requires this port to be opened.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker run --detach --name kong \
--network kong-net \
-p "80:8000" \
-p "443:8443" \
-p "8001:8001" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_PROXY_LISTEN=0.0.0.0:8000, 0.0.0.0:8443 http2 ssl" \
-e "KONG_LUA_SSL_TRUSTED_CERTIFICATE=system" \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-db" \
-e "KONG_PG_DATABASE=kong" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kong" \
kong:2.5-ubuntu

As you can see I am using a postgres database in my deployment and expose port 80 and 443.

If you want to know more about kong deployment on docker, please check this post.

Thirdly, you should know what type of private key to use and which CA you want to request your certificate from. To know more about using different type of certificate key and CA, please check this post.

Usage Example

Create a service

You don’t need a realy service or route for creating certificate requests. We will create a dummy service and route for that. 65535 is used here just to avoid conflicts with existing services.

1
2
3
4
5
curl --request POST \
--url http://localhost:8001/services \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=dummy-service \
--data url=http://127.0.0.1:65535

Create Route

/.well-known/acme-challenge is the required location for HTTP-01 validation.

1
2
3
4
5
6
7
curl --request POST \
--url http://localhost:8001/services/dummy-service/routes \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=dummy-route \
--data hosts=demo.your.domain.name \
--data protocols=http \
--data paths=/.well-known/acme-challenge

Enable ACME plugin

I am using the simpliest configuration here. As you can see I use config.storage=kong which stores the certificate in database.

1
2
3
4
5
6
7
8
curl --request POST \
--url http://localhost:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=acme \
--data config.domains=demo.your.domain.name \
--data config.tos_accepted=true \
--data config.account_email=youremail@domain \
--data config.storage=kong

Optional: Enable ACME Plugin with Redis

If you are using dbless deployment, you can use redis to store your certificate. In below example, my redis is in the same network as kong and its host name is redis-demo, password is A-SUPER-STRONG-DEMO-PASSWORD.

config.domains=demo.your.domain.name can be configured as config.domains=*.your.domain.name which means the users agree ACME plugin to request certificate for <ANYTHING>.your.domain.name. This is not the common name nor SAN on the certificate created. This option is more of telling ACME plugin which domain(s) it is supposed to request certificates for. If the domain name on your certificate request is not covered here, ACME plugin won’t request certificate for it.

1
2
3
4
5
6
7
8
9
10
11
12
curl --request POST \
--url http://localhost:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=acme \
--data config.domains=demo.your.domain.name \
--data config.tos_accepted=true \
--data config.account_email=youremail@domain \
--data config.storage=redis \
--data config.storage_config.redis.auth=A-SUPER-STRONG-DEMO-PASSWORD \
--data config.storage_config.redis.port=6379 \
--data config.storage_config.redis.database=0 \
--data config.storage_config.redis.host=redis-demo

Create Certificate

Now that we have everything setup, we can request our certificate. Kong ACME plugin only supports single domain certificate which means each FQDN has its own certificate.

You can either use the proxy endpoint to trigger certificate creation

1
curl https://mydomain.com -k

or Admin API endpoint

1
curl http://localhost:8001/acme -d host=mydomain.com

If you want to do a test run before requesting the real certificate

1
curl http://localhost:8001/acme -d host=mydomain.com -d test_http_challenge_flow=true

You should get your certiifcate pretty soon if everything goes well.

That’s all I want to cover today and I hope you find it useful.

See you on the next one.