API management on Kubernetes simplified: Discover Kong’s DB-Less Ingress Controller

Jervis Ferreira
Searce
Published in
9 min readMay 6, 2024

--

Authors: Jervis F, Nikhil YN

Hello everyone, today I shall provide a brief overview of Kong Ingress Controller for Kubernetes and some of the various plugins that are available for it. First, let us start off by seeing what Kong is.

Essentially, Kong is a light-weight and flexible cloud native API gateway. An API gateway is a reverse proxy that lets you manage, configure and route requests to your backends.

Kong is a versatile gateway that has extensive functionality. This is available in the form of modules and plugins.

There are 2 types of Kong available — OSS (Open Source) and Enterprise.

Kong OSS: is an open-source package that consists of the basic API gateway functionality and various open-source plugins. We will be exploring some of the plugins in this post.

Enterprise: the Kong API gateway with added functionality for eg. RBAC for Kubernetes and enterprise-grade plugins.

Kong Ingress Controller allows you to run Kong Gateway as a Kubernetes Ingress to handle inbound requests for a Kubernetes cluster.

To install Kong OSS, you can get started by deploying the kong Ingress controller. This allows you to run Kong as an Ingress resource. Kong takes responsibility for converting Kubernetes resources like Ingress and HTTP Route into valid Kong Gateway configuration.

Kong ingress in action

We shall be using the DB less method for deploying Kong OSS in the cluster.

In order to deploy Kong using the DB backed method, you can refer to my colleague Ziya Patel ’s post on Kong, which is available here:

Deploying KONG Ingress Controller on GKE with Cloud SQL Postgres

Kong Ingress Controller

blog.searce.com

To get started, install the Gateway API CRDs before installing Kong Ingress Controller.

$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

The result should look like this:

customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io configured
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io configured
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io configured
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io configured

Create a Gateway and GatewayClass instance to use with the aid of the YAML manifest mentioned below:
— -

apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: kong
annotations:
konghq.com/gatewayclass-unmanaged: 'true'
spec:
controllerName: konghq.com/kic-gateway-controller
- -
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: kong
spec:
gatewayClassName: kong
listeners:
- name: proxy
port: 80
protocol: HTTP

The results should look like this:

gatewayclass.gateway.networking.k8s.io/kong created
gateway.gateway.networking.k8s.io/kong created

You can now use Helm to install Kong in the cluster:

1.) Add the Kong Helm Chart:

$ helm repo add kong https://charts.konghq.com

$ helm repo update

2.)Install Kong Ingress Controller and Kong Gateway with Helm:

$ helm install kong kong/ingress -n kong — create-namespace

You can check connectivity to the Ingress controller by using the following set of commands:

$ export PROXY_IP=$(kubectl get svc — namespace kong kong-gateway-proxy -o jsonpath=’{.status.loadBalancer.ingress[0].ip}’)

$ echo $PROXY_IP

use curl to test the above:

$ curl $PROXY_IP

You should get the following result:

HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 48
X-Kong-Response-Latency: 0
Server: kong/3.0.0

{"message":"no Route matched with those values"}

We shall now deploy a sample service that will act as an upstream to proxy requests to. This is a simple service that returns information about the pod it runs in.

$ kubectl apply -f https://docs.konghq.com/assets/kubernetes-ingress-controller/examples/echo-service.yaml

this should give us the following output:

service/echo created
deployment.apps/echo created

We shall now add the routing configuration to proxy /echo requests to the backend using the following YAML:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo
annotations:
konghq.com/strip-path: 'true'
spec:
ingressClassName: kong
rules:
- http:
paths:
- path: /echo
pathType: ImplementationSpecific
backend:
service:
name: echo
port:
number: 1027

This will create the route which is a standard Ingress resource for Kong to proxy requests to.

To test it, use

$ curl -i $PROXY_IP/echo

this should provide the following response:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 170
Connection: keep-alive
Date: Sun, 31 Mar 2024, 12:30:45 GMT
X-Kong-Upstream-Latency: 2
X-Kong-Proxy-Latency: 0
Via: kong/3.6.1
X-Kong-Request-Id: 42a98d6fcacd0532ec13ab28b58bc046
Welcome, you are connected to node gke-gke-cluster-1-default-pool-c2168ec6-d0dp.
Running on Pod echo-965f7cf84-g4w2n
In namespace default.
With IP address 10.8.1.9.

