JWT, ext auth, and transformation policies
Use JWT, external auth, and transformation policies together to secure access to your routes.
Use JWT along with external auth and transformation policies together to secure access to your routes. The following example walks you through setting up the following resources:
- An external auth policy that secures access to the
httpbinroute by requiring a basicAuthorizationheader with a user password in requests. - A JWT policy that secures access to the
httpbinroute by requiring a JWT in anX-Authheader in requests. The policy also extracts claims from the JWT and adds the claims as headers in requests. - An external service to represent a remote JWKS server to get the public keys to validate the JWT.
- A transformation policy that extracts information from a header, transforms the information with a regular expression, and adds the information as a new request header. This example shows how you can use information extracted from the JWT claim in subsequent steps for more fine-grained traffic control.
Before you begin
This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started. If you have different names, make sure to update the sample configuration files in this guide.
- Set up Gloo Mesh Gateway in a single cluster.
- Install Bookinfo and other sample apps.
- Configure an HTTP listener on your gateway and set up basic routing for the sample apps.
Make sure that you have the following CLI tools, or something comparable:
htpasswdto generate hashed, salted passwords.base64to encode strings.
Make sure that the external auth service is installed and running. If not, install the external auth service.
kubectl get pods -A -l app=ext-auth-serviceGet the external address of your ingress gateway. The steps vary depending on the type of load balancer that backs the ingress gateway.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n istio-ingress istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSNote: Depending on your environment, you might see
<pending>instead of an external IP address. For example, if you are testing locally in kind or minikube, or if you have insufficient permissions in your cloud platform, you can instead port-forward the service port of the ingress gateway:kubectl -n istio-ingress port-forward deploy/istio-ingressgateway-main 8081
Step 1: Create an external auth policy
Create a basic external auth policy to secure access to the httpbin route. For more information about other types of external auth, see External authentication and authorization.
You can do the following steps in a different order, depending on when you want the policy to take effect. For example, you might want the policy to always take effect as soon as the route is created. To do so, you can create the policy before you add the route to the route table.
Generate a salt and hashed password for your user credentials. The following example uses the
htpasswdtool for a user nameduser.htpasswd -nbm user passwordExample output:
user:$apr1$TYiryv0/$8BvzLUO9IfGPGGsPnAgSu1Retrieve the salt and hashed password from the output of the previous step.
- Salt:
TYiryv0/ - Hashed password:
8BvzLUO9IfGPGGsPnAgSu1
- Salt:
Create a basic external auth policy that uses the salt and hashed password from the previous step.
kubectl apply -f - <<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: ExtAuthPolicy metadata: name: httpbin-ext-auth namespace: httpbin spec: applyToRoutes: - route: labels: route: httpbin config: glooAuth: booleanExpr: jwt || basic configs: - name: basic basicAuth: apr: users: user: hashedPassword: 8BvzLUO9IfGPGGsPnAgSu1 salt: TYiryv0/ - jwt: {} name: jwt server: name: httpbin-ext-auth-server namespace: gloo-mesh EOFReview the following table to understand this configuration. For more information, see the API reference.
Setting Description applyToRoutesUse labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, the policy applies to all routes in the workspace.booleanExprSet the order for processing auth configs when multiple authentication methods are used. In this example, requests that have a JWT are processed first. The basic auth that this external auth policy configures is only processed if a JWT is not included in the request. This setting saves extra processing time so that clients do not need to include both auth methods in their requests. name: basicSet the name for the basic auth configuration, such as basic. This name must match the name that you set in thebooleanExpr.basicAuthConfigure the basic auth credentials to use to authenticate requests. The example sets up user credentials for a user named userin the required APR1 format. For more information, see the API reference.hashedPasswordThe hashed password that you generated in the previous step. The example sets 8BvzLUO9IfGPGGsPnAgSu1.saltThe salt, or random data that hashes the password, that you generated in the previous step. The example sets TYiryv0/.jwtAdd a dummy external auth service to support multiple authentication methods with JWT. serverThe ExtAuthServerresource that represents the server for the policy to use, which you create in the next step.Create an external auth server to use for your policy.
kubectl apply -f - <<EOF apiVersion: admin.gloo.solo.io/v2 kind: ExtAuthServer metadata: name: httpbin-ext-auth-server namespace: gloo-mesh spec: destinationServer: port: number: 8083 ref: cluster: $CLUSTER_NAME name: ext-auth-service namespace: gloo-mesh EOFSend an unauthenticated request to the
httpbinapp.Example output: Notice that the request is denied with a
401 Unauthorizedresponse.HTTP/2 401Encode the expected user credentials from the external auth policy in base64 format.
echo -n "user:password" | base64Example output:
dXNlcjpwYXNzd29yZA==Repeat the request to the
httpbinapp, including the authorization header with the user credentials. This time, the request succeeds.Example output:
HTTP/2 200
Good job! You just secured basic access to the httpbin app. Now, you can add JWT verification with a JWT policy.
Step 2: Create a JWT policy
Create a basic JWT policy with a remote JWKS server to add claims from the JWT as headers to requests to the httpbin route. For more information about JSON web tokens, see JWT.
Get the following details for your remote JWKS server from your OIDC provider, such as Keycloak or Okta. Keep in mind that this server must be accessible from your cluster.
- Verify that the claims you want to pass along in the request header are included in the JWT that the OIDC provider generates, such as
organdemailclaims. - Get the Issuer URL for your OAuth2 server instance. The following examples show what the URLs might look like from common providers.
- Okta
defaultserver:https://dev-1234567.okta.com/oauth2/default - Deployed Keycloak instance in AWS with a
solorealm:http://a1aa111aaaa1a-123456789.us-west-1.elb.amazonaws.com:8080/auth/realms/solo
- Okta
- Get the remote JWKS URL to fetch the public keys. The following examples are from common providers.
- Okta
defaultserver:https://dev-1234567.okta.com/oauth2/default/v1/keys - Deployed Keycloak instance in AWS with a
solorealm:http://a1aa111aaaa1a-123456789.us-west-1.elb.amazonaws.com:8080/auth/realms/solo/protocol/openid-connect/certs
- Okta
- Verify that the claims you want to pass along in the request header are included in the JWT that the OIDC provider generates, such as
Create an external service to represent the remote JWKS server. The following example uses an
oktahost and assumes that you set up Okta and your Gloo virtual gateway for thehttpbinapp to listen for HTTPS traffic.kubectl apply -f - <<EOF apiVersion: networking.gloo.solo.io/v2 kind: ExternalService metadata: name: httpbin-jwks namespace: httpbin labels: expose: "true" host: okta spec: hosts: - dev-1234567.okta.com ports: - name: https number: 443 protocol: HTTPS clientsideTls: {} EOFCreate a basic JWT policy with the remote JWKS issuer details that you previously retrieved.
kubectl apply -f - <<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: JWTPolicy metadata: name: httpbin-jwt namespace: httpbin spec: applyToRoutes: - route: labels: route: httpbin config: phase: preAuthz: {} validationPolicy: ALLOW_MISSING providers: okta: keepToken: true claimsToHeaders: - append: true claim: org header: x-org - append: true claim: email header: x-email issuer: https://dev-1234567.okta.com/oauth2/default remote: url: https://dev-1234567.okta.com/oauth2/default/v1/keys destinationRef: kind: EXTERNAL_SERVICE port: number: 443 ref: name: httpbin-jwks tokenSource: headers: - name: X-Auth prefix: 'Bearer ' queryParams: - auth_token EOFReview the following table to understand this configuration. For more information, see the API reference.
Setting Description applyToRoutesUse labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, the policy applies to all routes in the workspace. Note that this policy applies to the same httpbin route that the external auth policy applies to.validationPolicySet to ALLOW_MISSINGto allow requests to succeed even if JWT authentication is missing, but fail when an invalid JWT token is presented. This way, clients do not have to pass a JWT in order to access thehttpbinapp. However, when clients do pass a JWT, the JWT is validated so that subsequent steps, like a transformation policy, use validated information.providersConfigure the JWT providers for the policy with the information that you previously retrieved. This example sets up an oktaprovider.claimsToHeadersSet the claims from the JWT payload that you want to extract and add as headers to the request before the request is forwarded to the upstream destination. This example extracts the organdemailclaims.issuerSet the JWT issuer that you previously retrieved. remote.urlConfigure the reference to the remote JWKS server with the details that you previously retrieved. remote.destinationRefAdd the details of the external service that you created to refer to the remote JWKS server. tokenSourceSpecify where Gloo Gateway looks for the token to use for JWT authentication. If unset, the default locations are tried in the following order: - The authorization header
X-Authwith the valueBearer <token>. - The query parameter for
access_token, such ashttps://<path>?access_token=<token>.
- The authorization header
Create a transformation policy that uses the information in the JWT
emailclaim to create a new header calleduser.kubectl apply -f - <<EOF apiVersion: trafficcontrol.policy.gloo.solo.io/v2 kind: TransformationPolicy metadata: name: httpbin-transform-user namespace: httpbin spec: applyToRoutes: - route: labels: route: httpbin config: phase: postAuthz: {} request: injaTemplate: extractors: user: header: 'X-Email' regex: '(.*)@.*$' subgroup: 1 headers: x-user: text: "{{ user }}" EOFReview the following table to understand this configuration. For more information, see the API reference.
Setting Description applyToRoutesUse labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, the policy applies to all routes in the workspace. Note that this policy applies to the same httpbin route that the JWT policy applies to.phaseSet the phase to postAuthzso that the transformation policy can use the information previously extracted from the JWT claim during the auth phase.requestConfigure an inja template to extract information from the existing headers, transform the information with a regular expression, and add a new request header. In the example, the policy extracts the X-Emailheader, grabs the information before the@sign in the email address, and adds this information to a newX-Userrequest header.
Step 3: Verify secured access to httpbin
To verify access with a JWT, you need an access token from your OIDC provider to include in the request.
Get an access token from your OIDC provider. The steps vary by provider, and you might need several details from your OIDC provider, such as the client ID, client secret, and token endpoint. For more information, consult your OIDC provider, such as the following guides that use the Postman and OIDC Debugger tools to get an access token.
- Okta refresh token docs or the Okta developer forum about Using Postman to get an access token.
- StackOverflow about Using Postman to get an access token from Keycloak.
Send an authenticated request to the
httpbinapp without the JWT. Notice that the request succeeds because you set the JWT validation to be optional (validationPolicy: ALLOW_MISSING).Send a request to the
httpbinapp again, this time with a fake JWT. The request fails because the JWT is not valid.Example output: Notice that the error message describes how the JWT is not formatted correctly.
HTTP/2 401 www-authenticate: Bearer realm="https://example.com:443/get", error="invalid_token" ... Jwt is not in the form of Header.Payload.Signature with two dots and 3 sectionsRepeat the request to the
httpbinapp, including the access token that you previously retrieved in the"X-Auth: Bearer <access-token>"header. Don’t include theX-Authorizationheader with basic auth, to avoid an unnecessary hop to the external auth service. This time, the JWT is validated and the request succeeds.Example output: Notice that the request includes the
organdemailclaims as headers to thehttpbinapp. Also, the transformation policy extracted the email from the JWT claim and added anX-Userheader.HTTP/2 200 ... { "args": {}, "headers": { "Accept": "*/*", "Authorization": "basic dXNlcjpwYXNzd29yZA==", "Host": "example.com", "User-Agent": "curl/7.87.0", "X-Auth": "Bearer eyJra...", "X-B3-Sampled": "0", "X-B3-Spanid": "c8547b27e22e0890", "X-B3-Traceid": "eb4be0e080550506c8547b27e22e0890", "X-Email": "user1@solo.io", "X-Envoy-Attempt-Count": "1", "X-Envoy-Decorator-Operation": "httpbin.httpbin.svc.cluster.local:8000/*", "X-Envoy-Internal": "true", "X-Envoy-Peer-Metadata": "ChQ...", "X-Envoy-Peer-Metadata-Id": "router~10.16.2.29~istio-ingressgateway-1-18-1-78b97d9d8d-vgmmn.istio-ingress~istio-ingress.svc.cluster.local", "X-Httpbin": "true", "X-Org": "solo.io", "X-User": "user1" }
Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl -n httpbin delete ExtAuthPolicy httpbin-ext-auth
kubectl -n gloo-mesh delete ExtAuthServer httpbin-ext-auth-server
kubectl -n httpbin delete ExternalService httpbin-jwks
kubectl -n httpbin delete JWTPolicy httpbin-jwt
kubectl -n httpbin delete TransformationPolicy httpbin-transform-user