Skip to main content

End User Auth and Authz with OpenShift Service Mesh and Keycloak

·5 mins

In this article, I will share the setup for enabling Authentication and Authorization in OpenShift Service Mesh with Keycloak.

Installing OpenShift Service Mesh #

Follow the Installing Red Hat OpenShift Service Mesh guide for setup

Enable the following configuration in your ServiceMeshControlPlane resource

  • Strict mTLS across the mesh
  • Automatic istio route creation
apiVersion: maistra.io/v2
kind: ServiceMeshControlPlane
spec:
  version: v1.1
  security:
    controlPlane:
      mtls: true
  gateways:
    OpenShiftRoute:
      enabled: true

Keycloak #

Keycloak is an open-source identity and access management application that uses open protocols and is easily integrated with other providers. It is the open-source project base of Red Hat Single Sign-on

Deploying Red Hat Single Sign-on #

The easiest way to deploy SSO is from the operator hub.

sso-operator

Follow the Keycloak identity provider article for adding a new security realm, client, role, user

Deploying Bookinfo example application #

Create a new namespace

oc new-project bookinfo

Edit the default Service Mesh Member Roll YAML and add bookinfo to the member’s list

apiVersion: maistra.io/v1
kind: ServiceMeshMemberRoll
metadata:
  name: default
spec:
  members:
  - bookinfo

From the CLI, deploy the Bookinfo application in the bookinfo project by applying the bookinfo.yaml file

oc apply -n bookinfo -f https://raw.githubusercontent.com/Maistra/istio/maistra-2.0/samples/bookinfo/platform/kube/bookinfo.yaml

Create the ingress gateway by applying the bookinfo-gateway.yaml file

oc apply -n bookinfo -f https://raw.githubusercontent.com/Maistra/istio/maistra-2.0/samples/bookinfo/networking/bookinfo-gateway.yaml

Set the value for the GATEWAY_URL parameter

oc get routes -n istio-system | grep bookinfo
export GATEWAY_URL=$(oc -n istio-system get route bookinfo-gateway-pl2rw -o jsonpath='{.spec.host}')

Adding default destination rules

I have enabled global mutual TLS in the control plane, so I’ll deploy the destination rule with all mtls

oc apply -n bookinfo -f https://raw.githubusercontent.com/Maistra/istio/maistra-2.0/samples/bookinfo/networking/destination-rule-all-mtls.yaml

Verifying the Bookinfo installation

Run the following command to confirm that the Bookinfo application is up and running

curl -o /dev/null -s -w "%{http_code}\n" http://$GATEWAY_URL/productpage

Authentication #

Enabling User-End Authentication #

Now it is time to enable end-user authentication.

The first thing you need to do is validate that it is possible to communicate between all services without authentication.

curl -k -o /dev/null -w "%{http_code}" http://$GATEWAY_URL/productpage
200

You can create the end-user authentication policy.

cat <<EOF | oc apply -n bookinfo -f -
apiVersion: authentication.istio.io/v1alpha1
kind: Policy
metadata:
  name: "productpage-jwt"
  namespace: "bookinfo"
spec:
  targets:
    - name: productpage
  peers:
    - mtls: {}
  origins:
    - jwt:
        issuer: "https://keycloak-sso.apps.amp01.lab.amp.aapaws/auth/realms/istio"
        jwksUri: "https://keycloak-sso.apps.amp01.lab.amp.aapaws/auth/realms/istio/protocol/openid-connect/certs"
        audiences:
          - customer
        triggerRules:
          - excludedPaths:
              - prefix: /healthz
  principalBinding: USE_ORIGIN
EOF

NOTE: If you see Origin authentication failed. after passing the correct access token and URL. Verify your istio-pilot pods for any x509: certificate signed by unknown authority; if that is the case, follow this workaround

Then let’s rerun the curl.

curl -k http://$GATEWAY_URL/productpage

And you will see something like

Origin authentication failed.

Set the value for the TOKEN parameter from keyclock

export TOKEN=$(curl -sk --data "username=demo&password=demo&grant_type=password&client_id=istio" https://keycloak-sso.apps.amp01.lab.amp.aapaws/auth/realms/istio/protocol/openid-connect/token | jq ".access_token")

Then let’s rerun the curl, this time with the token.

curl -k -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $TOKEN" http://$GATEWAY_URL/productpage
200

Authorization #

Create a deny-all policy in the namespace. The policy doesn’t have a selector field, which applies to every workload in the namespace. The spec: field in the policy has the empty value {}, this means that no traffic is permitted, effectively denying all requests

$ cat <<EOF | oc apply -n bookinfo -f -
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
spec:
  {}
EOF

Once the policy takes effect, verify that mesh rejected the curl connection to the workload.

$ curl -k -H "Authorization: Bearer $TOKEN" http://$GATEWAY_URL/productpage
RBAC: access denied

To give read access to the product page workload, create the policy that applies to the workload with label app: product page and allows users with roles customer to access it with all method.

$ cat <<EOF | oc apply -n bookinfo -f -
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: "productpage-authz"
  namespace: "bookinfo"
spec:
  selector:
    matchLabels:
      app: productpage
  rules:
    - to:
        - operation:
            methods: ["*"]
      when:
        - key: request.auth.claims[roles]
          values: ["customer"]
EOF

Wait for the newly defined policy to take effect.

After the policy takes effect, verify the connection to the httpbin workload succeeds.

curl -k  -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $TOKEN" http://$GATEWAY_URL/productpage
200

However, you can see the following errors on the page.

  • Error fetching product details
  • Error fetching product reviews on the page

Run the following command to create the details-viewer policy to allow the productpage workload, which issues requests using the cluster.local/ns/bookinfo/sa/bookinfo-productpage service account, to access the details workload via GET methods.

oc apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "details-viewer"
  namespace: bookinfo
spec:
  selector:
    matchLabels:
      app: details
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/bookinfo/sa/bookinfo-productpage"]
    to:
    - operation:
        methods: ["GET"]
EOF

Run the following command to create a policy reviews-viewer to allow the productpage workload, which issues requests using the cluster.local/ns/bookinfo/sa/bookinfo-productpage service account, to access the reviews workload through GET methods

oc apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "reviews-viewer"
  namespace: bookinfo
spec:
  selector:
    matchLabels:
      app: reviews
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/bookinfo/sa/bookinfo-productpage"]
    to:
    - operation:
        methods: ["GET"]
EOF

Point your browser at the Bookinfo productpage (http://$GATEWAY_URL/productpage). Now, you should see the “Bookinfo Sample” page with “Book Details” on the lower left part and “Book Reviews” on the lower right part.

However, in the “Book Reviews” section, you’ll see an error Rating service is currently unavailable. This error is because the reviews workload doesn’t permit access to the ratings workload. To fix this issue, you need to grant the reviews workload access to the ratings workload. Next, configure a policy to grant access to the reviews workload.

Run the following command to create the ratings-viewer policy to allow the reviews workload, which issues requests using the cluster.local/ns/bookinfo/sa/bookinfo-reviews service account, to access the ratings workload through GET methods

oc apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "ratings-viewer"
  namespace: bookinfo
spec:
  selector:
    matchLabels:
      app: ratings
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/bookinfo/sa/bookinfo-reviews"]
    to:
    - operation:
        methods: ["GET"]
EOF

Point your browser at the Bookinfo productpage (http://$GATEWAY_URL/productpage). You should see the “black” and “red” ratings in the “Book Reviews” section.