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: '{"keys":[{"kty":"RSA","kid":"solo-public-key-001","use":"sig","alg":"RS256","n":"AOfIaJMUm7564sWWNHaXt_hS8H0O1Ew59-nRqruMQosfQqa7tWne5lL3m9sMAkfa3Twx0LMN_7QqRDoztvV3Wa_JwbMzb9afWE-IfKIuDqkvog6s-xGIFNhtDGBTuL8YAQYtwCF7l49SMv-GqyLe-nO9yJW-6wIGoOqImZrCxjxXFzF6mTMOBpIODFj0LUZ54QQuDcD1Nue2LMLsUvGa7V1ZHsYuGvUqzvXFBXMmMS2OzGir9ckpUhrUeHDCGFpEM4IQnu-9U8TbAJxKE5Zp8Nikefr2ISIG2Hk1K2rBAc_HwoPeWAcAWUAR5tWHAxx-UXClSZQ9TMFK850gQGenUp8","e":"AQAB"}]}' EOFSend 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
devteam.export ALICE_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPwSave the JWT token for Bob. Bob works in the
opsteam.export BOB_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImJvYiIsInRlYW0iOiJvcHMiLCJleHAiOjIwNzQyNzQ5NTQsImxsbXMiOnsibWlzdHJhbGFpIjpbIm1pc3RyYWwtbGFyZ2UtbGF0ZXN0Il19fQ.GF_uyLpZSTT1DIvJeO_eish1WDjMaS4BQSifGQhqPRLjzu3nXtPkaBRjceAmJi9gKZYAzkT25MIrT42ZIe3bHilrd1yqittTPWrrM4sWDDeldnGsfU07DWJHyboNapYR-KZGImSmOYshJlzm1tT_Bjt3-RK3OBzYi90_wl0dyAl9D7wwDCzOD4MRGFpoMrws_OgVrcZQKcadvIsH8figPwN4mK1U_1mxuL08RWTu92xBcezEO4CdBaFTUbkYN66Y2vKSTyPCxg3fLtg1mvlzU1-Wgm2xZIiPiarQHt6Uq7v9ftgzwdUBQM1AYLvUVhCN6XkkR9OU3p0OXiqEDjAxcg
Send another request to the httpbin app. This time, you include Alice’s JWT token in the
Authorizationheader. Because these JWT tokens were signed by the JWT issuer that is used in the JWT policy, 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
teamclaim from the JWT token. The following example allows access to httpbin only if the value of the"team"claim matches the"de."regex pattern.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: de. matcher: REGEX_MATCH EOFSend a request to httpbin with the JWT token for Alice, which includes the
"team": "dev"claim. This claim matches the regex pattern that you defined in the VirtualHostOption resource. Verify that 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. Bob’s token includes the
"team": "ops"claim that does not match the regex pattern that you defined earlier. Verify that 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 ... RBAC: access denied%Update the RouteOption resource to allow only JWTs that include the
mistral-large-latestmodel in thellms.mistralainested claim. Thellms.mistralaiclaim is a list of models that the JWT has access to. To ensure that only JWTs with access to themistral-large-latestmodel are allowed, use theLIST_CONTAINSclaims matcher.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: llms.mistralai: mistral-large-latest matcher: LIST_CONTAINS EOFSend a request to httpbin with the JWT token for Alice. Alice’s token does not have the
llms.mistralainested claim. Verify that the request is denied and a 403 HTTP response message is returned.Example output:
* Mark bundle as not supporting multiuse < HTTP/1.1 403 Forbidden < content-type: text/plain < server: envoy < connection: close < transfer-encoding: chunked ... RBAC: access deniedRepeat the request with Bob’s token, which includes the
llms.mistralainested list claim. The list includes themistral-large-latestmodel that you allowed in your RouteOption resource. Verify that 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" ] } }
Next steps
Good job protecting all the routes attached to the gateway.
Next, try out a guide that applies a JWT policy in more specific scenarios.
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