diff --git a/docs/features.md b/docs/features.md
index 4f3318ca..2bae5354 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -355,30 +355,69 @@ Authorino's built-in OPA module precompiles the policies in reconciliation-time
### Kubernetes SubjectAccessReview ([`authorization.kubernetes`](https://pkg.go.dev/github.com/kuadrant/authorino/api/v1beta1?utm_source=gopls#Authorization_KubernetesAuthz))
-Access control enforcement based on rules defined in the Kubernetes authorization system (e.g. as `ClusterRole` and `ClusterRoleBinding` resources of Kubernetes RBAC authorization).
+Access control enforcement based on rules defined in the Kubernetes authorization system, i.e. `Role`, `ClusterRole`, `RoleBinding` and `ClusterRoleBinding` resources of Kubernetes RBAC.
-Authorino issues a [SubjectAccessReview](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/subject-access-review-v1) inquiry checking with the underlying Kubernetes cluster whether the user can access the requested API resouce. It can be used with `resourceAttributes` or `nonResourceAttributes` (the latter inferring HTTP verb and method from the original request).
+Authorino issues a [SubjectAccessReview](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/subject-access-review-v1) (SAR) inquiry that checks with the underlying Kubernetes server whether the user can access a particular resource, resurce kind or generic URL.
-A Kubernetes authorization policy config looks like the following in an Authorino `AuthConfig`:
+It supports **resource attributes authorization check** (parameters defined in the `AuthConfig`) and **non-resource attributes authorization check** (HTTP endpoint inferred from the original request).
+- Resource attributes: adequate for permissions set at namespace level, defined in terms of common attributes of operations on Kubernetes resources (namespace, API group, kind, name, subresource, verb)
+- Non-resource attributes: adequate for permissions set at cluster scope, defined for protected endpoints of a generic HTTP API (URL path + verb)
+
+Example of Kubernetes role for resource attributes authorization:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: pet-reader
+rules:
+- apiGroups: ["pets.io"]
+ resources: ["pets"]
+ verbs: ["get"]
+```
+
+Example of Kubernetes cluster role for non-resource attributes authorization:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: pet-editor
+rules:
+- nonResourceURLs: ["/pets/*"]
+ verbs: ["put", "delete"]
+```
+
+Kubernetes authorization policy configs look like the following in an Authorino `AuthConfig`:
```yaml
authorization:
- name: kubernetes-rbac
kubernetes:
user:
- valueFrom: # It can be a fixed value as well, by using `value` instead
+ valueFrom: # values of the parameter can be fixed (`value`) or fetched from the Auhtorization JSON (`valueFrom.authJSON`)
authJSON: auth.identity.metadata.annotations.userid
- groups: [] # User groups to test for.
+ groups: [] # user groups to test for.
- resourceAttributes: # Omit it to perform a non-resource `SubjectAccessReview` based on the request's path and method (verb) instead
- namespace: # other supported resource attributes are: group, resource, name, subresource and verb
+ # for resource attributes permission checks; omit it to perform a non-resource attributes SubjectAccessReview with path and method/verb assumed from the original request
+ # if included, use the resource attributes, where the values for each parameter can be fixed (`value`) or fetched from the Auhtorization JSON (`valueFrom.authJSON`)
+ resourceAttributes:
+ namespace:
value: default
+ group:
+ value: pets.io # the api group of the protected resource to be checked for permissions for the user
+ resource:
+ value: pets # the resource kind
+ name:
+ valueFrom: { authJSON: context.request.http.path.@extract:{"sep":"/","pos":2} } # resource name – e.g., the {id} in `/pets/{id}`
+ verb:
+ valueFrom: { authJSON: context.request.http.method.@case:lower } # api operation – e.g., copying from the context to use the same http method of the request
```
-`user` and `resourceAttributes` can be specified as a fixed value or patterns to fetch from the Authorization JSON.
+`user` and properties of `resourceAttributes` can be defined from fixed values or patterns of the Authorization JSON.
-An array of required `groups` can as well be specified and it will be used in the `SubjectAccessReview`.
+An array of `groups` (optional) can as well be set. When defined, it will be used in the `SubjectAccessReview` request.
### Keycloak Authorization Services (UMA-compliant Authorization API)
diff --git a/docs/user-guides/kubernetes-subjectaccessreview.md b/docs/user-guides/kubernetes-subjectaccessreview.md
index 6a7295e5..a9bd750b 100644
--- a/docs/user-guides/kubernetes-subjectaccessreview.md
+++ b/docs/user-guides/kubernetes-subjectaccessreview.md
@@ -8,14 +8,12 @@ Manage permissions in the Kubernetes RBAC and let Authorino to check them in req
- Authorino can delegate authorization decision to the Kubernetes authorization system, allowing permissions to be stored and managed using the Kubernetes Role-Based Access Control (RBAC) for example. The feature is based on the `SubjectAccessReview` API and can be used for `resourceAttributes` or `nonResourceAttributes` (the latter inferring HTTP verb and method from the original request).
+ Authorino can delegate authorization decision to the Kubernetes authorization system, allowing permissions to be stored and managed using the Kubernetes Role-Based Access Control (RBAC) for example. The feature is based on the `SubjectAccessReview` API and can be used for `resourceAttributes` (parameters defined in the `AuthConfig`) or `nonResourceAttributes` (inferring HTTP path and verb from the original request).
- Check out as well the user guides about [Authentication with Kubernetes tokens (TokenReview API)](./kubernetes-tokenreview.md), [Authentication with API keys](./api-key-authentication.md) and [Token normalization](./token-normalization.md).
+ Check out as well the user guide about [Authentication with Kubernetes tokens (TokenReview API)](./kubernetes-tokenreview.md).
For further details about Authorino features in general, check the [docs](./../features.md).
@@ -93,13 +91,7 @@ kubectl -n authorino port-forward deployment/envoy 8000:8000 &
## 6. Create the `AuthConfig`
-The `AuthConfig` below defines:
-- 2 sets of identities trusted to access the API:
- - users that authenticate with API keys (**`api-key-users`**), and
- - service accounts that authenticate with Kubernetes service account tokens (**`service-accounts`**);
-- 2 authorization policies based on Kubernetes SubjectAccessReview:
- - resource access reviews when the requested endpoint matches `/resources(/\w+)?` (**`resource-endpoints`**), and
- - non-resource access reviews when the requested endpoint does not match `/resources(/\w+)?` (**`non-resource-endpoints`**)
+The `AuthConfig` below sets all Kubernetes service accounts as trusted users of the API, and relies on the Kubernetes RBAC to enforce authorization using Kubernetes SubjectAccessReview API for non-resource endpoints:
```sh
kubectl -n authorino apply -f -< /tmp/kind-cluster-user-cert.pem
-yq r ~/.kube/config "users(name==$CURRENT_K8S_USER).user.client-key-data" | base64 -d > /tmp/kind-cluster-user-cert.key
-```
-
-Use the Kubernetes user's client TLS certificate to obtain a short-lived access token for the `api-consumer-1` `ServiceAccount`:
-
-```sh
-export ACCESS_TOKEN=$(curl -k -X "POST" "$KUBERNETES_API/api/v1/namespaces/authorino/serviceaccounts/api-consumer-1/token" \
- --cert /tmp/kind-cluster-user-cert.pem --key /tmp/kind-cluster-user-cert.key \
- -H 'Content-Type: application/json; charset=utf-8' \
- -d $'{ "apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest", "spec": { "audiences": ["talker-api"], "expirationSeconds": 600 } }' | jq -r '.status.token')
+Run a pod that consumes one of the greeting endpoints of the API from inside the cluster, as service account `api-consumer-1`, bound to the `talker-api-greeter` and `talker-api-speaker` cluster roles in the Kubernetes RBAC:
+
+```sh
+kubectl -n authorino run greeter --attach --rm --restart=Never -q --image=quay.io/3scale/authorino-examples:api-consumer --overrides='{
+ "apiVersion": "v1",
+ "spec": {
+ "containers": [{
+ "name": "api-consumer", "image": "quay.io/3scale/authorino-examples:api-consumer", "command": ["./run"],
+ "args":["--endpoint=http://envoy.authorino.svc.cluster.local:8000/hi","--method=POST","--interval=0","--token-path=/var/run/secrets/tokens/api-token"],
+ "volumeMounts": [{"mountPath": "/var/run/secrets/tokens","name": "access-token"}]
+ }],
+ "serviceAccountName": "api-consumer-1",
+ "volumes": [{"name": "access-token","projected": {"sources": [{"serviceAccountToken": {"path": "api-token","expirationSeconds": 7200}}]}}]
+ }
+}' -- sh
+# Sending...
+# 200
+```
+
+Run a pod that sends a `POST` request to `/say/blah` from within the cluster, as service account `api-consumer-1`:
+
+```sh
+kubectl -n authorino run speaker --attach --rm --restart=Never -q --image=quay.io/3scale/authorino-examples:api-consumer --overrides='{
+ "apiVersion": "v1",
+ "spec": {
+ "containers": [{
+ "name": "api-consumer", "image": "quay.io/3scale/authorino-examples:api-consumer", "command": ["./run"],
+ "args":["--endpoint=http://envoy.authorino.svc.cluster.local:8000/say/blah","--method=POST","--interval=0","--token-path=/var/run/secrets/tokens/api-token"],
+ "volumeMounts": [{"mountPath": "/var/run/secrets/tokens","name": "access-token"}]
+ }],
+ "serviceAccountName": "api-consumer-1",
+ "volumes": [{"name": "access-token","projected": {"sources": [{"serviceAccountToken": {"path": "api-token","expirationSeconds": 7200}}]}}]
+ }
+}' -- sh
+# Sending...
+# 200
+```
+
+Run a pod that sends a `POST` request to `/say/blah` from within the cluster, as service account `api-consumer-2`, bound only to the `talker-api-greeter` cluster role in the Kubernetes RBAC:
+
+```sh
+kubectl -n authorino run speaker --attach --rm --restart=Never -q --image=quay.io/3scale/authorino-examples:api-consumer --overrides='{
+ "apiVersion": "v1",
+ "spec": {
+ "containers": [{
+ "name": "api-consumer", "image": "quay.io/3scale/authorino-examples:api-consumer", "command": ["./run"],
+ "args":["--endpoint=http://envoy.authorino.svc.cluster.local:8000/say/blah","--method=POST","--interval=0","--token-path=/var/run/secrets/tokens/api-token"],
+ "volumeMounts": [{"mountPath": "/var/run/secrets/tokens","name": "access-token"}]
+ }],
+ "serviceAccountName": "api-consumer-2",
+ "volumes": [{"name": "access-token","projected": {"sources": [{"serviceAccountToken": {"path": "api-token","expirationSeconds": 7200}}]}}]
+ }
+}' -- sh
+# Sending...
+# 403
```
-Consume the API as `api-consumer-1`, which is bound to the `talker-api-greeter`, `talker-api-speaker` and `talker-api-resource-reader` roles in the Kubernetes RBAC:
-
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/hello
-# HTTP/1.1 200 OK
-```
+
+ Extra: consume the API as service account api-consumer-2
from outside the cluster
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/say/happy-to-be-here
-# HTTP/1.1 200 OK
-```
+
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api-authorino.127.0.0.1.nip.io:8000/resources/123
-# HTTP/1.1 200 OK
-```
+ To obtain access tokens to consume the API as service accounts from outside the cluster, start by proxying requests to the Kubernetes API:
-Use the Kubernetes user's client TLS certificate to obtain a short-lived access token for the `api-consumer-2` `ServiceAccount`:
+ ```sh
+ kubectl proxy --port=8181 # holds the shell
+ ```
-```sh
-export ACCESS_TOKEN=$(curl -k -X "POST" "$KUBERNETES_API/api/v1/namespaces/authorino/serviceaccounts/api-consumer-2/token" \
- --cert /tmp/kind-cluster-user-cert.pem --key /tmp/kind-cluster-user-cert.key \
- -H 'Content-Type: application/json; charset=utf-8' \
- -d $'{ "apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest", "spec": { "audiences": ["talker-api"], "expirationSeconds": 600 } }' | jq -r '.status.token')
-```
+ Then, obtain a short-lived access token for service account `api-consumer-2`, bound to the `talker-api-greeter` cluster role in the Kubernetes RBAC, using the Kubernetes TokenRequest API:
-Consume the API as `api-consumer-2`, which is bound to the `talker-api-greeter` role in the Kubernetes RBAC:
+ ```sh
+ ACCESS_TOKEN=$(curl -k -X "POST" "http://localhost:8181/api/v1/namespaces/authorino/serviceaccounts/api-consumer-2/token" \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -d $'{ "apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest", "spec": { "expirationSeconds": 600 } }' | jq -r '.status.token')
+ ```
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/hey
-# HTTP/1.1 200 OK
-```
+ Consume the API as `api-consumer-2` from outside the cluster:
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/say/something -i
-# HTTP/1.1 403 Forbidden
-# x-ext-auth-reason: Not authorized: unknown reason
-```
+ ```sh
+ curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/hello -i
+ # HTTP/1.1 200 OK
+ ```
-```sh
-curl -H "Authorization: Bearer $ACCESS_TOKEN" http://talker-api-authorino.127.0.0.1.nip.io:8000/resources/123 -i
-# HTTP/1.1 403 Forbidden
-# x-ext-auth-reason: Not authorized: unknown reason
-```
+ ```sh
+ curl -H "Authorization: Bearer $ACCESS_TOKEN" -X POST http://talker-api-authorino.127.0.0.1.nip.io:8000/say/something -i
+ # HTTP/1.1 403 Forbidden
+ ```
+
## Cleanup
diff --git a/docs/user-guides/kubernetes-tokenreview.md b/docs/user-guides/kubernetes-tokenreview.md
index c2ecf323..b0a1f44b 100644
--- a/docs/user-guides/kubernetes-tokenreview.md
+++ b/docs/user-guides/kubernetes-tokenreview.md
@@ -123,23 +123,16 @@ EOF
## 8. Consume the API from outside the cluster
-Get the Kubernetes API base endpoint and current Kubernetes user, and save the user's TLS certificate and TLS key to file:
+Proxy requests to the Kubernetes API (holds the shell):
```sh
-CURRENT_K8S_CONTEXT=$(kubectl config view -o json | jq -r '."current-context"')
-CURRENT_K8S_USER=$(kubectl config view -o json | jq -r --arg K8S_CONTEXT "${CURRENT_K8S_CONTEXT}" '.contexts[] | select(.name == $K8S_CONTEXT) | .context.user')
-CURRENT_K8S_CLUSTER=$(kubectl config view -o json | jq -r --arg K8S_CONTEXT "${CURRENT_K8S_CONTEXT}" '.contexts[] | select(.name == $K8S_CONTEXT) | .context.cluster')
-KUBERNETES_API=$(kubectl config view -o json | jq -r --arg K8S_CLUSTER "${CURRENT_K8S_CLUSTER}" '.clusters[] | select(.name == $K8S_CLUSTER) | .cluster.server')
-
-yq r ~/.kube/config "users(name==$CURRENT_K8S_USER).user.client-certificate-data" | base64 -d > /tmp/kind-cluster-user-cert.pem
-yq r ~/.kube/config "users(name==$CURRENT_K8S_USER).user.client-key-data" | base64 -d > /tmp/kind-cluster-user-cert.key
+kubectl proxy --port=8181
```
-Use the Kubernetes user's client TLS certificate to obtain a short-lived access token for the `api-consumer-1` `ServiceAccount`:
+Obtain a short-lived access token for the `api-consumer-1` `ServiceAccount`:
```sh
-export ACCESS_TOKEN=$(curl -k -X "POST" "$KUBERNETES_API/api/v1/namespaces/authorino/serviceaccounts/api-consumer-1/token" \
- --cert /tmp/kind-cluster-user-cert.pem --key /tmp/kind-cluster-user-cert.key \
+export ACCESS_TOKEN=$(curl -k -X "POST" "http://localhost:8181/api/v1/namespaces/authorino/serviceaccounts/api-consumer-1/token" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest", "spec": { "audiences": ["talker-api"], "expirationSeconds": 600 } }' | jq -r '.status.token')
```
@@ -173,7 +166,7 @@ metadata:
spec:
containers:
- name: api-consumer
- image: quay.io/3scale/authorino:api-consumer
+ image: quay.io/3scale/authorino-examples:api-consumer
command: ["./run"]
args:
- --endpoint=http://envoy.authorino.svc.cluster.local:8000/hello