This response shows us that Kong is able to correctly proxy traffic to the echo app that we deployed in Kubernetes.

PLUGINS in Kong

Kong provides a set of modules written in Lua called plugins that are used to extend the functionality of Kong. There are in total 3 types of plugins — open source, enterprise, and custom. Custom plugins are developed by the Kong community and are supported and maintained by the plugin creators. These are community or third party plugins.

Plugins allow you to add new features to your application by providing additional functionality to your implementation. Plugins can be configured to run in a variety of contexts, ranging from a specific route to all upstreams and can also execute actions inside Kong before or after a request has been proxied to the upstream API, as well as any incoming responses.

Let us take a look at some of the most commonly used Kong plugins:

Rate Limiting

This plugin is used to control the rate of requests sent to an upstream service. It can be used to prevent DoS attacks, limit web scraping and other forms of overuse. Without this plugins, clients have unlimited access to your applications, which impacts availability.

Kong imposes limits with the use of this plugin by ensuring that clients are restricted in the number of requests that they can make at any given point of time. The plugin supports identifying clients as ‘consumers’ based on authentication or by the client IP address of the requests.

To use a plugin, you would create a Kubernetes resource called KongPlugin and then annotate your route (Ingress) with the konghq.com/plugins annotation.

The YAML for the KongPlugin would be as follows:

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-5-min
annotations:
kubernetes.io/ingress.class: kong
config:
minute: 5
policy: local
plugin: rate-limiting

You can now associate the plugin with the service by using the following annotation:

$ kubectl annotate service echo konghq.com/plugins=rate-limit-5-min

Alternatively, associate the rate limiting plugin to a route in kong using:

$ kubectl annotate ingress echo konghq.com/plugins=rate-limit-5-min

To test the plugin, try sending 6 requests to the echo service, by using the following command:

$ for i in `seq 6`; do curl -sv $PROXY_IP/echo 2>&1 | grep “< HTTP”; done

The final request receives an HTTP 429 error:

< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 429 Too Many Requests

This shows that the plugin is preventing the request from reaching the upstream.

Basic Authentication

One of the most important factors to consider when setting up your application is to configure authentication. Kong provides a number of services to configure authentication for your upstream services. One of them is basic authentication, which we will be looking at now.

Basic authentication uses the username and password method of authenticating requests. Let us see how this works.

We start by creating a secret to add basic-auth credential for a consumer as follows:

$ kubectl create secret generic consumer-1-basic-auth \
— from-literal=kongCredType=basic-auth \
— from-literal=username=consumer-1 \
— from-literal=password=abc@123

The result should look like this:

secret/consumer-1-basic-auth created

We can then create a KongConsumer resource:

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: consumer-1
annotations:
kubernetes.io/ingress.class: kong
username: consumer-1
credentials:
- consumer-1-basic-auth
kongconsumer.configuration.konghq.com/consumer-1 created

Let us now create the basic auth plugin by creating a KongPlugin resource with the aid of the below YAML manifest:

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: echo-basic-auth
config:
anonymous: anonymous
hide_credentials: true
plugin: basic-auth

The result would be as follows:

kongplugin.configuration.konghq.com/echo-basic-auth created

Associate the plugin with the route that was created earlier:

$ kubectl annotate ingress echo konghq.com/plugins=’echo-basic-auth’

Now, send a request using curl:

$ curl -i $PROXY_IP/echo

Since we have sent a curl request without authentication, it returns the following response:

