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

fix: add authorizationGroups property to K8s SAR authorization, fixes #506 #507

Merged
merged 5 commits into from
Nov 21, 2024

Conversation

dhirajsb
Copy link
Contributor

No description provided.

@dhirajsb
Copy link
Contributor Author

@guicassolato I was thinking back to an authorization requirement we had in managed openshift connectors, where we were authorizing against an org field in JWT, and a groups claim.
I'm wondering whether we should support the following scenarios as well:

  • Groups selector could resolve to a single scalar string
  • Multiple groups selectors are needed to aggregate a list of groups

@guicassolato
Copy link
Collaborator

@guicassolato I was thinking back to an authorization requirement we had in managed openshift connectors, where we were authorizing against an org field in JWT, and a groups claim. I'm wondering whether we should support the following scenarios as well:

* Groups selector could resolve to a single scalar string

* Multiple groups selectors are needed to aggregate a list of groups

@dhirajsb, I think it should be straightforward and match the SAR API, i.e., authorizationGroups always resolves to an array of strings.

For cases like the one you described, one can always use CEL. E.g., the following resolves to a list containing a single string equal to the org name used as group.

authorizationGroups:
  expression: [auth.identity.org_name]

Because it's CEL, we can do fancy stuff. E.g.: [auth.identity.org_name] + auth.identity.groups.

guicassolato
guicassolato previously approved these changes Nov 19, 2024
@dhirajsb
Copy link
Contributor Author

Because it's CEL, we can do fancy stuff. E.g.: [auth.identity.org_name] + auth.identity.groups.

I was thinking about CEL after I wrote that comment, but wasn't sure how capable it was. Good to know we can cover fairly complex use cases with it. 👍

guicassolato
guicassolato previously approved these changes Nov 20, 2024
Signed-off-by: Dhiraj Bokde <[email protected]>
Copy link
Collaborator

@guicassolato guicassolato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, @dhirajsb! Thank you so much for this!

Leaving here a few verification steps for the record. We should probably add some docs too at some point.

Verification steps

Setup the environment:

make local-setup DEPLOY_KEYCLOAK=1 FF=1
kubectl port-forward deployment/envoy 8000:8000 2>&1 >/dev/null &
kubectl port-forward deployment/keycloak 8080:8080 2>&1 >/dev/null &

In the Keycloak Admin UI (http://keycloak.127.0.0.1.nip.io:8080 | admin:p):

  1. Define a talker-api-members role in the demo client of the kuadrant realm
  2. Create a Token Mapper of 'User Client Role' type to set the groups token claim
  3. Map the realm user john to the demo:talker-api-members role

Create permissions in the Kubernetes RBAC:

kubectl apply -f -<<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: reader
rules:
- apiGroups: ["talker-api.127.0.0.1.nip.io"]
  resources: ["messages"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: readers
subjects:
- kind: Group
  name: talker-api-members
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: reader
  apiGroup: rbac.authorization.k8s.io
EOF

Try the deprecated API

Create the AuthConfig:

kubectl apply -f -<<EOF
apiVersion: authorino.kuadrant.io/v1beta3
kind: AuthConfig
metadata:
  name: talker-api-protection
spec:
  hosts:
  - talker-api.127.0.0.1.nip.io
  authentication:
    keycloak:
      jwt:
        issuerUrl: http://keycloak.default.svc.cluster.local:8080/realms/kuadrant
  authorization:
    "k8s-rbac":
      kubernetesSubjectAccessReview:
        groups: # <== this will grant access to all requests given the rolebinding created before; not specifying this would require users to be bound directly to the role (not the group)
        - talker-api-members
        user:
          expression: auth.identity.preferred_username
        resourceAttributes:
          group:
            value: talker-api.127.0.0.1.nip.io
          resource:
            value: messages
          namespace:
            value: default
          verb:
            expression: request.method.lowerAscii()
EOF

Consumer the API as a user who is a member of talker-api-members:

ACCESS_TOKEN=$(kubectl run token --attach --rm --restart=Never -q --image=curlimages/curl -- http://keycloak.default.svc.cluster.local:8080/realms/kuadrant/protocol/openid-connect/token -s -d 'grant_type=password' -d 'client_id=demo' -d 'username=john' -d 'password=p' -d 'scope=openid' | jq -r .access_token)
curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api.127.0.0.1.nip.io:8000 -i
# HTTP/1.1 200 OK (due to the SAR request with static groups)

Consume the API as a user who is not a member of talker-api-members:

ACCESS_TOKEN=$(kubectl run token --attach --rm --restart=Never -q --image=curlimages/curl -- http://keycloak.default.svc.cluster.local:8080/realms/kuadrant/protocol/openid-connect/token -s -d 'grant_type=password' -d 'client_id=demo' -d 'username=jane' -d 'password=p' -d 'scope=openid' | jq -r .access_token)
curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api.127.0.0.1.nip.io:8000 -i
# HTTP/1.1 200 OK (due to the SAR request with static groups)

Try the new API

Create the AuthConfig:

kubectl apply -f -<<EOF
apiVersion: authorino.kuadrant.io/v1beta3
kind: AuthConfig
metadata:
  name: talker-api-protection
spec:
  hosts:
  - talker-api.127.0.0.1.nip.io
  authentication:
    keycloak:
      jwt:
        issuerUrl: http://keycloak.default.svc.cluster.local:8080/realms/kuadrant
  authorization:
    "k8s-rbac":
      kubernetesSubjectAccessReview:
        authorizationGroups: # <== checks whether any of the actual groups the user is a member of has permission
          expression: auth.identity.groups
        user:
          expression: auth.identity.preferred_username
        resourceAttributes:
          group:
            value: talker-api.127.0.0.1.nip.io
          resource:
            value: messages
          namespace:
            value: default
          verb:
            expression: request.method.lowerAscii()
EOF

Consume the API as a user who is a member of talker-api-members:

ACCESS_TOKEN=$(kubectl run token --attach --rm --restart=Never -q --image=curlimages/curl -- http://keycloak.default.svc.cluster.local:8080/realms/kuadrant/protocol/openid-connect/token -s -d 'grant_type=password' -d 'client_id=demo' -d 'username=john' -d 'password=p' -d 'scope=openid' | jq -r .access_token)
curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api.127.0.0.1.nip.io:8000 -i
# HTTP/1.1 200 OK

Consumer the API as a user who is not a member of talker-api-members:

ACCESS_TOKEN=$(kubectl run token --attach --rm --restart=Never -q --image=curlimages/curl -- http://keycloak.default.svc.cluster.local:8080/realms/kuadrant/protocol/openid-connect/token -s -d 'grant_type=password' -d 'client_id=demo' -d 'username=jane' -d 'password=p' -d 'scope=openid' | jq -r .access_token)
curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api.127.0.0.1.nip.io:8000 -i
# HTTP/1.1 403 Forbidden

@guicassolato guicassolato merged commit a1d035d into Kuadrant:main Nov 21, 2024
8 checks passed
@dhirajsb dhirajsb deleted the fix/k8s-authz-groups branch November 21, 2024 15:32
@dhirajsb
Copy link
Contributor Author

Thanks for testing a built version @guicassolato . I'm also working on testing this locally with a modified model registry. I'll let you know if I run into any issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants