Kong in Kubernetes- locking down api endpoints via auth and consumer
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 ;)
- Create the secret
- Create the consumer and ‘attach’ the secret
- Create the auth-key plugin
- 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.