Skip to content

Commit 324be36

Browse files
committed
2 parents 691c303 + 82c0c27 commit 324be36

File tree

12 files changed

+345
-112
lines changed

12 files changed

+345
-112
lines changed

Dockerfile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM kong:latest
22

33
LABEL description="Alpine + Kong + kong-oidc plugin + LUA Plugins"
4-
# Install the js-pluginserver
4+
55
USER root
66
# RUN apk add --update nodejs npm python3 make g++ && rm -rf /var/cache/apk/*
77
# RUN npm install --unsafe -g [email protected]
@@ -12,9 +12,13 @@ RUN apk add --update vim nano
1212
RUN apk update && apk add curl git gcc musl-dev
1313
RUN luarocks install luaossl OPENSSL_DIR=/usr/local/kong CRYPTO_DIR=/usr/local/kong
1414
RUN luarocks install --pin lua-resty-jwt
15-
RUN luarocks install kong-oidc
15+
# RUN luarocks install kong-oidc -- deprecated
1616
RUN luarocks install lunajson
1717

18+
COPY ./luaplugins/oidc /plugins/oidc
19+
WORKDIR /plugins/oidc
20+
RUN luarocks make
21+
1822
COPY ./luaplugins/query-checker /plugins/query-checker
1923
WORKDIR /plugins/query-checker
2024
RUN luarocks make
@@ -27,4 +31,8 @@ COPY ./luaplugins/rbac /plugins/rbac
2731
WORKDIR /plugins/rbac
2832
RUN luarocks make
2933

34+
COPY ./luaplugins/scope-checker /plugins/scope-checker
35+
WORKDIR /plugins/scope-checker
36+
RUN luarocks make
37+
3038
USER kong

README.md

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
This repo shows how to protect your APIs using the Kong API Gateway working as PEP proxy with a Keycloak integration for authentication and authorization of incoming requests.
44

