How to Use Kong Vault Authentication Plugin

Vault Authentication plugin integrates Hashicorp Vault to Kong Enterprise for user to manage their credentials. Users can either load existing credentials from vault or they create and store new credentials to vault via Kong Admin API.

Prepare Vault

I have vault install on my mac, If you’ve set up your vault instance already or prefer any other deployment methods, you can jump to the next section.

Dev Mode

Enable Vault dev mode

1
vault server -dev -dev-listen-address=0.0.0.0:8200

See the unseal code and root token

1
2
Unseal Key: 2XuiYKb8GUL5E7Jny+ukn9hPOP+r+UDCBMUxRHpxmdk=
Root Token: s.1g0JIQb90ASmhjjPreP4CMKD

Create vault environment Variable

1
2
export VAULT_ADDR='http://0.0.0.0:8200'
export vTOKEN=s.1g0JIQb90ASmhjjPreP4CMKD

Create version 1 kv engine

1
vault secrets enable -version=1 -path=key-auth kv

Standalone Mode (file storage)

Create Config file

1
2
3
4
5
6
7
8
9
10
11
disable_mlock = true
ui = true

listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}

storage "file" {
path = "./data"
}

Start Vault

1
vault server -config=vault-server.hcl

Init Vault

open a new terminal window and put below environment variable

1
export VAULT_ADDR='http://0.0.0.0:8200'

Then we can init

1
vault operator init -n=1 -t=1

We should get 1 unseal key and 1 root token

1
2
Unseal Key 1: 9LF/fflOotsX8biDBK6yqKgQPcB1803J/jTHbWlaq6E=
Initial Root Token: s.A2OPJxNH9g9TpxnqJyloCCr9

Unseal Vault

1
vault operator unseal 9LF/fflOotsX8biDBK6yqKgQPcB1803J/jTHbWlaq6E=

Add Token to Environment Varible

1
export VAULT_TOKEN=s.A2OPJxNH9g9TpxnqJyloCCr9

Create version 1 KV engine

1
vault secrets enable -path=key-auth -version=1 kv

Usage example

All examples below are deployed locally with Docker or Minikube with Kong EE version 2.1.4.0-alpine image. Please adjust settings to suit your needs.

Prerequisite : A Kong instance running with Admin API

You can find official documentation here.

In the first part, I will use Admin API to set up Vault authentication plugin step by step.

Let’s begin:

Create a service

1
2
3
4
curl -X POST http://localhost:8001/services \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"name":"vault-service","url":"https://httpbin.org/anything"}'

Create Route

1
2
3
4
curl -X POST http://localhost:8001/services/vault-service/routes \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"name":"vault-route","paths":["/vault"]}'

When we visit our route at http://<KONG_PROXY>/valut, we should get HTTP/1.1 200 OK.

Create Vault object

1
2
3
4
5
6
7
8
curl --request POST \
--url http://localhost:8001/vaults \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=<VAULT_OBJECT_NAME> \
--data host=<VAULT_HOST_NAME> \
--data mount=<VAULT_MOUNT_POINT> \
--data port=8200 \
--data vault_token=<VAULT_TOKEN>

You should get a response like below.

1
2
3
4
5
6
7
8
9
10
11
{
  "created_at": 1603857862,
  "host": "<VAULT_HOST_NAME>",
  "id": "<VAULT_ID>",
  "mount": "<VAULT_MOUNT_POINT>",
  "name": "<VAULT_OBJECT_NAME>",
  "port": 8200,
  "protocol": "http",
  "updated_at": 1603857862,
  "vault_token": "<VAULT_TOKEN>"
}

Enable Vault Auth plugin on this route

You can also enable JWT plugin on a service or globally.

1
2
3
4
5
curl --request POST \
--url http://localhost:8001/routes/vault-route/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=vault-auth \
--data config.vault.id=<VAULT_ID>

If we visit the route again, we will get HTTP/1.1 401 Unauthorized

Create consumer

1
2
3
4
curl -X POST http://localhost:8001/consumers \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"username":"vault-user"}'

Create Credential

Below command creates a credential for bob and set ttl to 3600 (1 hour) which means Kong will fetch this credential from vault every 1 hour.

1
2
3
4
curl -X POST http://localhost:8001/vaults/<VAULT_OBJECT_NAME>/credentials/vault-user \
-H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
-d '{"ttl":3600}'

We should get a response similar as below:

1
2
3
4
5
6
7
8
9
10
11
{
"data": {
"access_token": "axEHjn5ViJqhEIz6VIXl4AtebSPKnxd0",
"consumer": {
"id": "07d7e98f-327c-4097-a48e-a6087f98bb0c"
},
"created_at": 1607319673,
"secret_token": "CjJa6GAgLnBzr2frU49AML5MQ6mX2l5X",
"ttl": 3600
}
}

Access API

1
2
3
curl <KONG_PROXY>:8000/vault \
-H "access_token:axEHjn5ViJqhEIz6VIXl4AtebSPKnxd0" \
-H "secret_token:CjJa6GAgLnBzr2frU49AML5MQ6mX2l5X"

You should be able to access your API now.

Other deployments methods

DBless deployment

Because all credentials are stored in vault, we can still Admin API to create credentials in dbless deployment. If you prefer to use vault to manager all credential without exposing Admin API, you can create vault credentials manually and load it when Kong starts.

For further detail, please check create credential manually

First we need to generate an UUID for our vault object. Let’s use uuidgen to generate one E1B0164F-E80E-4373-9880-60B0C2C515DF. This will be used as your Vault object ID.

