In previous posts, I wrote about Azure API Management in combination with APIs hosted on Kubernetes:
- API Management with private APIs: requires API Management with virtual network integration because the APIs are reachable via an internal ingress on the Azure virtual network; use the premium tier 💰💰💰
- API Management with public APIs: does not require virtual network integration but APIs need to restrict access to the public IP address of the API Management instance; you can use the other less expensive tiers 🎉🎉🎉
Instead of using API Management, there are many other solutions. One of those solutions is Kong 🐵. In this post, we will take a look at Kong Ingress Controller, which can be configured via Kubernetes API objects such as ingresses and custom resource definitions defined by Kong. We will do the following:
- Install Kong via Helm
- Create an Ingress resource to access a dummy (and dumb 😊) user management API via http://hostname/users. The back-end API uses http://hostname/api/getusers so we will need to translate the path
- Create a KongIngress custom resource to configure the back-end (like only allowing GET and setting the target path to /api/getusers)
- Use a rate limiting plugin and associate it with the Ingress
- Require key authentication on the Ingress, which also requires a KongConsumer and a KongCredential resource
For a video version, head over to Youtube. I recommend 1,5x speed! 💤💤💤
The installation can be performed with Helm. The extra LoadBalancer parameters expose the proxy and admin API via a public IP address. I used Azure Kubernetes Service (AKS).
helm install stable/kong --name kong --set ingressController.enabled=true --set admin.type=LoadBalancer --set proxy.type=LoadBalancer
The above command installs Kong in the default namespace. List the services in that namespace with kubectl get svc and note the external IP of the kong-kong-proxy service. I associated that IP with a wildcard DNS entry like *.kong.yourdomain.com. That allows me to create an ingress for http://user.kong.yourdomain.com.
Note that you should not make the admin API publicly available via a load balancer. Just remove –set admin.type=LoadBalancer to revert to the default NodePort or set admin.type=ClusterIP.
The Helm chart will automatically install a PostgreSQL instance via a StatefulSet. The instance will have an 8GB disk attached. Use kubectl get pv to check that. You can use an external PostgreSQL instance or Cassandra (even Cosmos DB with the Cassandra API). I would highly recommend to use external state. There is also an option to not use a database but I did not try that.
Install the dummy user service
Use the deployment from the previous post, which deploys two pods with a container based on gbaeke/ingfunc. It contains the dummy API which is actually an Azure Function container running the Kestrel web server.
Create the Ingress object
The Ingress definition below, allows us to connect to the back-end user service using http://user.kong.baeke.info/users:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: func namespace: default annotations: kubernetes.io/ingress.class: kong plugins.konghq.com: http-ratelimit, http-auth spec: rules: - host: user.kong.baeke.info http: paths: - path: /users backend: serviceName: func servicePort: 80
The ingress.class annotation ensures that Kong picks up this Ingress definition because I also had Traefik installed, which is another Ingress Controller. The plugins.konghq.com annotation refers to two plugins:
- rate limiting: we will define this later to limit requests to 1 request/second
- key auth: we will define this later to require the consumer to specify a previously defined API key
Go ahead and save the above file and apply it with kubectl apply -f filename.yaml. In subsequent steps, do the same for the other YAML definitions. All resources will be deployed in the default namespace.
Kong-specific ingress properties
The KongIngress custom resource definition can be used to specify additional Kong-specific properties on the Ingress:
apiVersion: configuration.konghq.com/v1 kind: KongIngress metadata: name: func proxy: protocol: http path: "/api/getusers" connect_timeout: 10000 retries: 10 read_timeout: 10000 write_timeout: 10000 route: methods: - GET regex_priority: 0 strip_path: true preserve_host: true protocols: - http
The name of the KongIngress resource is func, which is the same name as the Ingress. This associates the KongIngress resource with the Ingress resource automatically. Note that we restricted the methods to GET and that we specify the path to the back-end API as /api/getusers. You also need strip_path set to true to make this work (strips the original path from the request).
To configure rate limiting, a typical capability of an API management solution, use the definition below:
apiVersion: configuration.konghq.com/v1 kind: KongPlugin metadata: name: http-ratelimit namespace: default config: second: 1 plugin: rate-limiting
This is a custom resource definition of kind (type) KongPlugin. Via the plugin property we specify the rate-limiting plugin and set it to one request per second. Note that we call this resource http-ratelimit and that we use this name in the annotation of the Ingress specification. That associates the plugin with that specific Ingress resource.
Require an API key
To require an API key, first create a consumer with a KongConsumer object:
apiVersion: configuration.konghq.com/v1 kind: KongConsumer metadata: name: top username: topuser
Next, create a credential and associate it with the consumer:
apiVersion: configuration.konghq.com/v1 kind: KongCredential metadata: name: topcred consumerRef: top type: key-auth config: key: yourverysecretkeyhere
We need a consumer and a key because the next steps will require a key when we call the API. To do just that, define a key-auth plugin:
apiVersion: configuration.konghq.com/v1 kind: KongPlugin metadata: name: http-auth namespace: default plugin: key-auth
The above plugin is associated with the Ingress using its name (http-auth) in the Ingress annotations.
Testing the API
Let’s try to call the API without a key:
Let’s send a key with the request via a parameter (via a header is also possible):
Note I used the httpie tool (apt install httpie) for nicer formatting!
If you want to try the rate limiting features, use this on the bash prompt:
while true; do http http://user.kong.baeke.info/users?apikey=KEY; done
Once in a while, you should see:
If you want to check the configuration, navigate to https://exposed-admin-IP:8444:
A bit further down the output of the admin API, the enabled plug-ins should be listed:
In this post, we looked at the basics of Kong Ingress Controller and a few of its options to translate the path, limit the rate of requests and key authentication. We did not touch on other stuff like SSL, the Enterprise version and many of the other plugins. Hopefully though, this is just enough to get you started with the open source version on Kubernetes. Take a look a the Kong documentation for more in depth information!