How to Use KongCustomEntity for JWT Signer Plugin With Kong Ingress Controller
In my previous post, I demonstrated how to persist private keys for the JWT Signer plugin when Kong is deployed in DB-less mode by manually writing the keys in Kong declarative config. But if you’re using the Kong Ingress Controller (KIC), which generates Kong configurations dynamically, what’s the best approach?
There are two options:
- Host the keys in a separate service within the same cluster, as shown in my previous post.
- Use the new
KongCustomEntity
CRD for seamless key management. This CRD was introduced a few months back and made GA in KIC 3.4 version.
In this post, I’ll show you how to generate keys and use the KongCustomEntity
CRD to create the jwt_signer_jwks
entity and integrate it with the JWT signer plugin.
Let’s get started.
Prerequisites:
- A Kubernetes cluster (e.g., KinD)
- CLIs
- kubectl
- helm
- yq
- envsubst
- Python
- (Optional) nix
Install Kong
Add Helm Repository
1 | helm repo add kong https://charts.konghq.com |
Deploy Kong
We will use the kong/ingress
chart to deploy Kong in DB-less mode with the Kong Ingress Controller.
1 | helm upgrade -i kong kong/ingress \ |
Apply the Kong License
You need an enterprise license to use the JWT signer plugin. You can apply the license using the KongLicense
CRD. Assuming your license is stored in a license.json
file, you can use the following command to set it to an environment variable:
1 | export KONG_LICENSE_DATA=$(cat license.json) |
Next, apply the license by running the following command:
1 | envsubst << EOF | kubectl apply -f - |
With Kong set up, let’s proceed to generate the JWKs.
Deploy demo app
For this demo, I will create a deployment with go-httpbin image and expose it with an Ingress.
1 | kubectl create deployment httpbin --image=mccutchen/go-httpbin |
We also need an ingress to route requests to the httpbin service.
1 | kubectl create ingress httpbin-route --class kong --rule '/test*=httpbin-svc:8080' |
Once it is done, we should be able to
Generate keys
Here is a simple Python script to help us with the demo. Let’s store it to jwk.py and run it.
We’ll use Python to generate our RSA and ECC keys. Create a file named jwk.py with the following content:
You need to install the following Python libraries:
- joserfc
- pyyaml
1 | from joserfc.jwk import RSAKey, ECKey |
Running this script will generate several files:
- public-jwk.json: Contains the public keys for token verification
- private-jwk.yaml: Contains the private keys for signing
- rsa-private.pem and ecc-private.pem: PEM format private keys
1 | ➜ tre |
To view the algorithms and key IDs for your generated keys:
1 | (echo "alg kid"; yq e '.keys[] | .alg + " " + .kid' private-jwk.yaml) | column -t |
Example output:
1 | alg kid |
(Optional) Dev with Nix
If you have nix installed, you can use my flake-template to prepare the python devevlopment environment. For mroe information, please check this post.
Configuring Kong
Now that we have the keys generated, let’s see how we can use it with JWT signer plugin.
Create a KongCustomEntity
Create a template template.yaml with below content.
1 | apiVersion: configuration.konghq.com/v1alpha1 |
We will use yq to populate the keys field from private-jwk.yaml to the template and applies it to our cluster:
1 | yq eval '.spec.fields.keys = (load("private-jwk.yaml") | .keys)' template.yaml | kubectl apply -f - |
In the example above, we have created a KongCustomEntity
object named jwt-signer-demo-keyset in the default namespace. The type is jwt_signer_jwks, and the keyset name is demo.
Create a KongPlugin
Now, let’s create a KongPlugin resource that will use the keyset demo we just created. In this case, I’m specifying ES256 as the signing algorithm.
1 | apiVersion: configuration.konghq.com/v1 |
Apply plugin
Attach the plugin to the ingress we created earlier:
1 | kubectl annotate ingress httpbin-route konghq.com/plugins=jwt-signer-demo |
Verify the set up
Now that the plugin is applied, when we send a request to <kong_proxy>/test/anything
, we should receive a 401 response. After making the request with a valid token, the upstream httpbin service should return a response that includes a token signed by the JWT Signer plugin.
We can verify the token header kid
to confirm it was signed with the key we generated earlier.
For example:
1 | curl -s http://proxy.li.k8s/test/anything \ |
This should return _9NQ0RKVjdMaovuNlTtzcgQKFLd9SBLPNKUKWib_Ni8
which is the ECC kid we saw earlier.
Key rotation
To rotate the keys, simply regenerate private-jwk.yaml
and apply the KongCustomEntity
will do the trick.
That’s all I want to show you today, see you in the next one.