Kong and (m)TLS both downstream and upstream


November 3, 2021

What we want to cover

When it comes to security with certificates (both encryption of the connection as well as authentication) most of us ever having worked with it having made the experience everything is complicated and we try to stay away.

As everything with Kong the idea is making things as lightweight as possible this also includes the complexity of setting up and maintaining certificate based security.

The four layers of TLS

Kong TLS four layers

When thinking about certificates in an API gateway context most people immediately think about two use cases:

  1. The gateway should present a certificate to the client
  2. The gateway should authenticate a user / grant access based on a presented by the client (mTLS)

But there are two more use cases we want to cover today:

  1. The certificate presented by Kong to backend (upstream)
  2. The trust from Kong to the upstream (which certificate(s) do we allow to be presented by the backend)

#1 Certificate presented by Kong to the client

So let’s start with the certificate being served by Kong to the client (so the one you would see when opening the proxy in the browser).

The default certificate

The default certificate presented by Kong after installation is a self-signed certificate. If you want to change this default copy your desired certificate and key on the machine and then set ssl_cert and ssl_cert_key in your /etc/kong/kong.conf.

Certificate per route

But what if Kong is listening on different hostnames/FQDN - in this case we need to have different certificates being presented depending on the route.

So when looking at the route object we notice the hosts parameter which we can use to limit the hosts being listened on and also the snis parameter (SNI stands for “server name indication"). So this is what we are looking for.

But how do we define a SNI and which certificates it shall use? Well, you might already have guessed it: there is an API for that: SNI API endpoint

So now that know how to attach a SNI to a route - how do we actually add the certificate being needed for the SNI? Also here the answer is: there is an API to upload a certificate.

So the order is actually the exact opposite of how we just went through it:

  1. Upload the certificate
  2. Create a SNI and link to the uploaded certificate
  3. Create a route linking to the SNI

Let’s Encrypt

If you want to automate the whole process using Let’s Encrypt have a look at the ACME plugin which integrates Kong with Let’s Encrypt for creation and auto-updating of certificates.

#2 mTLS for clients

While all the other documentation here is true for both Kong and Kong Enterprise mTLS is an Enterprise only plugin.


The mTLS plugin has one major parameter called ca_certificates. As the name already tells us we need to specify one or multiple CAs which we will use as the trust source - only incoming certificates having been created using those CAs will be trusted.

And similar to what we wrote above we need to have those CAs being uploaded in advance using the ca_certificates API endpoint.


So we have now authenticated the incoming certificate - but how do we now make sure not every issued certificate by the CA is allowed to make the call.

For this the mTLS plugin provides two paramters:

  1. By default the parameter skip_consumer_lookup is set to false so there must be a matching consumer in Kong - if not consumer is found the call gets denied. If a consumer is found you can do all the typical consumer based steps - especially by adding the consumer to one or multiple groups by using the ACL plugin
  2. Depending on the contents of your certificate it already might contain group membership information (see parameter authenticated_group_by) - if so those extracted groups can be directly used with the ACL plugin even without consumer matching in place.

#3 Certificate presented by Kong to the upstream

Also the trust between the gateway and the backend system can be secured using certificates. This time Kong is in charge to present a certificate to the upstream to prove its identity.

The default certificate

The default certificate presented by Kong after installation to the upstream is a self-signed certificate. If you want to change this default copy your desired certificate and key to the machine, enable client_ssl and then set client_ssl_cert and client_ssl_cert_key in your /etc/kong/kong.conf.

Certificate per service

Sometimes you need to present different certificates to different upstreams so we can override the default certificate on a per-service-level.

So when looking at the service object we notice the client_certificate parameter which we can use to specify the to-be-presented certificate.

Similar to what we discussed above to the certificate into Kong see the API to upload a certificate.

Trusting the upstream’s certificate

You can limit down the trusted certificates Kong is expecting from the upstreams - change the lua_ssl_trusted_certificate