diff --git a/microservices-connector/config/authN-authZ/README.md b/microservices-connector/config/authN-authZ/README.md index 4212a9505..a64b91630 100644 --- a/microservices-connector/config/authN-authZ/README.md +++ b/microservices-connector/config/authN-authZ/README.md @@ -1,7 +1,8 @@ # Use GMC and Istio to compose an OPEA Pipeline with authentication and authorization enabled -Authentication and authorization are essential measures that ensure the secure operation of our GenAI workload. Currently we provide two options to implement the task : via bearer JWT token and via keycloak. -And here we use the chatQnA pipeline as an example. +In enterprise settings not only do we want to identify who is using a service but also what they are entitled to use. This is where authentication and authorization comes in. In contrast, API tokens provide full access by virtue of possession as long as they are valid/not expired. With that aside, lets dive into how we can accomplish AuthN and AuthZ in OPEA using Istio and JWT tokens. + +Currently we provide two options to implement the task : via fake JWT token and via JWT token generated by OIDC providers. And here we use the chatQnA pipeline as an example. ## Prerequisite @@ -30,6 +31,10 @@ kubectl patch deployment -n chatqa router-server --patch '{ ## Perform authentication and authorization via fake JWT tokens +Here provides the case of authentication and authorization using fake JWT tokens. Fake JWT tokens are generated through a sample script provided by Istio community. + +In this example, we setup rules that only users with JWT token issued by "testing@secure.istio.io" and with claim "groups" equal to "group1" can access the chatQnA workload. + **Apply authentication and authorization policies to the pipeline endpoint based on raw JWT tokens** ```sh @@ -78,17 +83,17 @@ kubectl exec -it -n chatqa $CLIENT_POD -- curl -X POST $accessUrl -d '{"text":"W # try with an invalid token. Shall get response: "RBAC: access denied 403" kubectl exec -it -n chatqa $CLIENT_POD -- curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKEN1" -H 'Content-Type: application/json' -w " %{http_code}\n" -# try with the valid token. Shall get the correct response -kubectl exec -it -n chatqa $CLIENT_POD -- curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKEN2" -H 'Content-Type: application/json' +# try with the valid token. Shall get the correct response with http code +kubectl exec -it -n chatqa $CLIENT_POD -- curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKEN2" -H 'Content-Type: application/json' -w " %{http_code}\n" ``` -## Perform authentication and authorization via JWT tokens generated by Keycloak +## Perform authentication and authorization via JWT tokens generated by OIDC provider -Keycloak is an open source identity and access management project to add authentication to applications and secure services with minimum effort. Here we leverage Keycloak to generate a JWT token. +An OpenID Connect (OIDC) provider is the service that can help handle authentication and authorization. It provides identity information for users in the form of ID tokens, which contain claims about the user's identity (like their name, email, and user ID). In this example, we choose Keycloak, which is an open source identity and access management project to add authentication to applications and secure services with minimum effort to generate and manage JWT tokens. User can select the OIDC providers including Github, Google, Azure and etc according to their needs. -In this sample, we are going to test with scenario that only privileged users can access our chatQnA service and ask questions. In this case, user `mary` who has the role `user` can access the chatQnA pipeline. And user `bob` with the role `viewer` will not be able to access the service. Of course, the other users without valid token cannot access the service. +In this sample, we are going to test with the scenario that only privileged users can access our chatQnA service and ask questions. In this case, user `mary` who has the role `user` can access the chatQnA pipeline. And user `bob` with the role `viewer` will not be able to access the service. Of course, the other users without valid token cannot access the service. -**Keycloak installation and configuration** +**Sample OIDC provider installation and configuration: Keycloak** Install Keycloak in the kubernetes cluster for user management. **Note:** Replace the admin password as your own in the keycloak_install.yaml file. @@ -116,7 +121,7 @@ The user management is done via Keycloak and the configuration steps look like t 4. Create a new user name as `mary` and another user as `bob`. Set passwords for both users (set 'Temporary' to 'Off'). Select Role mapping on the top, assign the `user` role to `mary` and assign the `viewer` role to `bob`. -**Apply authentication and authorization policies to the pipeline endpoint based on Keycloak** +**Apply authentication and authorization policies to the pipeline endpoint based on OIDC provider** Use the commands to apply the authentication and authorization rules. @@ -124,24 +129,26 @@ Use the commands to apply the authentication and authorization rules. # export the router service through istio ingress gateway kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_router_gateway.yaml -# use 'envsubst' to substitute envs in yaml. +# 'envsubst' is used to substitute envs in yaml. # use 'sudo apt-get install gettext-base' to install envsubst if it does not exist on your machine # apply the authentication and authorization rule -# these files will restrict user access with valid token (with valid issuer, username and sub) +# these files will restrict user access with valid token (with valid issuer, username and realm role) envsubst < $(pwd)/config/authN-authZ/chatQnA_authN_keycloak.yaml | kubectl -n chatqa apply -f - envsubst < $(pwd)/config/authN-authZ/chatQnA_authZ_keycloak.yaml | kubectl -n chatqa apply -f - ``` +User could customize the chatQnA_authZ_keycloak.yaml to reflect roles, groups or any other claims they defined in the OIDC provider for the user. + **Validate authentication and authorization with different JWT tokens** -Fetch ID tokens for the two different users. **Note:** Remember to replace the `password` in the curl url and turn off the all the 'Required actions' under the 'Authentication' section. +Fetch access tokens for the two different users. **Note:** These commands are samples for Keycloak. And remember to replace the `password` in the curl url and turn off the all the 'Required actions' under the 'Authentication' section in Keycloak. ```bash -# get id token for mary. Please replace the password inside the url. -export TOKENA=$(curl -X POST 'http://${KEYCLOAK_ADDR}/realms/istio/protocol/openid-connect/token' -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=istio' --data-urlencode 'username=mary' -d 'password=${PASSWORD}' -d 'scope=openid' -d 'response_type=id_token' | jq -r .id_token) +# get access token for mary. Please replace the password inside the url. +export TOKENA=$(curl -X POST 'http://${KEYCLOAK_ADDR}/realms/istio/protocol/openid-connect/token' -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=istio' --data-urlencode 'username=mary' -d 'password=${PASSWORD}' -d 'scope=openid' -d 'response_type=id_token' | jq -r .access_token) -# get id token for bob. Please replace the password inside the url. -export TOKENB=$(curl -X POST 'http://${KEYCLOAK_ADDR}/realms/istio/protocol/openid-connect/token' -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=istio' --data-urlencode 'username=bob' -d 'password=${PASSWORD}' -d 'scope=openid' -d 'response_type=id_token' | jq -r .id_token) +# get access token for bob. Please replace the password inside the url. +export TOKENB=$(curl -X POST 'http://${KEYCLOAK_ADDR}/realms/istio/protocol/openid-connect/token' -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=istio' --data-urlencode 'username=bob' -d 'password=${PASSWORD}' -d 'scope=openid' -d 'response_type=id_token' | jq -r .access_token) ``` Access the istio ingress gateway to reach the chatQnA service with different tokens. Follow the istio guide [here](https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports) to determine the ingress IP and ports. @@ -156,6 +163,6 @@ curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","para # try with token of bob. Shall get response: "RBAC: access denied 403" curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKENB" -H 'Content-Type: application/json' -w " %{http_code}\n" -# try with the valid token from mary. Shall get the correct response from LLM -curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKENA" -H 'Content-Type: application/json' +# try with the valid token from mary. Shall get the correct response from LLM with http code +curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","parameters":{"max_new_tokens":17, "do_sample": true}}' -sS -H "Authorization: Bearer $TOKENA" -H 'Content-Type: application/json' -w " %{http_code}\n" ``` diff --git a/microservices-connector/config/authN-authZ/chatQnA_authZ_keycloak.yaml b/microservices-connector/config/authN-authZ/chatQnA_authZ_keycloak.yaml index 177d4b826..eb0557a06 100644 --- a/microservices-connector/config/authN-authZ/chatQnA_authZ_keycloak.yaml +++ b/microservices-connector/config/authN-authZ/chatQnA_authZ_keycloak.yaml @@ -15,9 +15,9 @@ spec: - key: request.auth.claims[preferred_username] values: - 'mary' - - key: request.auth.claims[aud] + - key: request.auth.claims[realm_access][roles] values: - - 'istio' + - 'user' selector: matchLabels: app: router-service