Kong in Kubernetes- locking down api endpoints via auth and consumer

Joshua Callis
4 min readJan 16, 2021

--

Have you looked at the Kong docs to put authentication on your API? and been a little like, what? where exactly do I start? If that’s you, then read on!

You may also be thinking, this is cool but how does it work in a nutshell?

The magic.. .

When a request is made with the apikey. Kong is smart enough to associate that key to the consumer. Any plugins defined within the consumer will be used. Then the request will go via the ingress to the service defined on port 80 (or whatever port you have set for your app) and return the response data from the endpoint.

If the apikey isn’t matched to a secret, the request won’t be passed to the service and instead return this lovely message.

For the more detailed explanation, continue ;)

  1. Create the secret
  2. Create the consumer and ‘attach’ the secret
  3. Create the auth-key plugin
  4. Add the auth-key plugin to the ingress

1. Secret

It’s possible to create the secret via a manifest, however for most use cases it’s easy enough to do it via the cmd within the kubernetes cluster. If automation is required, I would look to use the kong admin api instead.

kubectl create secret generic bobs-shop-apikey --from-literal=kongCredType=key-auth --from-literal=key=my-secret -n app

The secret will be stored within the cluster in the app namespace.

2. Consumer

You can think of a consumer as a new customer wanting to consume your api.

I’ve added the rl-by-ip-30 as an example, to showcase that you can set plugins specifically for consumers. So, if johnsons shop needed rate limiting set to 100 and bobs shop needed rate limiting set to 50, you could do so.

Kong allows you to explicitly set configurations for consumers and it’s all free! amazing, right!

apiVersion: configuration.konghq.com/v1kind: KongConsumermetadata:  name: bobs-shop-consumernamespace: app  annotations:    kubernetes.io/ingress.class: kong    konghq.com/plugins: 'rl-by-ip-50' username: bobsshopcustom_id: bobs-shop-consumer-1credentials:- bobs-shop-apikey

You’ll notice that i’ve applied the secret that was just created in the credentials within the consumer. This allows us to associate that key to bobs-shop-consumer.

In laymen's terms, the key is the consumer. I know this isn’t correct! but if you can think of it that way, I feel the whole auth/consumer link becomes easier to compartmentalise. i.e when you make a request, which you’ll see further on and the apikey is matched, it applies any configurations set within the consumer.

Bonus info: the rate limit plugin for the consumer

limit_by: consumer — is important, this allows the consumer to use a plugin, in this case the rate limit plugin.

If the same plugin is already defined within the ingress, and the consumer is used i.e the apikey, then the plugin defined within the consumer will take precedence.

apiVersion: configuration.konghq.com/v1kind: KongClusterPluginmetadata:  name: rl-by-ip-50  # requires the ingress annotation - "to help clear up issues that  we observed in environments with multiple instances of our ingress controller"  # - https://github.com/Kong/kubernetes-ingress-controller/issues/885#issuecomment-702453875annotations:  kubernetes.io/ingress.class: kongconfig:  minute: 50  limit_by: consumer  policy: localplugin: rate-limiting

3. auth-key plugin

The key_names is now required and you can list one or many keys to become the authentication key => value. i.e apikey:secret or customappkey:secret — in this example I’m just sticking to using the apikey as the ‘key’.

hide_credentials is set to true and hides the credentials within the request.

KongClusterPlugin means that the plugin is available cluster wide, i.e can be used in any namespace.

apiVersion: configuration.konghq.com/v1kind: KongClusterPluginmetadata:  name: app-auth  # requires the ingress annotation - "to help clear up issues that we observed in environments with multiple instances of our ingress controller"  # - https://github.com/Kong/kubernetes-ingress-controller/issues/885#issuecomment-702453875annotations:  kubernetes.io/ingress.class: kongconfig:  key_names:  - apikey  hide_credentials: trueplugin: key-auth

4. Ingress

This ingress is responsible for routing traffic to your service via the kong controller/load balancer and applying any plugins and routes.

apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: app-ing  namespace: appannotations:  kubernetes.io/ingress.class: kong  konghq.com/plugins: 'app-auth'spec:  rules:  - host: example.com  - http:      paths:      - path: /api/store/products      backend:       serviceName: app       servicePort: 80

You can then make a request with the apikey and access the route and return the request data.

Via the header

curl -i  http://example.com/api/store/products -H 'apikey:my-secret'

If within a browser, you can also use the get param.

curl -i  http://example.com/api/store/products?apikey:mysecret

You can also specify the apikey via the body, however I’ve found those are the only options I need/reach for in a production environment.

--

--

Joshua Callis
Joshua Callis

Written by Joshua Callis

Converted DevOps Engineer at oso.sh, Previously a Senior Software Engineer.

No responses yet