Access Control List plugin
Configure the Kong ACL Plugin. To use the ACL Plugin you need at least one Authentication plugin. This example uses the JWT Auth Plugin.
Before you begin ensure that you have Installed Kong Ingress Controller with Gateway API support in your Kubernetes cluster and are able to connect to Kong.
Before you begin ensure that you have Installed Kong Ingress Controller with Gateway API support in your Kubernetes cluster and are able to connect to Kong.
Prerequisites
Install the Gateway APIs
-
Install the Gateway API CRDs before installing Kong Ingress Controller.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
-
Create a
Gateway
andGatewayClass
instance to use.echo " --- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: kong annotations: konghq.com/gatewayclass-unmanaged: 'true' spec: controllerName: konghq.com/kic-gateway-controller --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: kong spec: gatewayClassName: kong listeners: - name: proxy port: 80 protocol: HTTP " | kubectl apply -f -
The results should look like this:
gatewayclass.gateway.networking.k8s.io/kong created gateway.gateway.networking.k8s.io/kong created
Install Kong
You can install Kong in your Kubernetes cluster using Helm.
-
Add the Kong Helm charts:
helm repo add kong https://charts.konghq.com helm repo update
-
Install Kong Ingress Controller and Kong Gateway with Helm:
helm install kong kong/ingress -n kong --create-namespace
Test connectivity to Kong
Kubernetes exposes the proxy through a Kubernetes service. Run the following commands to store the load balancer IP address in a variable named PROXY_IP
:
-
Populate
$PROXY_IP
for future commands:export PROXY_IP=$(kubectl get svc --namespace kong kong-gateway-proxy -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo $PROXY_IP
-
Ensure that you can call the proxy IP:
curl -i $PROXY_IP
The results should look like this:
HTTP/1.1 404 Not Found Content-Type: application/json; charset=utf-8 Connection: keep-alive Content-Length: 48 X-Kong-Response-Latency: 0 Server: kong/3.0.0 {"message":"no Route matched with those values"}
Deploy an echo service
To proxy requests, you need an upstream application to send a request to. Deploying this echo server provides a simple application that returns information about the Pod it’s running in:
kubectl apply -f https://docs.konghq.com/assets/kubernetes-ingress-controller/examples/echo-service.yaml
The results should look like this:
service/echo created
deployment.apps/echo created
Add routing configuration
Create routing configuration to proxy /lemon
requests to the echo server:
The results should look like this:
Test the routing rule:
curl -i $PROXY_IP/lemon
The results should look like this:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 140
Connection: keep-alive
Date: Fri, 21 Apr 2023 12:24:55 GMT
X-Kong-Upstream-Latency: 0
X-Kong-Proxy-Latency: 1
Via: kong/3.2.2
Welcome, you are connected to node docker-desktop.
Running on Pod echo-7f87468b8c-tzzv6.
In namespace default.
With IP address 10.1.0.237.
...
If everything is deployed correctly, you should see the above response. This verifies that Kong Gateway can correctly route traffic to an application running inside Kubernetes.
After the first route is working, create a second pointing to the same Service:
The results should look like this:
Add JWT authentication to the service
To add authentication in front of an API you just need to enable a plugin.
-
Create a KongPlugin resource.
echo " apiVersion: configuration.konghq.com/v1 kind: KongPlugin metadata: name: app-jwt plugin: jwt " | kubectl apply -f -
The results should look like this:
kongplugin.configuration.konghq.com/app-jwt created
-
Associate the plugin to the Ingress rules.
kubectl annotate service echo konghq.com/plugins=app-jwt
The results should look like this:
service/echo annotated
Any requests matching the proxying rules for
/lemon
and/lime
now requires a valid JWT and the consumer for the JWT to be associate with the right ACL. Requests without credentials are rejected. -
Send a request without the credentials.
curl -i $PROXY_IP/lemon
The results should look like this:
HTTP/1.1 401 Unauthorized Date: Fri, 09 Dec 2022 23:51:35 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Content-Length: 26 X-Kong-Response-Latency: 1 Server: kong/3.0.1 {"message":"Unauthorized"}
Provision consumers
To access the protected endpoints, create two consumers.
-
Create a consumer named
admin
:echo "apiVersion: configuration.konghq.com/v1 kind: KongConsumer metadata: name: admin annotations: kubernetes.io/ingress.class: kong username: admin " | kubectl apply -f -
The results should look like this:
kongconsumer.configuration.konghq.com/admin created
-
Create a consumer named
user
:echo "apiVersion: configuration.konghq.com/v1 kind: KongConsumer metadata: name: user annotations: kubernetes.io/ingress.class: kong username: user " | kubectl apply -f -
The results should look like this:
kongconsumer.configuration.konghq.com/user created
Provision JWT credentials
JWT is a standard for tokens stored in JSON. They include a metadata section about the algorithms used to construct the JWT, information (“claims”) about the token and its bearer, and a cryptographic signature that recipients can use to verify the validity of the token.
When generating tokens ensure that the payload
data contains a field named iss
alongside name
and iat
. For the admin token you should set "iss": "admin-issuer"
and for the user token you should set "iss": "user-issuer"
.
As valid JWTs are not easily constructed by hand, you can use the jwt.io tool to generate cryptographic keys and sign your JWTs.
Warning: These examples use a shared public key. Ensure you use your own public key in production.
-
Create secrets by replacing the RSA key strings with your own from jwt.io. The credentials are stored in Secrets with a
kongCredType
key whose value indicates the type of credential.kubectl create secret \ generic admin-jwt \ --from-literal=key="admin-issuer" \ --from-literal=algorithm=RS256 \ --from-literal=secret="dummy" \ --from-literal=rsa_public_key="-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr6m2/8lMUCiBBgCXFf8B DNBZ1Puk2JchjjrKQSiAbkhMgcBUzXqUaxZDc8S3s4/E1Y8HT5JMML1wF6h/AIVM FjL1F+qDj0klAHae0tfAU3B2pvUpOSkWU1wWJxQDUH+CF2ihKdEhYMcQv1HGsyZM FNuhYbzo9gjcTegQDHgJZd0BSoNxVBvSjE/adUU7kYuAomLDP7ETqlSSWlgIEUxL FGhdch0x21J7OETlWJI3UbZxKyCOjWpqcuXYgTRnrHHD8Sy2LWs6hSIToO2ZwWHJ HLcyt026eWtIhzu9NHfvU74QGLcAuDooRqtbG/u1pd8NFC7GwLqv6aIoSEvPJhbC Br+HeihpCtWg4viM/uWG6La6h0aGpS5VLI/jjDfPN9yN5Yg57lHnipQNMeSisuAE a10LKm5l4O6MC1VrFEqZWVGVZ/B+jEFlaqGPDSd3YvIaM7vk7S9TB4O5tEPaJ2XH YQv5LtOyGxy0QpI3PyaD1Tks28wDotYcOsPMP59v7LlFewhmMw2eqzJ1lgQ3CuLr p343+BMdTfLiw4Nv2h8EVFp3FLpr/xBbeM9ifkloTis+QJsxbnelGF0SzhBP5W4M Fz/+NmBYpY72Q+XtoszN4E1QUsk1InJ3Wf6hZm3z/CKZLbKIn/UTYTjzKIBPQdLX C6V0e/O3LEuJrP+XrEndtLsCAwEAAQ== -----END PUBLIC KEY-----" kubectl label secret admin-jwt konghq.com/credential=jwt kubectl create secret \ generic user-jwt \ --from-literal=key="user-issuer" \ --from-literal=algorithm=RS256 \ --from-literal=secret="dummy" \ --from-literal=rsa_public_key="-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr6m2/8lMUCiBBgCXFf8B DNBZ1Puk2JchjjrKQSiAbkhMgcBUzXqUaxZDc8S3s4/E1Y8HT5JMML1wF6h/AIVM FjL1F+qDj0klAHae0tfAU3B2pvUpOSkWU1wWJxQDUH+CF2ihKdEhYMcQv1HGsyZM FNuhYbzo9gjcTegQDHgJZd0BSoNxVBvSjE/adUU7kYuAomLDP7ETqlSSWlgIEUxL FGhdch0x21J7OETlWJI3UbZxKyCOjWpqcuXYgTRnrHHD8Sy2LWs6hSIToO2ZwWHJ HLcyt026eWtIhzu9NHfvU74QGLcAuDooRqtbG/u1pd8NFC7GwLqv6aIoSEvPJhbC Br+HeihpCtWg4viM/uWG6La6h0aGpS5VLI/jjDfPN9yN5Yg57lHnipQNMeSisuAE a10LKm5l4O6MC1VrFEqZWVGVZ/B+jEFlaqGPDSd3YvIaM7vk7S9TB4O5tEPaJ2XH YQv5LtOyGxy0QpI3PyaD1Tks28wDotYcOsPMP59v7LlFewhmMw2eqzJ1lgQ3CuLr p343+BMdTfLiw4Nv2h8EVFp3FLpr/xBbeM9ifkloTis+QJsxbnelGF0SzhBP5W4M Fz/+NmBYpY72Q+XtoszN4E1QUsk1InJ3Wf6hZm3z/CKZLbKIn/UTYTjzKIBPQdLX C6V0e/O3LEuJrP+XrEndtLsCAwEAAQ== -----END PUBLIC KEY-----" kubectl label secret user-jwt konghq.com/credential=jwt
Validation requirements impose that even if the secret
is not used for algorithm
RS256
or ES256
the field secret
must be present, so put some dummy value for it.
The results should look like this:
secret/admin-jwt created
secret/admin-jwt labeled
secret/user-jwt created
secret/user-jwt labeled
To associate the JWT Secrets with your consumers, you must add their name to the credentials
array in the KongConsumers.
-
Assign the credentials
admin-jwt
to theadmin
.kubectl patch --type json kongconsumer admin \ -p='[{ "op":"add", "path":"/credentials", "value":["admin-jwt"] }]'
The results should look like this:
kongconsumer.configuration.konghq.com/admin patched
-
Assign the credentials
user-jwt
to theuser
.kubectl patch --type json kongconsumer user \ -p='[{ "op":"add", "path":"/credentials", "value":["user-jwt"] }]'
The results should look like this:
kongconsumer.configuration.konghq.com/user patched
Send authenticated requests
To send an authenticated request, you must create signed JWTs for your users. Here are the pre-populated examples for both the Admin and User JWTs:
The iss
field in the payload matches the value you provided in --from-literal=key=
when creating the Kubernetes secret.
-
Copy the “Encoded” value and store it in an environment variable for both the
ADMIN_JWT
andUSER_JWT
:export ADMIN_JWT=eyJhbG... export USER_JWT=eyJhbG...
-
Send a request with the
Authorization
header.curl -I -H "Authorization: Bearer ${USER_JWT}" $PROXY_IP/lemon
The results should look like this:
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 947 Connection: keep-alive Server: gunicorn/19.9.0 Date: Mon, 06 Apr 2020 06:45:45 GMT Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true X-Kong-Upstream-Latency: 7 X-Kong-Proxy-Latency: 2 Via: kong/3.1.1
Adding access control
The JWT plugin and other Kong authentication plugins only provide authentication, not authorization. They can identify a consumer, and reject any unidentified requests, but not restrict which consumers can access which protected URLs. Any consumer with a JWT credential can access any JWT-protected URL, even when the JWT plugins for those URLs are configured separately.
To provide authorization, or restrictions on which consumers can access which
URLs, you need to also add the ACL plugin, which can assign groups to consumers
and restrict access to URLs by group. Create two plugins, one which allows only
an admin
group, and one which allows both admin
and user
:
- Create an ACL plugin that allows only the
admin
group.echo " apiVersion: configuration.konghq.com/v1 kind: KongPlugin metadata: name: admin-acl plugin: acl config: allow: ['admin'] " | kubectl apply -f -
The results should look like this:
kongplugin.configuration.konghq.com/admin-acl created
- Create an ACL plugin that allows both the
admin
anduser
group.echo " apiVersion: configuration.konghq.com/v1 kind: KongPlugin metadata: name: anyone-acl plugin: acl config: allow: ['admin','user'] " | kubectl apply -f -
The results should look like this:
kongplugin.configuration.konghq.com/anyone-acl created
-
Add the plugins to the routing configuration you created.
The results should look like this:
-
Add consumers to groups through credentials.
echo ' apiVersion: v1 kind: Secret metadata: name: admin-acl labels: konghq.com/credential: acl stringData: group: admin ' | kubectl apply -f - echo ' apiVersion: v1 kind: Secret metadata: name: user-acl labels: konghq.com/credential: acl stringData: group: user ' | kubectl apply -f -
The results should look like this:
secret/admin-acl created secret/user-acl created
-
Associate the groups to their consumers through
credentials
.kubectl patch --type json kongconsumer admin \ -p='[{ "op":"add", "path":"/credentials/-", "value":"admin-acl" }]' kubectl patch --type json kongconsumer user \ -p='[{ "op":"add", "path":"/credentials/-", "value":"user-acl" }]'
The results should look like this:
kongconsumer.configuration.konghq.com/admin patched kongconsumer.configuration.konghq.com/user patched
Send authorized requests
-
Send a request as the
admin
consumer to both the URLS.curl -sI $PROXY_IP/lemon -H "Authorization: Bearer ${ADMIN_JWT}" | grep HTTP curl -sI $PROXY_IP/lime -H "Authorization: Bearer ${ADMIN_JWT}" | grep HTTP
The results should look like this:
HTTP/1.1 200 OK HTTP/1.1 200 OK
-
Send a request as the
user
consumer.curl -sI $PROXY_IP/lemon -H "Authorization: Bearer ${USER_JWT}" | grep HTTP curl -sI $PROXY_IP/lime -H "Authorization: Bearer ${USER_JWT}" | grep HTTP
The results should look like this:
HTTP/1.1 403 Forbidden HTTP/1.1 200 OK