Kong API Gateway with Microservices — Part II — Handling Authentication and Authorization with Kong

Emrah T.
7 min readSep 20, 2023

--

In the previous story of this series, justification of the need for an API gateway was presented and Kong was installed on a K8S cluster. This story will showcase a simple microservice application and Kong’s role in handling authentication and authorization within this context. Please note that Kong’s functionality is not limited to these but there are more such as traffic management (e.g. rate limiting), centralized monitoring, audit etc.

Three Microservices and the Use Case

Before diving deep into Kong’s configuration, let me begin with the application that will be utilized in this story. It is a simple CRUD application which consists of three microservices developed with FastAPI framework. If you had the chance to read my previous story, a similar application was deployed. However, there is a major difference between these two code bases. In previous one, a custom gateway was written by wrapping FastAPI’s router object. Authentication and authorization logic were handled by this component which was also provisioned as a microservice. But here, there is no need to have authentication & authorization logic within microservices. Luckily, Kong will take care of this functionality in a practical manner.

Next, let’s summarize the function of each file within folders. __init__.py is the one which promotes a folder to a Python package. main.py hosts the code that implements the logic of application, path operation functions live in this file. models.py is used for defining Pydantic and SQLAlchemy models. And test.db is the binary file for sqlite local database.

File and folder structure of the application components.

Each microservice is capable of doing CRUD operations on its own sqlite database. There is no authentication or authorization logic present by default which means whoever reaches API endpoints can consume those. Our aim is to build up an environment where all authentication and authorization logic is implemented in Kong as it can be seen below.

Kong Ingress Controller placement and duties for this story.

The detailed scenario is as follows, we need to provide access to our microservices from a client. This client utilizes his own API’s to consume ours. They will have two different kinds of clients, admin and user. Admin client will be able to consume all three microservices whereas user client will only have access to “items”. Before going into the Kong part, classical K8S resources such as deployments and services were created to spin up our microservices.

K8S native resources to create microservices.

Next, we need to create ingresses for all three services. For Kong to recognize a service and consider it under its management, we need to use “kong ingressClass”.

Ingresses for all endpoints are created with the class “kong”.

Defining an ingress resource provides external access to our resources without any limitation. Please note that fastapi-kong.example.com hostname resolves to the listening IP of HAProxy load balancers in a way similar to what is defined here. If you built K8S on a public cloud, external IP of cloud provider’s load balancer must be resolved for your hostname.

Microservices are open to unlimited external access with ingresses.

Authentication & Authorization with Kong

Now, we need to define Kong-spesific CRDs to limit access to our resources. First, we need to enable JWT (JSON Web Token) plugin. Please note that it is a namespaced resource which means it must be enabled for each namespace if required. After its activation, services need to be annotated to make the plugin active for each of them. Ingresses and consumers might be annotated as well to associate a plugin. JWT plugin provides authentication such that nobody without a valid JWT can reach our endpoints. Such requests are greeted with an unfriendly “401 Not Authorized” response.

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: app-jwt
plugin: jwt
All three services must be annotated as the example above.
Request without legit JWT’s are declined after jwt plugin’s activation.

Next, consumers must be defined. There will be two of them representing the client’s admin and user. For a successful creation, they need to be seen as “Programmed: True” in kubectl output.

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: admin
annotations:
kubernetes.io/ingress.class: kong
username: admin
---
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: user
annotations:
kubernetes.io/ingress.class: kong
username: user
Admin and user consumers are created with success.

For a consumer to send legit requests to Kong, it needs to have required credentials. To do that, secrets for each of them must be created. These are native K8S secrets. Their protection by means such as Vault is out of the scope of this story but required in an enterprise environment. It is not easy to construct valid JWTs and their key by hand. We’re going to use jwt.io for this. public_key part within jwt.io must be used as a secret variable.

jwt.io platfrom where you can craft JWTs.
kubectl create secret \
generic admin-jwt \
--from-literal=kongCredType=jwt \
--from-literal=key="admin-issuer" \
--from-literal=algorithm=RS256 \
--from-literal=rsa_public_key="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
mwIDAQAB
-----END PUBLIC KEY-----"

After creating the secrets, we need to add them to consumers. Please note that arbitrary number of credentials can be added to a consumer.

kubectl patch --type json kongconsumer admin \
-p='[{
"op":"add",
"path":"/credentials",
"value":["admin-jwt"]
}]'

Now, we’re in a point that every request with a valid JWT can consume our endpoints. Postman (an API debug, test and development tool) will be used to send requests with JWTs in the header.

Sending request with admin’s JWT.
Sending request with user’s JWT

There is no difference between admin’s and user’s now. Remember, JWT only provides authentication. For authorization, we’ll be using another plugin, namely, ACL (Access Control List). We’re creating two ACL plugins for admin and anyone separately. Anyone ACL plugin will allow both admin and user consumers to have access to annotated resources.

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: admin-acl
plugin: acl
config:
allow: ['admin']
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: anyone-acl
plugin: acl
config:
allow: ['admin','user']

After that, respective ingresses of three microservices are annotated to activate ACL plugins. Remember, we’ll grant access to admin to use all three but the user’s reach must be limited to items API. Therefore, shops and users ingresses will be annotated with admin, items ingress will be annotated with anyone ACL plugin.

Three ingresses were annotated with two ACL plugins.

Next, we’re creating secrets for both consumers for ACL and add those credentials to respective consumers.

kubectl -n kong-fastapi create secret \
generic admin-acl \
--from-literal=kongCredType=acl \
--from-literal=group=admin

kubectl -n kong-fastapi create secret \
generic user-acl \
--from-literal=kongCredType=acl \
--from-literal=group=user

kubectl -n kong-fastapi patch --type json kongconsumer admin \
-p='[{
"op":"add",
"path":"/credentials/-",
"value":"admin-acl"
}]'

kubectl -n kong-fastapi patch --type json kongconsumer user \
-p='[{
"op":"add",
"path":"/credentials/-",
"value":"user-acl"
}]'

Let’s see it in Action

At this point, authentication and authorization of two consumers are done in accordance with the scenario provided above. Kong handled those two without requiring the developer to code even a single line more. Below, screencast shows sending request to all three endpoints by two users and respective responses.

Requests to all three services by admin and user clients with their JWTs and respective responses.

Conclusion & What’s next?

Please note that we granted access to consumers in microservice level. In real world, it is rarely done as such. Because, in our test scenario, an external consumer has write access to our all microservices. Limiting access with respect to request type is also possible by creating multiple ingresses for multiple paths (e.g. /api/user/get-user, /api/user/update-user) within the same microservice. Then, all you need to do is to annotate those fine-grained ingresses accordingly.

Moreover, JWT is not the only method and plugin provided by Kong to handle authentication. OAuth2.0 plugin is included in free plugins as well. An important point, it does not support DB-less installations. I’d love to implement and write a story about it in the future.

That’s it for this series. I tried to introduce API Gateways and demonstrate a scenario where Kong API Gateway is used to handle authentication and authorization. I’m leaving FastAPI microservices’ repository here for further reference. I hope it is useful and applicable to solve real life problems.

PS: If you liked the article, please support it with claps to reach more people. Cheers!

--

--

Emrah T.
Emrah T.

Written by Emrah T.

Linux, System Adm., IaC, DevSecOps, K8S and Cloud enthusiast. Love what you do or do what you love!!! www.linkedin.com/in/emrah-t-61337713b, github.com/EmrhT

Responses (2)