Before you begin

  1. Follow the Get started guide to install Gloo Gateway, set up a gateway resource, and deploy the httpbin sample app.

  2. 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.

  1. 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"}]}'
    EOF
      
  2. 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% 
       

  3. 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.

    1. Save the JWT token for Alice. Alice works in the dev team.

        export ALICE_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImFsaWNlIiwidGVhbSI6ImRldiIsImV4cCI6MjA3NDI3NDg4NCwibGxtcyI6eyJvcGVuYWkiOlsiZ3B0LTMuNS10dXJibyJdfX0.il5Rjsad65jpQR_pyRzBdEKFSj-ERmBf4K2VksvGvswWVv4n79lYERslr4KCECuiz9y_T-xUiQ9IkhW3YHzl5zo1kajhhIg7Nhnl1AvAqODbnF6wYpLRk0Npna_2T6lK3Yj54qQGi6vXG3IMRpo1_o2DrbdlKx2k_WFegCoQyyYazb4z3ZXfWvTiWqQDJA5wWcM3-jKzAWfNM8zgZWa-1BeAHDvpLcfWtuXEGSjkdCW0FQJOTjgIEqACnnXb2Jio0tWgelh9hDPILI-tvanj3iKCjpf3uF6g8QWSBNoVFfu7F1jJgj5Aj1sX8AV-CQVu2aQx3EHRZ1mL_3w3qSRWPw
        
    2. Save the JWT token for Bob. Bob works in the ops team.

        export BOB_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbG8tcHVibGljLWtleS0wMDEifQ.eyJpc3MiOiJzb2xvLmlvIiwib3JnIjoic29sby5pbyIsInN1YiI6ImJvYiIsInRlYW0iOiJvcHMiLCJleHAiOjIwNzQyNzQ5NTQsImxsbXMiOnsibWlzdHJhbGFpIjpbIm1pc3RyYWwtbGFyZ2UtbGF0ZXN0Il19fQ.GF_uyLpZSTT1DIvJeO_eish1WDjMaS4BQSifGQhqPRLjzu3nXtPkaBRjceAmJi9gKZYAzkT25MIrT42ZIe3bHilrd1yqittTPWrrM4sWDDeldnGsfU07DWJHyboNapYR-KZGImSmOYshJlzm1tT_Bjt3-RK3OBzYi90_wl0dyAl9D7wwDCzOD4MRGFpoMrws_OgVrcZQKcadvIsH8figPwN4mK1U_1mxuL08RWTu92xBcezEO4CdBaFTUbkYN66Y2vKSTyPCxg3fLtg1mvlzU1-Wgm2xZIiPiarQHt6Uq7v9ftgzwdUBQM1AYLvUVhCN6XkkR9OU3p0OXiqEDjAxcg
        
  4. 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 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"
         ]
     }
    }
      
  5. 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.

  1. Create a RouteOption resource that extracts the team claim 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
    EOF
      
  2. Send 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"
         ]
     }
    }
      
  3. 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% 
      
  4. Update the RouteOption resource to allow only JWTs that include the mistral-large-latest model in the llms.mistralai nested claim. The llms.mistralai claim is a list of models that the JWT has access to. To ensure that only JWTs with access to the mistral-large-latest model are allowed, use the LIST_CONTAINS claims 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
    EOF
      
  5. Send a request to httpbin with the JWT token for Alice. Alice’s token does not have the llms.mistralai nested 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 denied
      
  6. Repeat the request with Bob’s token, which includes the llms.mistralai nested list claim. The list includes the mistral-large-latest model 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