HTTP/1.1 401 Unauthorized
Date: Sun, 31 Mar 2024 16:03:04 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Basic realm="service"
Content-Length: 81
X-Kong-Response-Latency: 1
Server: kong/3.6.1
X-Kong-Request-Id: d77aea46139cd338da390e5f71d7cd72
{
"message":"Unauthorized",
"request_id":"d77aea46139cd338da390e5f71d7cd72"

We will now attempt to send a request to the service using basic authentication method:

$ curl -i $PROXY_IP/echo -u consumer-1:abc@123

It now returns the following output:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 169
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Date: Sun, 31 Mar 2024 16:04:31 GMT
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 1
Via: kong/3.6.1
X-Kong-Request-Id: 2504331e56c64e102095467991de5b6a
Welcome, you are connected to node gke-gke-cluster-1-default-pool-c2168ec6-d0dp.
Running on Pod echo-965f7cf84-g4w2n.
In namespace default.
With IP address 10.1.10.18.

Key auth

The second type of authentication is key-auth. This plugin lets you add API key authentication to a service or a route. Consumers then add their API key either in a query string parameter, a header, or a request body to authenticate their requests. Let us now see how this works.

We begin by creating a secret first using the following YAML

apiVersion: v1
kind: Secret
metadata:
name: consumer-2-key-auth
labels:
konghq.com/credential: key-auth
stringData:
key: consumer-2-password

The result should look like this:

secret/consumer-2-key-auth created

Create a new consumer named consumer-2:

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: consumer-2
annotations:
kubernetes.io/ingress.class: kong
username: consumer-2
credentials:
- consumer-2-key-auth

The result would look somewhat like this:

kongconsumer.configuration.konghq.com/consumer-2 created

We can now go ahead and create a KongPlugin called echo-key-auth using the following YAML:

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: echo-key-auth
config:
key_names:
- apikey
anonymous: anonymous
hide_credentials: true
plugin: key-auth
kongplugin.configuration.konghq.com/echo-key-auth created

Associate the plugin with the route that you have created:

$ kubectl annotate ingress echo konghq.com/plugins=’echo-basic-auth, echo-key-auth’ — overwrite

Note: Your endpoints are now secure, but neither consumer can access the endpoint even after providing valid credentials. This is because each plugin will verify the consumer using its own authentication method. If you recollect, we had added the basic-auth plugin earlier in this post.

To allow multiple authentication methods, create an anonymous consumer which is the default user (sans any credentials) if no valid credentials are provided:

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: anonymous
annotations:
kubernetes.io/ingress.class: kong
username: anonymous

The result should be somewhat like this:

kongconsumer.configuration.konghq.com/anonymous created

All requests to the API will now succeed as the anonymous consumer is being used as a default.

To secure the API, you can add a request termination plugin to the anonymous consumer that would return the response code 401.

Create a request termination plugin:

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: anonymous-request-termination
config:
message: "Authentication required"
status_code: 401
plugin: request-termination

— —

the results should look like this:

kongplugin.configuration.konghq.com/anonymous-request-termination created

Associate the above plugin to the anonymous consumer, by adding the following annotation to the anonymous kongconsumer manifest

konghq.com/plugins: anonymous-request-termination

the results should look like this:

kongconsumer.configuration.konghq.com/anonymous configured

In order to test the configuration, try issuing an unauthenticated request:

$ curl -i $PROXY_IP/echo

It returns the following output:

HTTP/1.1 401 Unauthorized
Date: Sun, 07 Apr 2024 12:10:18 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Content-Length: 37
X-Kong-Response-Latency: 0
Server: kong/3.6.1
X-Kong-Request-Id: 2a001e40d05666793b29ff737cd7d65c

Try sending a request with invalid credentials:

$ curl -i $PROXY_IP/echo -H apikey:invalid

HTTP/1.1 401 Unauthorized
Date: Sun, 07 Apr 2024 12:12:14 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 37
X-Kong-Response-Latency: 1
Server: kong/3.6.1
X-Kong-Request-Id: ac86f24ed5862e610f5851035f017067

And now we shall try issuing a curl request with the correct creds:

$ curl -i $PROXY_IP/echo -H apikey:consumer-2-password

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 168
Connection: keep-alive
Date: Sun, 07 Apr 2024 12:13:33 GMT
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 0
Via: kong/3.6.1
X-Kong-Request-Id: ff54d34e9dbede532ddcdf53c456e91a
Welcome, you are connected to node gke-gke-cluster-1-default-pool-c2168ec6–16sm.
Running on Pod echo-965f7cf84–6wv7n.
In namespace default.
With IP address 10.1.10.5.

Conclusion

As we have already seen, Kong is a production ready API gateway that is incredibly flexible and adaptable for a wide range of use-cases. There are numerous plugins that allow you to customize the way your backends receive traffic. I would encourage you to go ahead and have a look at some of the more popular plugins over here:

Kong Docs

--

--