Then we can start Kong with below configurations. Please change all the id, token and secrets to your setting.

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
27
28
29
30
31
_format_version: "2.1"
_transform: true

services:
- name: vault-service
url: https://httpbin.org/anything
routes:
- name: vault-route
paths:
- /vault

consumers:
- username: vault-user
id: c0278688-8aa8-45ff-94d2-fd4897e3a800

plugins:
- name: vault-auth
service: ~
route: vault-route
config:
vault:
id: E1B0164F-E80E-4373-9880-60B0C2C515DF

vaults:
- host: <VAULT_HOST_NAME>
vault_token: <VAULT_TOKEN>
mount: <VAULT_MOUNT_POINT>
protocol: http
name: <VAULT_OBJECT_NAME>
port: 8200
id: E1B0164F-E80E-4373-9880-60B0C2C515DF

Access API

Because we have created credentials previously, we can use credential directly once Kong starts.

1
2
3
curl <KONG_PROXY>:8000/vault \
-H "access_token:axEHjn5ViJqhEIz6VIXl4AtebSPKnxd0" \
-H "secret_token:CjJa6GAgLnBzr2frU49AML5MQ6mX2l5X"

Kubernetes Ingress Controller

We need to use custom entities to define our Vault object on Kubernetes.

Create Namespace

1
kubectl create namespace kong

Create vault.json file.

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"vaults": [
{
"host": "<VAULT_HOST_NAME>",
"id": "<VAULT_ID>",
"mount": "<VAULT_MOUNT_POINT>",
"name": "<VAULT_OBJECT_NAME>",
"port": 8200,
"protocol": "http",
"vault_token": "<VAULT_TOKEN>"
}
]
}

Store the configuration as a Kubernetes Secret.

1
kubectl create secret generic -n kong kong-custom-entities --from-file=config=vault.json

Deploy Kong in DBless mode

Please check here for detail.

You can add environment variable to the ingress-ccontroller container on your yaml file before deploying the file.

1
2
3
env:
- name: CONTROLLER_KONG_CUSTOM_ENTITIES_SECRET
value: kong/kong-custom-entities

You can also deploy the official file

1
kubectl apply -f https://bit.ly/k4k8s-enterprise-install

After it is deployed you can use kubectl to edit or jsonpatch to patch deployment.

1
kubectl patch deployment ingress-kong -n kong --type json -p='[{"op":"add","path":"/spec/template/spec/containers/1/env/0","value":{"name":"CONTROLLER_KONG_CUSTOM_ENTITIES_SECRET","value":"kong/kong-custom-entities"}}]'

Deploy service, consumer, plugin and ingress as below

Please save below to kong.yaml file and kubectl apply -f kong.yaml to apply it. Please make sure to change <VAULT_ID> to match the vault object we created above.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-deployment
spec:
replicas: 1
selector:
matchLabels:
app: echo-pod
template:
metadata:
labels:
app: echo-pod
spec:
containers:
- name: echoheaders
image: k8s.gcr.io/echoserver:1.10
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
spec:
selector:
app: echo-pod
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: vault-user
annotations:
kubernetes.io/ingress.class: "kong"
username: bob
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: proxy-from-k8s-to-httpbin
annotations:
konghq.com/plugins: "vault-auth"
konghq.com/strip-path: "true"
spec:
ingressClassName: kong
rules:
- http:
paths:
- backend:
service:
name: echo-service
port:
number: 80
path: /vault
pathType: Prefix
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: vault-auth
config:
access_token_name: access_token
vault:
id: <VAULT_ID>
secret_token_name: secret_token
plugin: vault-auth

Access API

After deployment completed, we can visit our route again.

1
2
3
curl <KONG_PROXY>:8000/vault \
-H "access_token:axEHjn5ViJqhEIz6VIXl4AtebSPKnxd0" \
-H "secret_token:CjJa6GAgLnBzr2frU49AML5MQ6mX2l5X"

Other resources

Create credential manually

Users can generate credentials manually into vault and load these credentials to Kong memory.

Create consumer id

On Mac, you can use uuidgen

1
2
uuidgen
672C883C-F4ED-4EFB-AF2E-97CC263B824D

Generate 2 random password

We will use these two password as access_token and secret_token.

1
2
pwgen -s 32 2
I15D9KE9crnIASqrCa98x49Usj3SODsg d4RNdIh30KAsVhqRKdTzbB09nWIN5VHv

Combine UUID and Password

Combine uuid, access_token and secret_token into json and then you can create your credentail with Vault UI.

1
2
3
4
5
6
7
8
{
"access_token": "I15D9KE9crnIASqrCa98x49Usj3SODsg",
"consumer": {
"id": "672C883C-F4ED-4EFB-AF2E-97CC263B824D"
},
"secret_token": "d4RNdIh30KAsVhqRKdTzbB09nWIN5VHv",
"ttl": 3600
}

Put credential to vault via vault CLI

You can also use Vault CLI. Please save above content to secret.json and run.

1
vault kv put key-auth/I15D9KE9crnIASqrCa98x49Usj3SODsg @secret.json

User Mapping

  • With Admin API
    We can create a user with the generated uuid above.

    1
    http POST :8001/consumers/ username=bob id=672C883C-F4ED-4EFB-AF2E-97CC263B824D
  • DBless mode

    1
    2
    3
    consumers:
    - username: paul
    id: 672C883C-F4ED-4EFB-AF2E-97CC263B824D
  • Kubernetes

    Assign consumer.id to a KongConsumer object is not supported at the moment.