How to Install Kong Custom Lua Plugin

There is no doubt that Kong is one of the best API gateways on the market. One of its cool features is that you can extend its functionality by writing custom plugins. I’ve got a couple viewers asking me to talk about how to install custom plugins, that’s what I am going to talk about in this post.

Kong used to support Lua plugins only but you can use Go, Javascript or Python plugins as of version 2.4. I will cover how to install Lua plugin on this post and Go Plugin on the next one.

Please note this post is about INSTALLING plugins. If you are looking for a tutorial about developing kong plugins, please read official post.

Prerequisites:
Docker: You need to have Docker installed and understand the basics of Docker.
Terminal: We will use terminal to run docker container and build docker images.
JQ: We will use JQ to filter outputs.

Start Kong

I will run Kong in DBless mode for our demo, container name is kong-demo. To start this Kong container, let’s run below command.

Please note the container will be removed when it stop. If you don’t want this behaviour, please remove --rm flag from the command. I am also running this container in detached mode as it is easier to play with.

1
2
3
4
5
6
docker run --rm --detach --name kong-demo \
-p "8000-8001:8000-8001" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_PROXY_LISTEN=0.0.0.0:8000" \
-e "KONG_DATABASE=off" \
kong:2.4-alpine

Now that we have our container running, we can install plugins.

Lua Plugin

I will be using kong-jwt2header as our example.

Install with lua files

This repo is extremely easy to use as the repo owner provides a script to install.

Clone repo

1
git clone https://github.com/yesinteractive/kong-jwt2header.git

Modify Script

Kong used to be running as root user but it is no longer the case. Therefore we need to modify this script to use Kong for reload.

Let’s cd kong-jwt2header to the folder and then use the editor of your choice to modify update_docker.sh file.

You need to change below line

1
docker exec --user root -e KONG_PLUGINS="bundled,$dir" $container kong reload - vv

to

1
docker exec --user kong -e KONG_PLUGINS="bundled,$dir" $container kong reload - vv

Run script

Now we can run our script.

1
./update_docker.sh

We will be asked to enter our container name. We enter kong-demo and enter.

Check plugin

1
curl http://localhost:8001 -s | jq .plugins | grep kong-jwt2header

We should see "kong-jwt2header": true in the result.

What this script does was

  1. Create folder /usr/local/share/lua/5.1/kong/plugins/kong-jwt2header inside kong-demo container.
  2. Copy two plugins files into this folder.
  3. Add environment variable KONG_PLUGINS="bundled,kong-jwt2header" and then reload Kong.

Install with Luarocks

This might not work after GitHub made this change.

If you are getting below error, you probably need to update your rockspec or use the manual installation method.

1
2
3
4
5
6
7
8
docker exec -it --user root kong-demo luarocks install kong-jwt2header
Installing https://luarocks.org/kong-jwt2header-1.0-3.rockspec
Cloning into 'kong-jwt2header'...
fatal: unable to connect to github.com:
github.com[0: 52.64.108.95]: errno=Operation timed out


Error: Failed cloning git repository.

We can also install plugins with luarocks. Luarocks is the package manager for Lua modules. As Kong has luarocks installed by default, we can easily install rock files inside container. We will still use kong-jwt2header because it is also available on luarocks website.

Install Rock file

Let’s stop and start a new kong-demo container first.

Then we run below command.

1
docker exec -it --user root kong-demo luarocks install kong-jwt2header

As you can see I am running luarocks install as root user inside the container and luarocks will fetch and install plugin automatically.

Enable Plugin and reload

We need to add environment variable KONG_PLUGINS="bundled,kong-jwt2header" and reload Kong.

1
docker exec --user kong -e KONG_PLUGINS="bundled,kong-jwt2header" kong-demo kong reload -vv

Check plugin

1
curl http://localhost:8001 -s | jq .plugins | grep kong-jwt2header

