-
Notifications
You must be signed in to change notification settings - Fork 6
istio and keycloak integration #32
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
base: main
Are you sure you want to change the base?
Changes from all commits
a2aaee8
271c635
18525e1
194c419
f0f2406
ea361a7
a41db94
0ea1b62
8560b18
1ef0ac0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| :toc: macro | ||
| toc::[] | ||
| :idprefix: | ||
| :idseparator: - | ||
| == Authentication with Keycloak and Istio | ||
|
|
||
| === Introducing Keycloak | ||
| https://www.keycloak.org/[Keycloak] is an opensource IAM (Identity and Access Management) solution with a broad set of features like SSO, authentication and authorization, social login, multifactor authentication etc. | ||
|
|
||
| === Introducing Istio | ||
| https://istio.io/latest/docs/concepts/[Istio] is an opensource _https://istio.io/latest/about/service-mesh/[Service Mesh]_ that helps organizations run distributed MS-based apps anywhere. | ||
| It contains both a control plane and a data plane (Envoy Sidecar). The control plane takes the desired configuration, and its view of the services, and dynamically programs the proxy servers, updating them as the rules or the environment changes. Its powerful features provide a uniform and more efficient way to secure, connect, and monitor services. | ||
|
|
||
| * *Traffic management*: managing flow of traffic among services, from external and simplifying configuration of service-level properties like circuit breakers, timeouts etc. | ||
| * *Observability*: generating detailed telemetry includes detailed metrics, distributed traces, and full access logs | ||
| * *Security*: providing strong identity, powerful policy, transparent TLS encryption, and among-services/external/RBAC authentication, authorization and audit (AAA) tools | ||
|
|
||
| == Setting up the Quarkus reference application with Istio and Keycloak | ||
|
|
||
| This is a step-by-step guide to setting up the Quarkus reference application in conjunction with Istio and Keycloak. This setup was tested in a local Kubernetes cluster in a WSL environment. To set up the environment, follow these instructions: https://1000kit.gitlab.io/guides/docs/dev-environment/wsl2-pure/. | ||
|
|
||
| === Create the Kubernetes cluster | ||
| To create the cluster, we use k3d. First create a local Docker registry. | ||
| ``` | ||
| k3d registry create registry --port 5000 | ||
| ``` | ||
| The next step is to create the cluster. Navigate to the folder with the reference application and run the following command in your WSL environment. | ||
| ``` | ||
| k3d cluster create -c k8s/cluster-setup.yaml --k3s-server-arg '--no-deploy=traefik' | ||
| ``` | ||
| This will create a local Kubernetes cluster with one master and two worker nodes within your WSL environment. By default, k3d creates a Traefik proxy as an ingress controller. Since we will use the Istio Ingress Gateway later, we do not need it here. | ||
|
Comment on lines
+23
to
+31
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we really commit ourselves to I am happy to be told otherwise but why dont we describe this directly with |
||
|
|
||
| === Install Istio | ||
| The first step is to install Istio. You can find a https://istio.io/latest/docs/setup/getting-started/[Getting Started] guide and some sample applications on the Istio homepage. + | ||
| Navigate in your home directory and execute the following commands: | ||
| ``` | ||
| curl -L https://istio.io/downloadIstio | sh - | ||
| cd istio-1.10.0 | ||
| export PATH=$PWD/bin:$PATH | ||
| ``` | ||
|
Comment on lines
+33
to
+40
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to "install" istio natively on our machine?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Further such instruction also does not seem stable and maintainable.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Further if we just recommend to follow specific installation instructions from the web, we could simply link them: This is already maintained and updated to the latest version. So we can link instead of copying. Otherwise we can think about full automation where it really makes sense or integration in devonfw-ide, etc. where suitable. |
||
|
|
||
| For this example we use the `default` configuration profile. This will install the Istio core components. We label the Kubernetes namespace 'default' to instruct Istio to inject the Envoy sidecar proxy. | ||
| ``` | ||
| istioctl install --set profile=default -y | ||
| kubectl label namespace default istio-injection=enabled | ||
| ``` | ||
|
|
||
| === Build the application and create a Docker image | ||
| Now we need to build the application and the corresponding Docker image. Use Maven to build the application. | ||
| ``` | ||
| mvn clean package | ||
GuentherJulian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
| If you want to create a native executable use: | ||
| ``` | ||
| mvn clean package -Pnative | ||
| ``` | ||
| If tests fail because of authorization configurations you can add `-Dmaven.test.skip=true` option to skip the tests. | ||
|
|
||
| Then we create a Docker image and push it into your local Docker registry, which was started by k3d. | ||
|
|
||
| ``` | ||
| docker build -f src/main/docker/Dockerfile.jvm . -t demo-quarkus | ||
| docker tag demo-quarkus k3d-registry:5000/demo-quarkus | ||
|
Comment on lines
+48
to
+63
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all this has nothing to do with the integration of keycloak and istio what this documentation should be about. |
||
| docker push k3d-registry:5000/demo-quarkus | ||
| ``` | ||
|
|
||
| === Deploy the application in Kubernetes | ||
| To deploy the application, apply the following files to your Kubernetes cluster. | ||
| ``` | ||
| kubectl apply -f k8s/postgres-deployment.yaml | ||
| kubectl apply -f k8s/postgres-service.yaml | ||
|
|
||
| kubectl apply -f k8s/deployment.yaml | ||
| kubectl apply -f k8s/service.yaml | ||
| ``` | ||
|
|
||
| You now should see a response from the application when using curl to reach the URL http://demo-quarkus.default:8080/animals. | ||
| ``` | ||
| kubectl exec $(kubectl get pod -l app=demo-quarkus -o jsonpath={.items..metadata.name}) -c demo-quarkus -- curl http://demo-quarkus.default:8080/animals | ||
| Response: {"totalElements":0,"number":0,"size":100,"totalPages":0,"stream":[]} | ||
| ``` | ||
|
|
||
| This only works within the Kubernetes cluster, as we use the Kubernetes service name to reach the application. To expose the application for access from outside we can use Istio Gateways: | ||
| ``` | ||
| kubectl apply -f k8s/istio/demo-quarkus-gateway.yaml | ||
| ``` | ||
| You should now able to open the URL http://demo-quarkus.localhost/animals in your browser. | ||
|
|
||
| === Deploy and configure Keycloak | ||
| We use Keycloak for identity management. Apply the following files to your cluster to start a Keycloak instance and expose it via Istio Ingress Gateway for access from outside. | ||
| ``` | ||
| kubectl apply -f k8s/keycloak/keycloak.yaml | ||
| kubectl apply -f k8s/keycloak/keycloak-gateway.yaml | ||
| ``` | ||
|
|
||
| You should be able to open http://keycloak-demo-quarkus.localhost/auth/ in the browser. Open the Keycloak administration console and log in with `admin` as username and password. + | ||
| Add a new realm by selecting the realm-export.json file. | ||
|
|
||
| Click on `Groups` and create a new group `User`. Then click on `Roles` and create the following roles: | ||
| ``` | ||
| admin, user, demo-quarkus.FindObject, demo-quarkus.SaveObject, demo-quarkus.DeleteObject | ||
| ``` | ||
| Now create a new user called `demo` and add it to the group you created. | ||
| Finally, open the client `demo-quarkus-cli`, click on the tab Mapper and add the group mapper to the client. Your Keyloak is now fully configured. | ||
|
|
||
| === Test the application | ||
| Now it is time to test the application. Run the following command: | ||
| ``` | ||
| kubectl exec $(kubectl get pod -l app=demo-quarkus -o jsonpath={.items..metadata.name}) -c demo-quarkus -- curl http://demo-quarkus.default:8080/animals | ||
| ``` | ||
| You should get a valid response from the application. But there are no animals in our database at the moment. So let's try to create an animal. To do this, run the following command: | ||
| ``` | ||
| kubectl exec $(kubectl get pod -l app=demo-quarkus -o jsonpath={.items..metadata.name}) -c demo-quarkus -- curl -H "Content-Type: application/json" --request POST --data '{"name": "dog", "basicInfo": "home pet", "numberOfLegs":4}' http://demo-quarkus.default:8080/animals -i | ||
| ``` | ||
| You will get an `401 Unauthorized` error message. This is because this operation is secured with the role `demo-quarkus.SaveObject`. You can only access this operation if you pass a valid JWT token in the request header. So add the role to the user in Keycloak and run the following command to get the token. | ||
| ``` | ||
| TOKEN=$(curl -d 'client_id=demo-quarkus-cli' -d 'username=demo' -d 'password=demo' -d 'grant_type=password' 'http://keycloak-demo-quarkus.localhost/auth/realms/demo-quarkus/protocol/openid-connect/token' | jq ".access_token" -r) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. request token with client secret too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You only need to pass the client secret if 'Access Type' of the Keycloak client is set to |
||
| ``` | ||
| Now you can call the operation again and this time pass the token: | ||
| ``` | ||
| kubectl exec $(kubectl get pod -l app=demo-quarkus -o jsonpath={.items..metadata.name}) -c demo-quarkus -- curl -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" --request POST --data '{"name": "dog", "basicInfo": "home pet", "numberOfLegs":4}' http://demo-quarkus.default:8080/animals -i | ||
| ``` | ||
| There is now an animal stored in the database. You can check this by displaying the list of animals again. + | ||
| To use the other methods implemented in the application to find and delete animals, you need to add the roles `demo-quarkus.FindObject` and `demo-quarkus.DeleteObject` to the user and get a new token. | ||
|
|
||
| === Authorization by Istio | ||
| You can also add authorization policies with Istio. Requests are then first validated by the Istio service mesh before being forwarded to the application. + | ||
| Add the authorization policy by applying the file `k8s/istio/authorization-policy.yaml` to your cluster. | ||
| ``` | ||
| kubectl apply -f k8s/istio/authorization-policy.yaml | ||
| ``` | ||
| Now try again to get the list of animals. You will get an `RBAC: access denied` error message. This is because the url http://demo-quarkus.default:8080/animals is now also protected by a Istio policy. You need to pass a valid JWT token with the role 'user'. So add the role 'user' to the user in keycloak, get a new token and try again. + | ||
| Now you should get a valid response. | ||
| ``` | ||
| kubectl exec $(kubectl get pod -l app=demo-quarkus -o jsonpath={.items..metadata.name}) -c demo-quarkus -- curl http://demo-quarkus.default:8080/animals -H "Authorization: Bearer $TOKEN" | ||
| ``` | ||
|
|
||
| === When to use which authorization? | ||
| Normally, the Istio authorisation policies are sufficient to provide standard role bases permissions. In this case, you do not need to validate the token again in the application. + | ||
| If you want to add further validation, such as checking the claims of the JWT token or dynamically adding permissions based on database entries, then it is better to add further authorization logic in the code of the application. | ||
|
|
||
| === Securing the gateways with HTTPS | ||
| At the moment, external traffic from the client to the service is not secured. Istio provides secure gateways to host the services on HTTPS using simple or mutual TLS. | ||
|
|
||
| First we generate a self-signed root certificate for the services. | ||
| ``` | ||
| openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=Demo Inc./CN=demo.com' -keyout root-cert.key -out root-cert.crt | ||
| ``` | ||
| In the next step we create a certificate and a private key for `demo-quarkus.localhost`. We first generate a private key and a CSR (Certificate Signing Request) and then use the root certificate to sign the CSR and create the certificate. | ||
| ``` | ||
| openssl req -out demo-quarkus.csr -newkey rsa:2048 -nodes -keyout demo-quarkus.key -subj "/CN=demo-quarkus.localhost/O=demo organization" | ||
| openssl x509 -req -days 365 -CA root-cert.crt -CAkey root-cert.key -set_serial 0 -in demo-quarkus.csr -out demo-quarkus.crt | ||
| ``` | ||
| Now we need to create a TLS secret with the generated key and certificate. | ||
| ``` | ||
| kubectl create -n istio-system secret tls demo-quarkus-credential --key=demo-quarkus.key --cert=demo-quarkus.crt | ||
| ``` | ||
| Apply the file `demo-quarkus-gateway-secure.yaml` to your cluster. The file defines a gateway listening on port 443 with a simple TLS protocol. The name of the credential in the yaml file must match the name of the secret you created earlier. | ||
| Now you should be able to reach the service and get the correct response by passing the root certificate in the curl command. | ||
| ``` | ||
| export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') | ||
| curl --resolve "demo-quarkus.localhost:443:$INGRESS_HOST" --cacert root-cert.crt "https://demo-quarkus.localhost:443/animals" | ||
| ``` | ||
| If you execute the command without the `--cacert` option, the command will fail because a secure connection cannot be established. You can use the `-v`(verbose) option of the curl command to get detailed information. | ||
| {empty} + | ||
| {empty} + | ||
|
|
||
| ==== Mututal TLS | ||
| In the next step, we extend our gateway to support https://en.wikipedia.org/wiki/Mutual_authentication[mutual TLS]. This may be necessary if you need to establish trust between the client and the server and vice versa. | ||
| To do this, change the TLS mode in the gateway file from `SIMPLE` to `MUTUAL` and apply it again to the Kubernetes cluster. Try the curl command from the previous step again. It will not work because the server now also expects a certificate from the client. | ||
|
|
||
| First delete the old tls secret. | ||
| ``` | ||
| kubectl -n istio-system delete secret demo-quarkus-credential | ||
| ``` | ||
| Now create a new secret. You must set the properties `tls.key` and `tls.crt`, and `ca.crt` to include the CA certificate (root certificate). | ||
| ``` | ||
| kubectl create -n istio-system secret generic demo-quarkus-credential --from-file=tls.key=demo-quarkus.key --from-file=tls.crt=demo-quarkus.crt --from-file=ca.crt=root-cert.crt | ||
| ``` | ||
| Create the client certificate and sign it with the root certificate. | ||
| ``` | ||
| openssl req -out client.csr -newkey rsa:2048 -nodes -keyout client.key -subj "/CN=client.com/O=client organization" | ||
| openssl x509 -req -days 365 -CA root-cert.crt -CAkey root-cert.key -set_serial 1 -in client.csr -out client.crt | ||
| ``` | ||
| When passing the client certificate and private key to the curl command, you should be able to see a valid response. | ||
| ``` | ||
| curl --resolve "demo-quarkus.localhost:443:$INGRESS_HOST" --cacert root-cert.crt --cert client.crt --key client.key "https://demo-quarkus.localhost:443/animals" | ||
| ``` | ||
|
|
||
| ==== Internal traffic | ||
| By default, a service's sidecar proxy is configured to accept both mTLS and non-mTLS traffic (`PERMISSIVE` mode). This can be changed by using a `PeerAuthentication` resource. Use `STRICT` mode to force traffic to use mTLS, or `DISABLE` mode where traffic must be in plain text. | ||
| ``` | ||
| kubectl apply -n foo -f - <<EOF | ||
| apiVersion: security.istio.io/v1beta1 | ||
| kind: PeerAuthentication | ||
| metadata: | ||
| name: default | ||
| spec: | ||
| mtls: | ||
| mode: DISABLE | ||
| EOF | ||
| ``` | ||
| You can use the Grafana dashboard to check whether plain text is sent or not. | ||
|
|
||
| == Istio dashboards | ||
| Istio provides various dashboards for monitoring and visualising different aspects of your Istio network. Create the monitor applications by applying the `addons` folder to your Kubernetes cluster. | ||
| ``` | ||
| kubectl apply -f k8s/istio/addons/ | ||
| ``` | ||
| Once the pods have been created, you can launch the dashboard application by running: | ||
| ``` | ||
| istioctl dashboard dashboardName | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from our experience in devon4j structuring documentation like this does not work out and is not maintainable.
Therefore my suggestion is: