Previously we focused on service to service security based on workload identity and configured policies to restrict access based on the Workspace in addition to direct SPIFFE identity. Now we will focus on creating end-user authentication and authroization policy based on a JWT token, ensuring that only properly authenticated users that have the right permissions can use the Market Data service
First, lets verify we can access the Market Data service via its external ingress endpoint:
curl -i https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
Even though request-level authentication was not provided, the call should succeed and return you a market quote.
This lab will demonstrate using Auth0 as an OIDC provider that will create JWT tokens for end user requests which will be authenticated and authorized at the Ingress Gateway or Application sidecar as a Policy Enforcement Point (PEP)
- Configure the
IngressGateway
for the market data service with requestauthentication
settings to enforce JWT validation usingtctl apply
:
envsubst < 07-app-security-jwt/01-jwt-auth.yaml | tctl apply -f -
Inspect the file 07-app-security-jwt/01-jwt-auth.yaml
. This is nearly identical to the gateway we configured in the previous lab. The important additional pieces are in the authentication
and authorization
sections:
authentication:
jwt:
issuer: https://dev-8s63qgb5.us.auth0.com/
jwksUri: https://dev-8s63qgb5.us.auth0.com/.well-known/jwks.json
authorization:
local:
rules:
- name: allow-all
from:
- jwt:
iss: https://dev-8s63qgb5.us.auth0.com/
sub: "*"
to:
- paths: ["*"]
In the authentication
section we are configuring how JWT tokens are going to be validated. There we configure the issuer for the tokens and the keystore with the keys to be used to validate the token signature.
In the authorization
section we configure the AuthZ rules we want to apply to incoming traffic. In this example we are allowing all request as long as the JWT token is valid and signed by the issuer.
- Verify that you no longer can invoke the market data service when you are unauthenticated:
- Test authentication when no JWT is provided:
curl -i https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
- Test using a JWT that is valid but not signed by issuer we configured:
export TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"
curl -i -H "Authorization: Bearer $TOKEN" https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
Both of these requests will result in an HTTP 401
or HTTP 403
response code.
Next we will obtain a valid JWT token from our OIDC provider, Auth0, and present this token to authenticate our request:
export TOKEN=$(curl --request POST \
--url 'https://dev-8s63qgb5.us.auth0.com/oauth/token' \
--data grant_type=password \
--data client_id=$CLIENT_ID \
--data client_secret=$CLIENT_SECRET \
--data [email protected] --data password=t3trat3! | jq -r '.id_token')
echo "JWT Token:"
echo $TOKEN
curl -i -H "Authorization: Bearer $TOKEN" https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
This time we receive a 200 reponse code along with our market data quote.
Our application ingress is now authenticating any valid JWT token from our OIDC provider. However, we do not have any policy for authorizing the correct users. Within TSB establishing this policy is an easy task.
- Configure the
IngressGateway
for the market data service with requestauthentication
andauthorization
policy to enforce JWT validation and rules based on the token claims usingtctl apply
:
envsubst < 07-app-security-jwt/02-jwt-authz.yaml | tctl apply -f -
Inspect the file 07-app-security-jwt/02-jwt-authz.yaml
. Focus in on the updated authorization
section:
authorization:
local:
rules:
- name: only-market-data
from:
- jwt:
iss: https://dev-8s63qgb5.us.auth0.com/
sub: "*"
other:
https://tetrate.io/role: market-data
to:
- paths: ["*"]
In the authorization
section we configure the AuthZ rules we want to apply to incoming traffic. In this example we will:
- Enforce the configured rule for all requests (
paths: ["*"]
). - Require tokens to be issued by the configured issuer.
- Require
https://tetrate.io/role
claim in the token to have the value ofmarket-data
.
Once again, invoke the service with a JWT token from the same user, [email protected]
:
export TOKEN=$(curl --request POST \
--url 'https://dev-8s63qgb5.us.auth0.com/oauth/token' \
--data grant_type=password \
--data client_id=$CLIENT_ID \
--data client_secret=$CLIENT_SECRET \
--data [email protected] --data password=t3trat3! | jq -r '.id_token')
echo "JWT Token:"
echo $TOKEN
curl -i -H "Authorization: Bearer $TOKEN" https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
This call will fail with an HTTP 403
response code and a message RBAC: Access Denied
. By decoding the JWT token we can verify that this user does not have the proper claim: https://tetrate.io/role: market-data
; instead the role is general
:
./07-app-security-jwt/jwt-decode.sh
...
{
"https://tetrate.io/role": "general",
"nickname": "plain-user",
"name": "[email protected]",
"picture": "https://s.gravatar.com/avatar/19edebeadd9274400dad859b6dba978d?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fpl.png",
"updated_at": "2022-02-11T20:07:23.844Z",
"email": "[email protected]",
"email_verified": false,
"iss": "https://dev-8s63qgb5.us.auth0.com/",
"sub": "auth0|6206b60059cf77006a55712b",
"aud": "pNfSwmLegMhf13oJOJWM7fmee2xL7qYN",
"iat": 1644610043,
"exp": 1644646043
}
Now obtain a JWT token from our OIDC providerfor a user with the proper claims. This time the API call will succeed:
export TOKEN=$(curl --request POST \
--url 'https://dev-8s63qgb5.us.auth0.com/oauth/token' \
--data grant_type=password \
--data client_id=$CLIENT_ID \
--data client_secret=$CLIENT_SECRET \ \
--data [email protected] --data password=t3trat3! | jq -r '.id_token')
echo "JWT Token:"
echo $TOKEN
curl -i -H "Authorization: Bearer $TOKEN" https://quotes.$PREFIX.workshop.cx.tetrate.info/quotes/GOOG
By decoding the JWT token we can verify that this user has the proper claim: https://tetrate.io/role: market-data
:
./07-app-security-jwt/jwt-decode.sh
...
{
"https://tetrate.io/role": "market-data",
"nickname": "market-data-user",
"name": "[email protected]",
"picture": "https://s.gravatar.com/avatar/a731eee74307d53bd2e2d945761dd5e3?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fma.png",
"updated_at": "2022-02-11T20:30:54.514Z",
"email": "[email protected]",
"email_verified": false,
"iss": "https://dev-8s63qgb5.us.auth0.com/",
"sub": "auth0|6206c73b59cf77006a5575b3",
"aud": "pNfSwmLegMhf13oJOJWM7fmee2xL7qYN",
"iat": 1644611454,
"exp": 1644647454
}