5+
- [1. Install](#1-install)
6+
- [2. Setup](#2-setup)
7+
- [3. Kong/Konga Configurations](#3-kongkonga-configurations)
8+
- [3.1 Creation of a Service](#31-creation-of-a-service)
9+
- [3.2 Creation of Routes](#32-creation-of-routes)
10+
- [3.3 Keycloak configs for Kong](#33-keycloak-configs-for-kong)
11+
- [3.4 Configure custom plugins](#34-configure-custom-plugins)
12+
- [3.4.1 Configuring Milti-Tenancy Plugin](#341-configuring-milti-tenancy-plugin)
13+
- [3.4.2 Configuring RBAC Plugin](#342-configuring-rbac-plugin)
14+
- [3.4.3 Configuring scope-checker Plugin](#343-configuring-scope-checker-plugin)
15+
- [3.4.4 Configuring Query-Checker plugin](#344-configuring-query-checker-plugin)
16+
- [4. Keycloak Configuration](#4-keycloak-configurations)
17+
- [Testing](#testing)
18+
- [Docs](#docs)
19+
20+
521
## 1. Install
622

723
The source code of all plugins must be in the respective folder, meaning:
@@ -51,9 +67,8 @@ Creating new routes for a given service is fairly simple. Just like creating a n
5167
3. Click on 'Other' and then select 'Add Plugin' option on the oidc plugin
5268
Configure the oidc plugins as shown in the image below with the configurations as per your client.
5369

54-
![Keycloak](img/keycloak.png)
70+
![Keycloak](img/oidc.png)
5571

56-
For client creation in Keycloak, please refer to [this guide](kongkeycloak.pdf).
5772

5873
### 3.4 Configure custom plugins
5974

@@ -63,15 +78,22 @@ To add any plugin, we follow the same procedure as done before with the OIDC plu
6378

6479
#### 3.4.1 Configuring Milti-Tenancy Plugin
6580

66-
You can add the plugin and global, service or at route level. Select the plugin in the 'Add Plugin' section.
81+
You can add the plugin at global, service or at route level. Select the plugin in the 'Add Plugin' section.
6782

6883
`tenant name` (required) field defines the custom header name. It can be renamed as per your custom requirement. This field will be checked against the token presented in the request.
69-
7084
![MultiTenancy](img/multi-tenancy.png)
7185

86+
Ex: As shown in the image above, we have set the tenant name to `fiware-service`. In the incoming request to kong, it is mandatory to have a request header with name "fiware-service" and the value set in keycloak for the given user.
87+
88+
*Note*: See section [4.5](#4-keycloak-configurations) for more details to configure keycloak.
89+
90+
![postman-multi-tenancy](img/postman-multi-tenancy.png)
91+
92+
7293
#### 3.4.2 Configuring RBAC Plugin
7394

7495
Similar to the multi-tenancy plugin, this plugin can be used at global, service or routes level.
96+
When `use custom roles` is disabled, the plugin expects the role in the form *tenantname_role*. Ex: if the tenant name is set to *fiware-service:app*, then the accepted roles are `app_read`, `app_write` and `app_admin`.
7597

7698
- `tenant name`(required)(String) field defines the custom header name. It can be renamed as per your custom requirement.
7799

@@ -82,19 +104,83 @@ Similar to the multi-tenancy plugin, this plugin can be used at global, service
82104
- `admin role` (depends on 'use custom roles')(String) Pre-defined role needed for DELETE requests.
83105
- `include client role` (optional)(Bool) field indicated whether to use client roles or user roles configured in Keycloak.
84106
- `client name` (depends on include client role)(String) field is used to specify the name of the client.
85-
107+
108+
*Note*: See section [4.6](#4-keycloak-configurations) for more details to configure keycloak.
86109
![RBAC](img/rbac.png)
87110

111+
#### 3.4.3 Configuring scope-checker Plugin
112+
113+
*Note*: This plugin is intended for future use with release of oriod-ld.
114+
With this plugin its possible to validate the scopes sent as headers by the client against the permission given to the client in Keycloak. Make sure the headers and token attributes are set with the name as `scopes`
115+
116+
- `plus allowed` (Boolean) field used to decide plus(+) wildcard entries in scope
117+
Ex: /fiware/+/temparature allows /fiware/kitchen/temparature, /fiware/room1/temparature ..
118+
- `hash allowed` (Boolean) field used to decide hash(#) wildcard entries in scope
119+
Ex: /fiware/kitchen/# allows /fiware/kitchen/temparature , /fiware/kitchen/pressure ..
120+
121+
![scope-checker](img/scope-checker.png)
122+
123+
#### 3.4.4 Configuring Query-Checker plugin
124+
This plugin is used to authorize request path with query
125+
126+
- `path` (String) speicfy the full path that has to checked.
127+
Ex: /v2/entities/urn:ngsi-ld:Product:010?type=Product
128+
- `wildcard allowed` (Boolean) field used to include * wildcard as shown in fig
129+
130+
![query-checker](img/query-checker.png)
131+
132+
## 4. Keycloak Configurations
133+
134+
1. Create a new Realm if needed.
135+
1. Select the pop-up with `Add realm` option under *Master* on the left top of Keycloak admin page.
136+
2. Set the name of the realm, Ex: *kong* and click `create`
137+
2. Kong client :
138+
1. Create new client with client id `kong`
139+
2. Under client settings, set access type to `confidential`
140+
3. Set service accounts `enabled`
141+
4. Set valid redirect uris `*`
142+
5. Click on *Credentials*, use the Secret to configure kong in konga as mentioned in section [3.3](#33-keycloak-configs-for-kong)
143+
144+
![kong-keycloak](img/kong-keycloak.png)
145+
3. App Client:
146+
1. Create new client with client id `app`
147+
2. Under client settings, set access type to `public` or `confidential`
148+
3. Set valid redirect uris `app`
149+
150+
![app-keycloak](img/app-keycloak.png)
151+
4. Create a user in keycloak to get token using client id `app`
152+
5. To support *multi-tenancy* plugin, it is necessary to have tenant name in token:
153+
1. Click on *Clients* on the left navigation bar
154+
2. Click *Mappers* under Clients and *create*
155+
3. Set the *Name,User Attribute and Token Claim Name* as to *tenant name*, Ex: `fiware-service` and save
156+
4. Use the other follwing settings as shown in the image below.
157+
![fiware-mapper](img/fiware-mapper.png)
158+
5. Now go to *Users* in the left navigation bar,*view all users* and select the user.
159+
6. Click on *Attributes* and configure for fiware service as shown below.
160+
![user-attribute](img/user-attribute.png)
161+
7. The attributes can be multivalued with format *attr1##attr2* Ex: test1##test2
162+
6. To support *RBAC* plugin, it is necessary to configure roles in keycloak:
163+
1. Go to *Roles* in the left navigation bar, select *Add Role*.
164+
2. Set the name in the form `tenantname_role`, ex: `test1_read` and save.
165+
3. Go to Users and select the user to set roles.
166+
4. Select *Role Mapping* under given user and select the roles to be added under *Available Roles* and click *Add selected*.
167+
![user-roles](img/user-roles.png)
168+
5. You can also create `client roles` under *Clients* and then select *Roles*, create new role using *Add Role*
169+
6. Now under a given user, select *Role Mapping*, choose the client under *Client Roles* and add avaiable roles
170+
88171

89172
## Testing
90173

91174
After following the above steps, you can test the funcionalities of kong and its plugins using any rest client, e.g. postman or insomnia.
92175

93-
- Get a token from keycloak after creating a user.
176+
- Get a token from keycloak after creating a user.
94177
- Use the Authorization header to fill the above recieved token in the format 'Bearer <*token*>'
95-
- Add fiware-service header in the request and make sure it is put in the user token.
178+
- Add necessary headers depending on the plugins in the request and make sure it is put in the user token.
96179
- Send the request and verify.
97180

181+
*Ex*: To use postman for testing, please check the image below to configure token.
182+
![token-postman](img/token-postman.png)
183+
98184

99185
## Docs
100186

config/kong.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ services:
55
- name: example-service
66
url: http://localhost:1026/v2/entites
77
plugins:
8-
- name: multi-tenancy,query-checker,rbac
8+
- name: multi-tenancy, rbac, scope-checker, query-checker, oidc

docker-compose.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ services:
4646
KONG_ADMIN_ERROR_LOG: /dev/stderr
4747
KONG_PROXY_ACCESS_LOG: /dev/stdout
4848
KONG_PROXY_ERROR_LOG: /dev/stderr
49-
# Allow plugins to be used. The plugin name is your JS file name e.g. hello.js
50-
KONG_PLUGINS: bundled,oidc,multi-tenancy,query-checker,rbac
49+
KONG_PLUGINS: bundled,multi-tenancy, rbac, scope-checker, query-checker
5150
KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl
5251
KONG_ADMIN_LISTEN: 0.0.0.0:8001, 0.0.0.0:8444 ssl
5352
ports:
Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,48 @@
11
local M = {}
22

3-
function split_token (inputstr, sep)
3+
function split_token(inputstr, sep)
44
if sep == nil then
5-
sep = "%s"
5+
sep = "%s"
66
end
7-
local t={}
8-
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
9-
table.insert(t, str)
7+
local t = {}
8+
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
9+
table.insert(t, str)
1010
end
1111
return t
1212
end
1313

1414
-- decoding
1515
function M.decode(token)
16-
b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
16+
b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
1717
token_payload = split_token(token, ".")
1818
data = token_payload[2]
19-
data = string.gsub(data, '[^'..b..'=]', '')
20-
return (data:gsub('.', function(x)
21-
if (x == '=') then return '' end
22-
local r,f='',(b:find(x)-1)
23-
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
24-
return r;
25-
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
26-
if (#x ~= 8) then return '' end
27-
local c=0
28-
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
19+
data = string.gsub(data, "[^" .. b .. "=]", "")
20+
return (data:gsub(
21+
".",
22+
function(x)
23+
if (x == "=") then
24+
return ""
25+
end
26+
local r, f = "", (b:find(x) - 1)
27+
for i = 6, 1, -1 do
28+
r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and "1" or "0")
29+
end
30+
return r
31+
end
32+
):gsub(
33+
"%d%d%d?%d?%d?%d?%d?%d?",
34+
function(x)
35+
if (#x ~= 8) then
36+
return ""
37+
end
38+
local c = 0
39+
for i = 1, 8 do
40+
c = c + (x:sub(i, i) == "1" and 2 ^ (8 - i) or 0)
41+
end
2942
return string.char(c)
30-
end))
43+
end
44+
))
3145
end
3246
-- end of copy
3347

34-
return M
48+
return M
Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,53 @@
1-
local BasePlugin = require "kong.plugins.base_plugin"
1+
-- Baseplugin deprecated in version 3.x.x
2+
-- local BasePlugin = require "kong.plugins.base_plugin"
3+
-- local MultiTenancyHandler = BasePlugin:extend()
4+
5+
local MultiTenancyHandler = {
6+
VERSION = "0.0.2",
7+
PRIORITY = 10
8+
}
9+
210
local filter = require("kong.plugins.multi-tenancy.filter")
311
local kong = kong
412
local lunajson = require "lunajson"
513

6-
local MultiTenancyHandler = BasePlugin:extend()
7-
814
function MultiTenancyHandler:new()
915
MultiTenancyHandler.super.new(self, "multi-tenancy")
1016
end
1117

1218
local function check_tenant(conf)
1319
local token = kong.request.get_header("Authorization")
1420
local tenant_name = conf.tenant_name
15-
kong.log.debug(" ##### Tenant name " , tenant_name)
21+
kong.log.debug(" ##### Tenant name ", tenant_name)
1622
local tenant_header = kong.request.get_header(tenant_name)
1723
if token == nil or tenant_header == nil then
1824
kong.log.err("Cannot process Headers: ", err)
19-
return nil, { status = 403, message = "Headers missing !!" }
25+
return nil, {status = 403, message = "Headers missing !!"}
2026
end
2127
local token_decoded = filter.decode(token)
22-
local jsonparse = lunajson.decode( token_decoded )
28+
local jsonparse = lunajson.decode(token_decoded)
2329
if jsonparse[tenant_name] == nil then
2430
kong.log.err("fiware-service missing in token")
25-
return nil, { status = 403, message = "fiware-service missing in token" }
26-
else
31+
return nil, {status = 403, message = "fiware-service missing in token"}
32+
else
2733
arraylength = #jsonparse[tenant_name]
28-
for a = 1, arraylength do
34+
for a = 1, arraylength do
2935
if jsonparse[tenant_name][a] == tenant_header then
3036
return true
3137
end
3238
end
3339
end
3440
return false
35-
end
36-
41+
end
3742

3843
function MultiTenancyHandler:access(conf)
39-
MultiTenancyHandler.super.access(self)
44+
-- MultiTenancyHandler.super.access(self)
4045
local ok, err = check_tenant(conf)
4146
if not ok then
4247
return kong.response.error(403, "Permission Denied !")
43-
else
48+
else
4449
return
4550
end
4651
end
4752

48-
return MultiTenancyHandler
53+
return MultiTenancyHandler
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1+
local typedefs = require "kong.db.schema.typedefs"
2+
13
return {
2-
no_consumer = true,
4+
name = "multi-tenancy",
35
fields = {
4-
tenant_name = { type = "string", required = true , default = "fiware-service" }
6+
{
7+
config = {
8+
type = "record",
9+
fields = {
10+
{
11+
tenant_name = {type = "string", required = true, default = "fiware-service"}
12+
}
13+
}
14+
}
15+
}
516
}
6-
}
17+
}

0 commit comments

Comments
 (0)