We should see "kong-jwt2header": true in the result as well.

Build custom image

In my opinion the best way to use custom plugin is to build a custom image because you don’t need to install plugin manually in case you need to rotate your pod.

To build a custom image is quite simple, here is an example.

  1. Please save below to Dockerfile.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FROM alpine:latest as builder
    RUN apk add --no-cache git
    RUN mkdir /jwt2header
    RUN git clone https://github.com/yesinteractive/kong-jwt2header.git /jwt2header

    FROM kong:2.4-alpine
    USER root
    RUN mkdir /usr/local/share/lua/5.1/kong/plugins/kong-jwt2header
    COPY --from=builder /jwt2header/plugin/. /usr/local/share/lua/5.1/kong/plugins/kong-jwt2header
    USER kong
  2. Build image
    Below command builds the image and tag it as kong-demo. It will also replace existing kong-demo image if you already have one.

    1
    docker build --no-cache -t kong-demo .
  3. Start Kong with custom image

    1
    2
    3
    4
    5
    6
    7
    docker run --rm --detach --name kong-demo \
    -p "8000-8001:8000-8001" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
    -e "KONG_PROXY_LISTEN=0.0.0.0:8000" \
    -e "KONG_DATABASE=off" \
    -e "KONG_PLUGINS=bundled,kong-jwt2header" \
    kong-demo

Use custom plugin

As the plugin is installed and enabled, let me show you how to use this plugin.

kong-jwt2header plugin can be used to extract claim:value from a passed in JWT token and apply its value to header. It can be used to change the routing or simply add extra headers to upstream.

As we are running Kong in dbless mode, we need to pass our declarative config to Kong. You can choose to mount this file inside container so Kong will start with all the configs. However, I will post this config to kong via admin api /config endpoint.

Create Config file

Let’s save this file as test.yaml in our current folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
_format_version: "2.1"
_transform: true

services:
- name: first-demo
url: https://httpbin.org/anything
routes:
- name: first-demo-route
paths:
- /demo
- name: second-demo
url: https://mockbin.org/request
routes:
- name: second-demo-route
headers:
x-kong-jwt-claim-demo:
- test
paths:
- /demo

plugins:
- config:
strip_claims: "false"
token_required: "false"
enabled: true
name: kong-jwt2header

As you can see I’ve got two services with two routes. The first route match all request go to <proxy_node>/demo and the second route match when the requests go to path <proxy_node>/demo with a header x-kong-jwt-claim-demo: test.

We also enable this plugin globally and set config.strip_claims and config.token_required as false.

Let’s apply this config with below command.

1
curl -X POST http://localhost:8001/config -F [email protected]

Test Plugin

  1. Test first-demo-route
    We should reach httpbin with below request.

    1
    curl http://localhost:8000/demo
  2. Test second-demo-route
    We should reach mockbin with below request.

    1
    curl http://localhost:8000/demo --header "x-kong-jwt-claim-demo:test"
  3. Generate JWT
    You can use jwt.io to create your token. I am using JWT-cli.

    1
    jwt encode --secret=demo '{"demo":"test"}'

    We should get a JWT token like this one.

    1
    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkZW1vIjoidGVzdCIsImlhdCI6MTYyNDAxMDU0MH0.HxVkGI1N0woJPtjiJRYZI-xwbMJ77w9dC1QhYcoFCbU
  4. Call route with token

    1
    2
    curl http://localhost:8000/demo \
    --header "authorization:bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkZW1vIjoidGVzdCIsImlhdCI6MTYyNDAxMDU0MH0.HxVkGI1N0woJPtjiJRYZI-xwbMJ77w9dC1QhYcoFCbU"

We can see we are being routed to second-demo-route because plugin extract claim demo:test from JWT token and created x-kong-jwt-claim-demo:test header, hence the new request match the second route.

In the following post, I will cover how to use Custom Go plugin with Kong.

Thanks for reading.