Service Mesh (ASM) supports JSON Web Token (JWT) authentication to verify the source of HTTP requests. By validating the access token in a request header, ASM confirms that the request comes from a trusted issuer and allows only authorized requests to reach your services.
JWT authentication in ASM uses two Istio security resources that work together:
RequestAuthentication validates the token. If a request carries a JWT, this resource checks the token against the configured issuer and JSON Web Key Set (JWKS). Requests with invalid tokens are rejected. However, requests with _no token at all are still accepted_ -- they simply carry no authenticated identity.
AuthorizationPolicy enforces access control. This resource specifies who can access the service based on the authenticated identity. To require a valid JWT on every request, pair a
RequestAuthenticationwith anAuthorizationPolicy.
A RequestAuthentication alone does not block unauthenticated requests. To reject requests that carry no token, create a companion AuthorizationPolicy.
Prerequisites
Before you begin, make sure that you have:
A Container Service for Kubernetes (ACK) cluster added to an ASM instance, with ASM version 1.6 or later
Authentication methods
ASM supports two authentication methods:
| Method | Protocol | Scope |
|---|---|---|
| Transmission authentication | Mutual TLS (mTLS) | Service-to-service communication |
| Source authentication | JWT | Client-to-service requests |
JWT is a standard that uses representative claims to secure information transmission between two parties. The following steps walk through source authentication with JWT. For more information about JWT, see jwt.io.
Step 1: Deploy sample services
Create a namespace and deploy two sample services -- httpbin (the target service) and sleep (the client) -- to test JWT authentication.
Create a namespace named
foowith the labelistio-injection=enabledto enable sidecar injection. For more information, see Manage global namespaces.Create the following YAML files.
Connect to the ACK cluster with kubectl and deploy both services in the
foonamespace. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.kubectl apply -f httpbin.yaml -n foo kubectl apply -f sleep.yaml -n fooVerify that
sleepcan reachhttpbinwithout any authentication policy in place. Expected output: A200 OKresponse confirms that the services are running and reachable.kubectl exec -it deploy/sleep -n foo -- curl -I httpbin.foo.svc.cluster.local:8000HTTP/1.1 200 OK server: envoy date: Thu, 21 Dec 2023 07:39:55 GMT content-type: text/html; charset=utf-8 content-length: 9593 access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 14
Step 2: Create a RequestAuthentication policy
A RequestAuthentication resource defines how ASM validates JWTs for a specific workload. After you apply this policy, requests with invalid tokens are rejected with 401 Unauthorized. Requests with a valid token or no token at all are still accepted.
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > RequestAuthentication. Click Create from YAML.
Select foo from the Namespace drop-down list, enter the following YAML, and click Create. This policy applies to the
httpbinservice. ThejwtRulesfield specifies that any JWT in the request must havetesting@secure.istio.ioas its issuer (issclaim). Thejwksfield provides the public key used to verify the token signature. For more information about JWKS, see RFC 7517: JSON Web Key.ImportantIn production, use
jwksUriinstead of inlinejwksto point to a remote JWKS endpoint. This enables automatic key rotation without redeploying the policy.apiVersion: "security.istio.io/v1beta1" kind: "RequestAuthentication" metadata: name: "jwt-example" namespace: foo spec: selector: matchLabels: app: httpbin jwtRules: - issuer: "testing@secure.istio.io" jwks: '{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]}'Verify the policy by sending requests from the
sleepPod. The following sample token is used throughout this tutorial: Decode the token payload to see its claims: Output: To decode tokens visually, go to jwt.io. Now test with different tokens from thesleepPod: Note that requests without a token still succeed. The next step addresses this.Scenario Expected response Valid token 200 OKInvalid token 401 UnauthorizedNo token 200 OK(no identity, but accepted)TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'echo $TOKEN | cut -d '.' -f2 - | base64 --decode -{"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"}# Open a shell in the sleep Pod kubectl exec -it deploy/sleep -n foo -- sh # Request with a valid token -- expect 200 OK curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000 # Request with an invalid token -- expect 401 Unauthorized curl -I -H "Authorization: Bearer invalidtoken" httpbin.foo.svc.cluster.local:8000
Step 3: Create an AuthorizationPolicy
An AuthorizationPolicy restricts access to the httpbin service so that only requests with a valid, verified JWT are allowed. After you apply this policy, requests without a token are rejected with 403 Forbidden.
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
Click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy. Click Create from YAML.
Select foo from the Namespace drop-down list, enter the following YAML, and click Create. The
requestPrincipalsfield uses the format<iss>/<sub>. This policy allows requests only when the decoded JWT hasiss: testing@secure.istio.ioandsub: testing@secure.istio.io.apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: foo spec: selector: matchLabels: app: httpbin action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]Verify the policy from the
sleepPod: Unlike Step 2, requests without a token now return403 Forbiddenbecause theAuthorizationPolicyrequires a matching principal.Scenario Expected response Reason Valid token 200 OKToken matches the requestPrincipalsruleInvalid token 403 ForbiddenAuthorization policy denies the request No token 403 ForbiddenNo authenticated identity to match the ALLOW rule kubectl exec -it deploy/sleep -n foo -- sh # Valid token -- expect 200 OK TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg' curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000 # Invalid token -- expect 403 Forbidden curl -I -H "Authorization: Bearer invalidtoken" httpbin.foo.svc.cluster.local:8000
Step 4: Restrict access by JWT claims
Refine the authorization policy to check specific JWT claims beyond iss and sub. In this example, the policy requires the groups claim to contain group1.
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
Click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy.
Find the require-jwt policy and click YAML in the Actions column.
In the Edit dialog box, update the YAML to include a
whencondition and click OK. Thewhencondition checks thegroupsclaim in the decoded JWT. A request is allowed only if:issistesting@secure.istio.iosubistesting@secure.istio.iogroupscontainsgroup1
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: foo spec: selector: matchLabels: app: httpbin action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] when: - key: request.auth.claims[groups] values: ["group1"]Test with a token that includes the
groupsclaim. Sample token with group claims: Decode to inspect the claims: Output: Test from thesleepPod:Scenario Expected response Reason Token with group1200 OKAll conditions met Valid token without groupsclaim403 ForbiddenMissing required claim TOKEN_GROUP='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjM1MzczOTExMDQsImdyb3VwcyI6WyJncm91cDEiLCJncm91cDIiXSwiaWF0IjoxNTM3MzkxMTA0LCJpc3MiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyIsInNjb3BlIjpbInNjb3BlMSIsInNjb3BlMiJdLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.EdJnEZSH6X8hcyEii7c8H5lnhgjB5dwo07M5oheC8Xz8mOllyg--AHCFWHybM48reunF--oGaG6IXVngCEpVF0_P5DwsUoBgpPmK1JOaKN6_pe9sh0ZwTtdgK_RP01PuI7kUdbOTlkuUi2AO-qUyOm7Art2POzo36DLQlUXv8Ad7NBOqfQaKjE9ndaPWT7aexUsBHxmgiGbz1SyLH879f7uHYPbPKlpHU6P9S-DaKnGLaEchnoKnov7ajhrEhGXAQRukhDPKUHO9L30oPIr5IJllEQfHYtt6IZvlNUGeLUcif3wpry1R5tBXRicx2sXMQ7LyuDremDbcNy_iE76Upg'echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode - | jq{ "exp": 3537391104, "groups": [ "group1", "group2" ], "iat": 1537391104, "iss": "testing@secure.istio.io", "scope": [ "scope1", "scope2" ], "sub": "testing@secure.istio.io" }kubectl exec -it deploy/sleep -n foo -- sh # Token with group1 -- expect 200 OK curl -I -H "Authorization: Bearer $TOKEN_GROUP" httpbin.foo.svc.cluster.local:8000 # Original token (no groups claim) -- expect 403 Forbidden curl -I -H "Authorization: Bearer $TOKEN" httpbin.foo.svc.cluster.local:8000
What's next
For more information about JWT algorithms supported by ASM, using jwksUri for remote key fetching, and configuring specific paths to bypass JWT authentication, see FAQ about JWT.