Gateway-level JWT policy
Learn how to set up JWT authentication and claim-based authorization for all routes attached to a gateway.
This feature is an Enterprise-only feature that requires a Gloo Gateway Enterprise license.
Before you begin
Follow the Get started guide to install Gloo Gateway, set up a gateway resource, and deploy the httpbin sample app.
Get the external address of the gateway and save it in an environment variable.
Add JWT policy to gateways
To protect all the routes that are attached to a gateway, create a JWT policy with a VirtualHostOption.
Create a VirtualHostOption with the details of the JSON Web Key Set (JWKS) server to use to verify the signature of JWTs in future protected requests.
kubectl apply -f- <<EOF apiVersion: gateway.solo.io/v1 kind: VirtualHostOption metadata: name: jwt namespace: gloo-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http namespace: gloo-system options: jwt: providers: selfminted: issuer: solo.io jwks: local: key: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAskFAGESgB22iOsGk/UgX BXTmMtd8R0vphvZ4RkXySOIra/vsg1UKay6aESBoZzeLX3MbBp5laQenjaYJ3U8P QLCcellbaiyUuE6+obPQVIa9GEJl37GQmZIMQj4y68KHZ4m2WbQVlZVIw/Uw52cw eGtitLMztiTnsve0xtgdUzV0TaynaQrRW7REF+PtLWitnvp9evweOrzHhQiPLcdm fxfxCbEJHa0LRyyYatCZETOeZgkOHlYSU0ziyMhHBqpDH1vzXrM573MQ5MtrKkWR T4ZQKuEe0Acyd2GhRg9ZAxNqs/gbb8bukDPXv4JnFLtWZ/7EooKbUC/QBKhQYAsK bQIDAQAB -----END PUBLIC KEY----- EOF
Send a request to the httpbin app. Verify that your request is denied and that you get back a 401 HTTP response code, because all routes on the gateway now require a valid JWT token from the provider.
Example output:
< HTTP/1.1 401 Unauthorized HTTP/1.1 401 Unauthorized < www-authenticate: Bearer realm="http://www.example.com:8080/headers" www-authenticate: Bearer realm="http://www.example.com:8080/headers" < content-length: 14 content-length: 14 < content-type: text/plain content-type: text/plain < date: Fri, 28 Jun 2024 02:19:00 GMT date: Fri, 28 Jun 2024 02:19:00 GMT < server: envoy server: envoy < * Connection #0 to host 34.XXX.XX.XXX left intact Jwt is missing%
Create an environment variable to save the JWT tokens for the users Alice and Bob. You can optionally create other JWT tokens by using the JWT generator tool.
Save the JWT token for Alice. Alice works in the
dev
team.export ALICE_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyAiaXNzIjogInNvbG8uaW8iLCAib3JnIjogInNvbG8uaW8iLCAic3ViIjogImFsaWNlIiwgInRlYW0iOiAiZGV2IiwgImxsbXMiOiB7ICJvcGVuYWkiOiBbICJncHQtMy41LXR1cmJvIiBdIH0gfQ.I7whTti0aDKxlILc5uLK9oo6TljGS6JUrjPVd6z1PxzucUa_cnuKkY0qj_wrkzyVN5djy4t2ggE1uBO8Llpwi-Ygru9hM84-1m53aO07JYFya1VTDsI25tCRG8rYhShDdAP5L935SIARta2QtHhrVcd1Ae7yfTDZ8G1DXLtjR2QelszCd2R8PioCQmqJ8PeKg4sURhu05GlBCZoXES9-rtPVbe6j3YLBTodJAvLHhyy3LgV_QbN7IiZ5qEywdKHoEF4D4aCUf_LqPp4NoqHXnGT4jLzWJEtZXHQ4sgRy_5T93NOLzWLdIjgMjGO_F0aVLwBzU-phykOVfcBPaMvetg
Save the JWT token for Bob. Bob works in the
ops
team.export BOB_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyAiaXNzIjogInNvbG8uaW8iLCAib3JnIjogInNvbG8uaW8iLCAic3ViIjogImJvYiIsICJ0ZWFtIjogIm9wcyIsICJsbG1zIjogeyAibWlzdHJhbGFpIjogWyAibWlzdHJhbC1sYXJnZS1sYXRlc3QiIF0gfSB9.p7J2UFwnUJ6C7eXsFCSKb5b7ecWZ75JO4TUJHafjLv8jJ7GzKfJVk7ney19PYUrWrO4ntwnnK5_sY7yaLUBCJ3fv9pcoKyRtJTw1VMMTQsKkWFgvy-jEwc9M-D5lrUfR1HXGEUm6NBaj_Ja78XScPZb_-APPqMIvzDZU04vd6hna3UMc4DZE0wcnTjOqoND0GllHLupYTfgX0v9_AYJiKRAcJvol1W14dI7szpY5GFZtPqq0kl1g0sJPg-HQKwf7Cfvr_JLjkepNJ6A1lsrG8QbuUvMUAdaHzwLvF3L_G6VRjEte6okZpaq0g2urWpZgdNmPVN71Q_0WhyrJTr6SyQ
Send another request to the httpbin app. This time, you include Alice’s JWT token in the
Authorization
header. Because these JWT tokens were signed by the JWT issuer that is used in the VirtualHostOption, the request now succeeds. Verify that you get back a 200 HTTP response code.Example output:
< HTTP/1.1 200 OK HTTP/1.1 200 OK .... { "headers": { "Accept": [ "*/*" ], "Host": [ "www.example.com:8080" ], "User-Agent": [ "curl/7.77.0" ], "X-Envoy-Expected-Rq-Timeout-Ms": [ "15000" ], "X-Forwarded-Proto": [ "http" ], "X-Request-Id": [ "c7e10708-abda-42b7-833e-b6ac93252612" ] } }
Repeat the request with Bob’s JWT token. Verify that the request succeeds with a 200 HTTP response code.
Example output:
< HTTP/1.1 200 OK HTTP/1.1 200 OK ...
Authorize access based on claims
You can use the claims in the JWT token to restrict access beyond basic authentication.
Create a RouteOption resource that extracts the
team
claim from the JWT token. The following example allows access to httpbin only if the JWT contains the"team": "dev"
claim.kubectl apply -f- <<EOF apiVersion: gateway.solo.io/v1 kind: RouteOption metadata: name: jwt namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin options: rbac: policies: viewer: nestedClaimDelimiter: . principals: - jwtPrincipal: claims: team: dev EOF
Send another request to the httpbin with the JWT token for Alice. Because the JWT matcher is set to
LIST_CONTAINS
, the request only succeeds if theteam: dev
claims is present in the JWT token. Because Alice’s JWT token includes that claim, the request succeeds.Example output:
< HTTP/1.1 200 OK HTTP/1.1 200 OK .... { "headers": { "Accept": [ "*/*" ], "Host": [ "www.example.com:8080" ], "User-Agent": [ "curl/7.77.0" ], "X-Envoy-Expected-Rq-Timeout-Ms": [ "15000" ], "X-Forwarded-Proto": [ "http" ], "X-Request-Id": [ "c7e10708-abda-42b7-833e-b6ac93252612" ] } }
Repeat the request with Bob’s JWT token. Because Bob’s token does not include the
team: dev
claim, the request is denied and a 403 HTTP response code is returned.Example output:
* Mark bundle as not supporting multiuse < HTTP/1.1 403 Forbidden < content-type: text/plain < date: Tue, 18 Jun 2024 03:21:26 GMT < server: envoy < connection: close < transfer-encoding: chunked
Next steps
Good job protecting all the routes attached to the gateway.
Next, try out a guide that applies a JWT policy at a route-specific level.
Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl delete routeoption jwt -n httpbin
kubectl delete virtualhostoption jwt -n gloo-system