Multi-tenancy authentication through Kong API Gateway
Join the DZone community and get the full member experience.
Join For FreeThe API Gateway pattern implements a service that’s the entry point into the microservices based application, from external API clients or consumers.
It is responsible for request routing, API composition, and other edge functions such as authentication.
When working with a microservices architecture, either on a greenfield project or during migration from monolith, a best practice is to start addressing cross-cutting concerns. Authentication is such a concern and in this article we’ll cover the Authentication of a multi-tenancy application.
There are numerous technologies we can use to implement an API Gateway pattern, including off-the-shelf API Gateway products like Kong.
When it receives a request, Kong searches within a routing map that specifies which upstream service to route the request to. This function is identical to the reverse proxy feature provided by web servers such as NGINX. Kong is based on the NGINX HTTP server, and let us configure flexible routing rules that use the HTTP method, headers and path to select the backend/upstream service and it also provides a bundle of plugins that implement edge functions such as authentication: acl and key auth.
Let’t consider the following prerequisites and steps to implement api key based authentication:
- myService - a microservice that should be private and exposes an endpoint http://myServicePrivateHost:80/api/v1/myServicePath
- two tenants tenant1.mydomain.com and tenant2.mydomain.com for which we want to authenticate the requests
- Kong running on http://localhost:8000 for proxying and http://localhost:8001 for Admin API
1. Create a service object, a representation of the upstream microservice
Request:
x
curl -X POST \
http://localhost:8001/services \
-H 'Content-Type: application/json' \
-d '{
"host": "myServicePrivateHost",
"name": "myService"
}'
Response:
x
{
"host": "myServicePrivateHost",
"created_at": 1577460734,
"connect_timeout": 60000,
"id": "110a66e0-ab86-495d-9fc7-1ade0eea6f41",
"protocol": "http",
"name": "myService",
"read_timeout": 60000,
"port": 80,
"path": null,
"updated_at": 1577460734,
"retries": 5,
"write_timeout": 60000,
"tags": null,
"client_certificate": null
}
2. Create a route object for each tenant
- service id - is extracted from step 1 response
- hosts - field containing the actual domain, tenant specific
- route name - must be unique and in this case it is prefixed with the tenant name
Request:
x
curl -X POST \
http://localhost:8001/routes \
-H 'Content-Type: application/json' \
-d '{
"protocols": [
"http",
"https"
],
"service": {
"id": "110a66e0-ab86-495d-9fc7-1ade0eea6f41"
},
"name": "tenant1.routeForMyService",
"preserve_host": false,
"regex_priority": 0,
"strip_path": false,
"paths": [
"/api/v1/myServicePath"
],
"hosts": [
"tenant1.mydomain.com"
],
"methods": [
"GET"
]
}'
When a request comes in, Kong will make use of the host header, path and HTTP method in order to select the route. If no route will be found the response will be:
xxxxxxxxxx
{
"message": "no Route matched with those values"
}
At this point we have two unauthenticated routes and we'll create a consumer for each of them. Those consumers can be client applications or users and they can have multiple api keys.
3. Create consumer object for each tenant
Request:
xxxxxxxxxx
curl --request POST \
--url http://localhost:8001/consumers \
--header 'Content-Type: application/json' \
--data '{"username":"someConsumerForTenant1"}'
Now we should group the consumers for a specific tenant (there are also cases where we want to group by a tenant's application as well) and the specified group name will be whitelisted for consuming the api.
4. Create a group for each consumer
Request:
xxxxxxxxxx
curl --request POST \
--url http://localhost:8001/consumers/someConsumerForTenant1/acls \
--header 'Content-Type: application/json' \
--data '{"group":"tenant1Group"}'
5. Applying key-auth plugin for the tenant specific route
Request:
xxxxxxxxxx
curl --request POST \
--url http://localhost:8001/routes/tenant1.routeForMyService/plugins \
--header 'Content-Type: application/json' \
--data '{"name":"key-auth"}'
6. Applying acl plugin for the tenant specific route
Request:
x
curl --request POST \
--url http://localhost:8001/routes/tenant1.routeForMyService/plugins \
--header 'Content-Type: application/json' \
--data '{"name":"acl", "config":{"whitelist":["tenant1Group"]}}'
7. Generate an api key for tenant1:
Request:
x
curl --request POST \
--url http://localhost:8001/consumers/someConsumerForTenant1/key-auth \
--header 'Content-Type: application/json' \
--data '{}'
Response:
xxxxxxxxxx
{
"key": "6DlgBFKA2a0uD9CHVgqQvsZfWsePC0zu",
"created_at": 1577462044,
"consumer": {
"id": "47dd2c7b-8094-4a9f-96f0-15120ddb8a79"
},
"id": "40c69ad1-37f8-4551-ab66-eb3609e78f26"
}
8. Access /api/v1/myServicePath for tenant1 without an apikey header:
Request:
x
curl --request GET \
--url http://localhost:8000/api/v1/myServicePath \
--header 'host: tenant1.mydomain.com'
Response:
xxxxxxxxxx
{
"message": "No API key found in request"
}
9. Access /api/v1/myServicePath for tenant1 with the corresponding api key generated in step 7:
Request:
x
curl --request GET \
--url http://localhost:8000/api/v1/myServicePath \
--header 'apikey: 6DlgBFKA2a0uD9CHVgqQvsZfWsePC0zu' \
--header 'host: tenant1.mydomain.com'
Response:
xxxxxxxxxx
{
"message": "name resolution failed"
}
Note: The response depends on the actual microservice deployed locally. If it's up and running it will return the proper response. In our case the service doesn't exist so it will return 503 service temporary unavailable and the above response.
10. Access /api/v1/myServicePath for tenant2 with the tenant1's api key:
Response:
xxxxxxxxxx
{
"message": "You cannot consume this service"
}
These steps can be further automated by creating a wrapper over Kong Admin API. It will be in fact a microservice other services (e.g an IAM - Identity and access management - microservice responsible with authentication and authorization) will communicate with, as a means to generate consumers and api keys for those consumers.
In this way we can leverage Kong API Gateway for centralizing the authentication to our multi-tenancy application.
Published at DZone with permission of Cezar Romaniuc. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments