How to Use mTLS Plugin

In my previous post, I talked about how to use Vault and JWT Authentication plugins. In today’s post, I want to talk about Mutual TLS Plugin.

Prerequisite :

A Kong Enterprise instance running with database.

CA and Client certificate: If you don’t have existing client certificates, you can use either use mkcert or follow this tutorial and use OpenSSL to set up your private CA.

You can find plugin documentation here.

Use Example

I will be using cURL to set up everything via Admin API.

Create a service

1
2
3
4
curl -X POST http://localhost:8001/services \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"name":"mtls-service","url":"https://httpbin.org/anything"}'

Create Route

1
2
3
4
curl -X POST http://localhost:8001/services/mtls-service/routes \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"name":"mtls-route","paths":["/mtls"]}'

When we visit our route at curl 'http://localhost:8000/mtls' -i, we should get HTTP/1.1 200 OK.

Create ca-certificate object

We need to create ca certificate objects for root and intermediate certificates.

1
curl http://localhost:8001/ca_certificates -F [email protected]
1
curl http://localhost:8001/ca_certificates -F [email protected]

You should get response as below respectively

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"cert": "<PEM_CERT>",
"id": "c383d81a-bffc-4e2a-b0d3-ac56b441a07b",
"cert_digest": "3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c",
"tags": null,
"created_at": 1607347576
}

{
"cert": "<PEM_CERT>",
"id": "8796de73-3673-4c4c-bd23-2afbb00f10be",
"cert_digest": "a542bca09c5e4579c619774ae59082bce0f86d261c5a7a5a0f6217c10279ea7c",
"tags": null,
"created_at": 1607347585
}

Enable mTLS plugin

As usual, I enabled plugin globally.

1
2
3
4
5
6
curl --request POST \
--url http://localhost:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=mtls-auth \
--data config.ca_certificates=8796de73-3673-4c4c-bd23-2afbb00f10be \
--data config.ca_certificates=c383d81a-bffc-4e2a-b0d3-ac56b441a07b

If we visit our route again, we will get HTTP/1.1 401 Unauthorized

Create consumer

Create a consumer with username or custom_id the same as the CN/Email on client certificates.

1
2
3
4
curl --request POST \
--url http://localhost:8001/consumers \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data username=<CLIENT_CERT_CN_NAME>

Access API

mTLS means mutual TLS authentication so we need to access our route via https protocol now. If your tls certificate is self-signed, you need to add -k flag.

1
curl https://<PROXY_URL>:8443/mtls --key client.key --cert client.crt

You should be able to access API again with response as below. As you can see X-Consumer-Id and X-Consumer-Username was added to my request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.78.0",
"X-Amzn-Trace-Id": "Root=1-60ffa1b7-6763273d3e489c0c14b6e43f",
"X-Consumer-Id": "8715a4f8-e1bd-44f1-82d2-22724127c47c",
"X-Consumer-Username": "liyangau",
"X-Forwarded-Host": "kong.li.lan",
"X-Forwarded-Path": "/mtls",
"X-Forwarded-Prefix": "/mtls"
},
"json": null,
"method": "GET",
"origin": "192.168.186.1",
"url": "https://kong.li.lan/anything"
}

Other deployments methods

DBless deployment

Please save below content to kong.yaml and load it to your DBless deployment configuration. Please updat <CLIENT_CERT_CN_NAME> to CN or email of your client certificate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_format_version: "2.1"
_transform: true

services:
- name: mtls-service
url: https://httpbin.org/anything
routes:
- name: mtls-route
paths:
- /mtls

consumers:
- username: <CLIENT_CERT_CN_NAME>

plugins:
- name: mtls-auth
config:
ca_certificates:
- 7ccf0aed-0181-4c51-99fa-5ccc831cee8b
route: mtls-route

ca_certificates:
- id: 7ccf0aed-0181-4c51-99fa-5ccc831cee8b
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

After successfully deploying your Kong instance, you should be able to access your api.

