{{announcement.body}}
{{announcement.title}}

Multi-tenancy authentication through Kong API Gateway

DZone 's Guide to

Multi-tenancy authentication through Kong API Gateway

In this article, we discuss how to add multi-tenancy authentication through Kong's API Gateway to better secure our microservices.

· Security Zone ·
Free Resource

The 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:

Shell




x


 
1
curl -X POST \
2
  http://localhost:8001/services \
3
  -H 'Content-Type: application/json' \
4
  -d '{
5
    "host": "myServicePrivateHost",
6
    "name": "myService"
7
}'


 Response:

Shell




x


1
{
2
    "host": "myServicePrivateHost",
3
    "created_at": 1577460734,
4
    "connect_timeout": 60000,
5
    "id": "110a66e0-ab86-495d-9fc7-1ade0eea6f41",
6
    "protocol": "http",
7
    "name": "myService",
8
    "read_timeout": 60000,
9
    "port": 80,
10
    "path": null,
11
    "updated_at": 1577460734,
12
    "retries": 5,
13
    "write_timeout": 60000,
14
    "tags": null,
15
    "client_certificate": null
16
}



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:

Shell




x


 
1
curl -X POST \
2
  http://localhost:8001/routes \
3
  -H 'Content-Type: application/json' \
4
  -d '{
5
    "protocols": [
6
        "http",
7
        "https"
8
    ],
9
    "service": {
10
        "id": "110a66e0-ab86-495d-9fc7-1ade0eea6f41"
11
    },
12
    "name": "tenant1.routeForMyService",
13
    "preserve_host": false,
14
    "regex_priority": 0,
15
    "strip_path": false,
16
    "paths": [
17
        "/api/v1/myServicePath"
18
    ],
19
    "hosts": [
20
        "tenant1.mydomain.com"
21
    ],
22
    "methods": [
23
        "GET"
24
    ]
25
}'


Note: Perform the same request from the current step and replace tenant1 with tenant2 for the purpose of creating the route for the other tenant.


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:

Shell




xxxxxxxxxx
1


 
1
{
2
    "message": "no Route matched with those values"
3
}


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:

Shell




xxxxxxxxxx
1


1
curl --request POST \
2
  --url http://localhost:8001/consumers \
3
  --header 'Content-Type: application/json' \
4
  --data '{"username":"someConsumerForTenant1"}'


Note: Perform the same request from the current step and replace tenant1 with tenant2 for the purpose of creating the consumer for the other tenant.


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:

Shell




xxxxxxxxxx
1


1
curl --request POST \
2
  --url http://localhost:8001/consumers/someConsumerForTenant1/acls \
3
  --header 'Content-Type: application/json' \
4
  --data '{"group":"tenant1Group"}'


Note: Perform the same request from the current step and replace tenant1 with tenant2 for the purpose of creating the group for the other tenant.


5. Applying key-auth plugin for the tenant specific route

Request:

Shell




xxxxxxxxxx
1


1
curl --request POST \
2
  --url http://localhost:8001/routes/tenant1.routeForMyService/plugins \
3
  --header 'Content-Type: application/json' \
4
  --data '{"name":"key-auth"}'


Note: Perform the same request from the current step and replace tenant1 with tenant2 for the purpose of applying the plugin for the other tenant route.


6. Applying acl plugin for the tenant specific route

Request:

Shell




x


 
1
curl --request POST \
2
  --url http://localhost:8001/routes/tenant1.routeForMyService/plugins \
3
  --header 'Content-Type: application/json' \
4
  --data '{"name":"acl", "config":{"whitelist":["tenant1Group"]}}'


Note: Perform the same request from the current step and replace tenant1 with tenant2 for the purpose of applying the plugin for the other tenant route. 


7. Generate an api key for tenant1:

Request:

Shell




x


1
curl --request POST \
2
  --url http://localhost:8001/consumers/someConsumerForTenant1/key-auth \
3
  --header 'Content-Type: application/json' \
4
  --data '{}'


Response:

Shell




xxxxxxxxxx
1


 
1
{
2
    "key": "6DlgBFKA2a0uD9CHVgqQvsZfWsePC0zu",
3
    "created_at": 1577462044,
4
    "consumer": {
5
        "id": "47dd2c7b-8094-4a9f-96f0-15120ddb8a79"
6
    },
7
    "id": "40c69ad1-37f8-4551-ab66-eb3609e78f26"
8
}



8. Access /api/v1/myServicePath for tenant1 without an apikey header:

Request:

Shell




x


 
1
curl --request GET \
2
  --url http://localhost:8000/api/v1/myServicePath \
3
  --header 'host: tenant1.mydomain.com'


Response:

Shell




xxxxxxxxxx
1


 
1
{
2
    "message": "No API key found in request"
3
}



9. Access /api/v1/myServicePath for tenant1 with the corresponding api key generated in step 7:

Request:

Shell




x


1
curl --request GET \
2
  --url http://localhost:8000/api/v1/myServicePath \
3
  --header 'apikey: 6DlgBFKA2a0uD9CHVgqQvsZfWsePC0zu' \
4
  --header 'host: tenant1.mydomain.com'


Response:

Shell




xxxxxxxxxx
1


 
1
{
2
    "message": "name resolution failed"
3
}


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:

Shell




xxxxxxxxxx
1


 
1
{
2
    "message": "You cannot consume this service"
3
}



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.

Topics:
microservices best practices ,api gateway pattern ,kong gateway ,apikeys ,authentication ,multitenancy ,microservice architecture ,multitenant ,api consumers

Published at DZone with permission of Cezar Romaniuc . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}