Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authN-authZ: change folder and split support #322

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# Use GMC and Istio to compose an OPEA Pipeline with authentication and authorization enabled
# Leveraging Istio to compose an OPEA Pipeline with authentication and authorization enabled

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, we first provide the solution on AuthN and AuthZ in OPEA using Istio and JWT tokens. Another option is to leverage the oauth2-proxy with various OIDC providers for authentication and authorization. Using oauth2-proxy with Istio ensures secure, scalable access control, centralizes user management, and provides seamless single sign-on capabilities, improving overall security and user experience in complex microservices environments.

Currently we provide three kinds of setups for authentication and authorization: via fake JWT token, via JWT token generated by OIDC providers and via oauth2-proxy and OIDC providers. And here we use the chatQnA pipeline as an example.

## Prerequisite

Before composing an OPEA pipeline with authN & authZ using GMC, user need to install Istio to support this feature. Please follow the steps [here](https://istio.io/latest/docs/setup/install/istioctl/) for installation.
Before composing an OPEA pipeline with authN & authZ, user need to install Istio to support this feature. Please follow the steps [here](https://istio.io/latest/docs/setup/install/istioctl/) for Istio installation.

**Deploy chatQnA GMC custom resource and enable Istio sidecar injection**
**Deploy chatQnA pipeline and enable Istio sidecar injection**

```sh
# make sure you are executing under the microservices-connector folder
kubectl create ns chatqa
kubectl apply -f $(pwd)/config/samples/chatQnA_xeon.yaml

# patch the router deployment to enable istio sidecar injection
kubectl patch deployment -n chatqa router-server --patch '{
# deploy ChatQnA pipeline. You can either leverage GMC or the ChatQnA helm chart.
kubectl create ns chatqa
# here's the command to leverage GMC custom resource for ChatQnA deployment.
kubectl apply -f $(pwd)/../microservices-connector/config/samples/chatQnA_xeon.yaml
# please refer the doc https://github.com/opea-project/GenAIInfra/tree/main/helm-charts/chatqna for deployment with helm chart.
# and install under `chatqa` namespace

# expose an environment variable to set the deployment method
# which will affect the configuration we use for authentication and authorization
export DEPLOY_METHOD=<your deploy method, valid values: "gmc-based" and "helm-chart-based">

# patch the deployment to enable istio sidecar injection
# for GMC based deployment, set the `deployment-name` to `router-service-deployment`
# for helm chart based deployment, set the `deployment-name` to `chatqna`
kubectl patch deployment -n chatqa <deployment-name> --patch '{
"spec": {
"template": {
"metadata": {
Expand All @@ -29,9 +39,9 @@ kubectl patch deployment -n chatqa router-server --patch '{
}'
```

The istio ingress gateway will be used to access the chatQnA service in different setups. 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.
The istio ingress gateway will be used to access the chatQnA service in different setups. 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 and expose them as environment variables.

## Perform authentication and authorization via Bearer JWT tokens
## Perform authentication and authorization via Bearer JWT tokens and curl

Authentication and authorization are essential for securing microservices architectures. Using Bearer JWT tokens for these processes ensures that only authenticated users with valid tokens can access specific services, protecting sensitive data. Authentication verifies user identity, while authorization controls their permissions. This layered approach not only prevents unauthorized access but also provides detailed control over service interactions, maintaining system security and compliance. Here we leverage Istio mechanisms together with Bearer JWT tokens to fulfill that.

Expand All @@ -46,13 +56,14 @@ In this example, we setup rules that only users with JWT token issued by "testin
**Apply authentication and authorization policies to the pipeline endpoint based on raw JWT tokens**

```sh
# make sure running under authN-authZ folder
# apply the yaml to request authentication using JWT token
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_authZ_fakejwt.yaml -n chatqa
kubectl apply -f $(pwd)/$(DEPLOY_METHOD)/chatQnA_authZ_fakejwt.yaml -n chatqa

# apply the yaml file to request that only JWT token with
# issuer & sub == "[email protected]" and groups belongs to group1
# can access the endpoint of chatQnA service
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_authN_fakejwt.yaml -n chatqa
kubectl apply -f $(pwd)/$(DEPLOY_METHOD)/chatQnA_authN_fakejwt.yaml -n chatqa
```

After applying these two yaml files, we have setup the policy that only user with a valid JWT token (with valid issuer and claims) could access the pipeline endpoint.
Expand Down Expand Up @@ -83,7 +94,12 @@ Deploy one client pod and test the chatQnA application with different tokens
kubectl create deployment client-test -n chatqa --image=python:3.8.13 -- sleep infinity

export CLIENT_POD=$(kubectl get pod -n chatqa -l app=client-test -o jsonpath={.items..metadata.name})
export accessUrl=$(kubectl get gmc -n chatqa -o jsonpath="{.items[?(@.metadata.name=='chatqa')].status.accessUrl}")

# export an accessUrl based on either GMC or helm chart based ChatQnA
if [ "${DEPLOY_METHOD}" = "gmc-based" ]; then
export accessUrl=$(kubectl get gmc -n chatqa -o jsonpath="{.items[?(@.metadata.name=='chatqa')].status.accessUrl}")
else
export accessUrl="http://chatqna.chatqa.svc.cluster.local:8888"

# try without 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 'Content-Type: application/json' -w " %{http_code}\n"
Expand All @@ -106,8 +122,9 @@ In this sample, we are going to test with the scenario that only privileged user
Install Keycloak in the kubernetes cluster for user management. **Note:** Replace the admin password as your own in the keycloak_install.yaml file.

```bash
# make sure running under authN-authZ folder
cd $(pwd)
kubectl apply -f $(pwd)/config/authN-authZ/keycloak_install.yaml
kubectl apply -f $(pwd)/keycloak_install.yaml

# get the ip and port to access keycloak.
export HOST_IP=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' | cut -d '/' -f3 | cut -d ':' -f1)
Expand Down Expand Up @@ -135,14 +152,14 @@ Use the commands to apply the authentication and authorization rules.

```bash
# export the router service through istio ingress gateway
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_router_gateway.yaml
kubectl apply -f $(pwd)/$(DEPLOY_METHOD)/chatQnA_router_gateway.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 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 -
envsubst < $(pwd)/$(DEPLOY_METHOD)/chatQnA_authN_keycloak.yaml | kubectl -n chatqa apply -f -
envsubst < $(pwd)/$(DEPLOY_METHOD)/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.
Expand Down Expand Up @@ -175,9 +192,9 @@ curl -X POST $accessUrl -d '{"text":"What is the revenue of Nike in 2023?","para
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"
```

## Perform authentication and authorization via oauth2-proxy and OIDC provider
## Perform authentication and authorization via oauth2-proxy and OIDC provider and UI

Another choice we have is using oauth2-proxy and OIDC providers. These two streamline authentication and authorization by handling user identity and access management. oauth2-Proxy acts as a gateway, integrating with OIDC providers to authenticate users and issue tokens. This setup ensures secure access to applications by validating user credentials and managing permissions, simplifying the implementation of robust security protocols across services.
Another choice we have is using oauth2-proxy and OIDC providers. These two streamline authentication and authorization by handling user identity and access management. oauth2-proxy acts as a gateway, integrating with OIDC providers to authenticate users and issue tokens. This setup ensures secure access to applications by validating user credentials and managing permissions, simplifying the implementation of robust security protocols across services.

<img src="./docs/OPEA auth flow with oauth2-proxy.png" width="700" height="400">

Expand All @@ -188,8 +205,9 @@ We are using a similar scenario here that only privileged users can access our c
Here we take Keycloak as the sample OIDC Provider to use in the example. Follow the steps to install and configure Keycloak.

```bash
# make sure running under authN-authZ folder
cd $(pwd)
kubectl apply -f $(pwd)/config/authN-authZ/keycloak_install.yaml
kubectl apply -f $(pwd)/keycloak_install.yaml

# get the ip and port to access keycloak.
export HOST_IP=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' | cut -d '/' -f3 | cut -d ':' -f1)
Expand Down Expand Up @@ -245,7 +263,7 @@ export CLIENT_SECRET=<YOUR_CLIENT_SECRET>
# Using bash here. More methods found here:
# https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview#generating-a-cookie-secret
export COOKIE_SECRET=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_' ; echo)
envsubst < $(pwd)/config/authN-authZ/oauth2_install.yaml | kubectl apply -f -
envsubst < $(pwd)/oauth2_install.yaml | kubectl apply -f -
```

**Expose the pipeline endpoint through Istio Ingressgateway and install chatQnA UI**
Expand All @@ -254,21 +272,19 @@ Here we expose the chatQnA endpoint through the ingress gateway and then install

```bash
# expose chatqna endpoint
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_router_gateway_oauth.yaml
# build chatqna UI image
kubectl apply -f $(pwd)/$(DEPLOY_METHOD)/chatQnA_router_gateway_oauth.yaml
# build chatqna UI image if not exist on your machine
git clone https://github.com/opea-project/GenAIExamples.git
cd GenAIExamples/ChatQnA/docker/ui/
export BACKEND_SERVICE_ENDPOINT="http://chatqna-service.com:${INGRESS_PORT}/"
export DATAPREP_SERVICE_ENDPOINT="http://chatqna-service.com:${INGRESS_PORT}/dataprep"
docker build --no-cache -t opea/chatqna-conversation-ui:latest --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy --build-arg BACKEND_SERVICE_ENDPOINT=$BACKEND_SERVICE_ENDPOINT --build-arg DATAPREP_SERVICE_ENDPOINT=$DATAPREP_SERVICE_ENDPOINT -f ./docker/Dockerfile.react .
docker build --no-cache -t opea/chatqna-conversation-ui:latest --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy -f ./docker/Dockerfile.react .
# inject image to containerd repo
docker save -o ui.tar opea/chatqna-conversation-ui:latest
sudo ctr -n k8s.io image import ui.tar
# install chatqna conversation UI
cd && cd GenAIInfra/microservices-connector
helm install chatqna-ui ../helm-charts/common/chatqna-ui
cd && cd GenAIInfra
helm install chatqna-ui $(pwd)/helm-charts/common/chatqna-ui
# expose ui service outside
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_ui_gateway.yaml
kubectl apply -f $(pwd)/chatQnA_ui_gateway.yaml
```

**Add authentication and authorization rules to the pipeline through Istio Ingress Gateway**
Expand All @@ -277,13 +293,13 @@ Here we apply the authentication and authorization rules.

```bash
# Before applying the authorization rule, need to add the oauth2-proxy as the external authorization provider
kubectl apply -f $(pwd)/config/authN-authZ/chatQnA_istio_external_auth.yaml
kubectl apply -f $(pwd)/chatQnA_istio_external_auth.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 group and role)
envsubst < $(pwd)/config/authN-authZ/chatQnA_authN_oauth.yaml | kubectl apply -f -
envsubst < $(pwd)/config/authN-authZ/chatQnA_authZ_oauth.yaml | kubectl apply -f -
envsubst < $(pwd)/chatQnA_authN_oauth.yaml | kubectl apply -f -
envsubst < $(pwd)/chatQnA_authZ_oauth.yaml | kubectl apply -f -
```

**Validate authentication and authorization with UI service**
Expand Down
14 changes: 14 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_authN_fakejwt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: fake-jwt-example
spec:
jwtRules:
- issuer: [email protected]
jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.22/security/tools/jwt/samples/jwks.json
selector:
matchLabels:
app: chatqna
15 changes: 15 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_authN_keycloak.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: jwt-keycloak
spec:
jwtRules:
- issuer: http://${KEYCLOAK_ADDR}/realms/${REALM}
jwksUri: http://${KEYCLOAK_ADDR}/realms/${REALM}/protocol/openid-connect/certs
outputPayloadToHeader: jwt-parsed
selector:
matchLabels:
app: chatqna
21 changes: 21 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_authZ_fakejwt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: fake-jwt-example
spec:
action: ALLOW
rules:
- from:
- source:
requestPrincipals:
- [email protected]/[email protected]
when:
- key: request.auth.claims[groups]
values:
- group1
selector:
matchLabels:
app: chatqna
23 changes: 23 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_authZ_keycloak.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: router
spec:
action: ALLOW
rules:
- when:
- key: request.auth.claims[iss]
values:
- 'http://${KEYCLOAK_ADDR}/realms/istio'
- key: request.auth.claims[preferred_username]
values:
- 'mary'
- key: request.auth.claims[realm_access][roles]
values:
- 'user'
selector:
matchLabels:
app: chatqna
34 changes: 34 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_router_gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: router-gateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: chatqna-router
namespace: chatqa
spec:
gateways:
- default/router-gateway
hosts:
- '*'
http:
- route:
- destination:
host: chatqna
port:
number: 8888
39 changes: 39 additions & 0 deletions authN-authZ/helm-chart-based/chatQnA_router_gateway_oauth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

---
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: chatqna-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- chatqna-service.com
port:
name: http
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: chatqna-virtual-service
namespace: istio-system
spec:
gateways:
- istio-system/chatqna-gateway
hosts:
- chatqna-service.com
http:
- match:
- uri:
prefix: /
route:
- destination:
host: chatqna.chatqa.svc.cluster.local
port:
number: 8888
2 changes: 1 addition & 1 deletion microservices-connector/usage_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,4 @@ No resources found in chatqa namespace.

## Use GMC and Istio to compose an OPEA Pipeline with authentication and authorization enabled

The critical steps of authentication and authorization are vital to maintaining the integrity and safety of our GenAI workload. Please check the [readme](config/authN-authZ/README.md) file for more details.
The critical steps of authentication and authorization are vital to maintaining the integrity and safety of our GenAI workload. Please check the [readme](../authN-authZ/README.md) file for more details.
Loading