Kubernetes Ingress Controller

Please change <CLIENT_CERT_CN_NAME> of your consumer object and put your CA root crtificate in x509 format.

Below example will deploy :

  • Echo deployment
  • Echo Service
  • Mutual TLS plugin
  • Consumer mtls-user
  • Ingress rule to use Mutual TLS Authentication plugin
  • CA certificate object

Please save below to mtls.yaml and use kubectl apply -f mtls.yaml to apply it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-deployment
spec:
replicas: 1
selector:
matchLabels:
app: echo-pod
template:
metadata:
labels:
app: echo-pod
spec:
containers:
- name: echoheaders
image: k8s.gcr.io/echoserver:1.10
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
spec:
selector:
app: echo-pod
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: proxy-to-echo
annotations:
kubernetes.io/ingress.class: "kong"
konghq.com/plugins: mtls-auth
konghq.com/strip-path: "true"
spec:
rules:
- http:
paths:
- path: /mtls
backend:
serviceName: echo-service
servicePort: 80
---
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: mtls-user
annotations:
kubernetes.io/ingress.class: "kong"
username: <CLIENT_CERT_CN_NAME>
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: mtls-auth
config:
ca_certificates:
- 7ccf0aed-0181-4c51-99fa-5ccc831cee8b
plugin: mtls-auth
---
apiVersion: v1
kind: Secret
metadata:
name: my-ca-cert
annotations:
kubernetes.io/ingress.class: kong
labels:
konghq.com/ca-cert: 'true'
type: Opaque
stringData:
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
id: 7ccf0aed-0181-4c51-99fa-5ccc831cee8b

Extended information

  • CA Certificate object must include root certificate
  • Multiple CA is supported. You just need to add ca_certificate object id to mTLS plugin under config.ca_certificates.

Consumer matching

This plugin can use CN or DN from client certificate for matching. You can change it by setting config.authenticated_group_by when applying Mutual TLS plugin.

Class 1 S/MIME certificate only has email address and Class 3 S/MIME has both email and Common Name for matching. This means you are not limited to use Email for your consumers when you are using Class 3 S/MIME certificate.

Auto Matching

Users can choose to match username AND/OR custom_id of a consumer against the matching criterial from client certificate. As long as there is 1 match, consumer is authenticated and allowed to use API.

The matching priorities are Email @username > Email @custom_id > Common Name @username > Common Name @custom_id

Users can use config.consumer_by to set what to match.

Manual Matching

If user set config.consumer_by empty [], Kong must use manual mapping, which mean if subject_name is not set on a consumer, authentication fail immediately.

Manual mapping has two fields to use:

  • subject_name
  • ca_certificate.id (optional)

These two fields can only be added via Admin API and are not visible on Kong manager at the moment.

1
2
3
4
curl --request POST \
--url http://localhost:8001/consumers/<CONSUMER_USERNAME>/mtls-auth \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data subject_name=<VALUE_FOR_MATCHING>

User can set subect_name to either an email or common name. Email and subject name has the highest priority, as long as there is a user with subject_name match email, it will match even when consumer_by is set to something else.

Use Mutual TLS between two Kong nodes

You can also to set up mutual TLS between your servers easily with at least two Kong nodes. You can find the official doc here.

Here are the steps:

  1. Mount client certificates on Reverse Proxy node.

    1
    2
    volumes:
    - /certs/client:/clien
  2. Add below environment variables to Reverse Proxy instance

    1
    2
    3
    KONG_CLIENT_SSL="on"
    KONG_CLIENT_SSL_CERT="/client/client.crt"
    KONG_CLIENT_SSL_CERT_KEY="/client/client.key"
  3. Enable mTLS plugin on API Gateway node, you can enable the plugin globally or on a specific route.

  4. Add services on Reverse Proxy node pointing to API Gateway route.

Traffic between these two Kong nodes is now secured with mTLS.

Thanks for reading, see you next time.