Skip to content

Commit

Permalink
update authorizer documentation
Browse files Browse the repository at this point in the history
On-behalf-of: @SAP christoph.mewes@sap.com
  • Loading branch information
xrstf committed Dec 5, 2024
1 parent 70b5401 commit 8ecf970
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 76 deletions.
219 changes: 151 additions & 68 deletions docs/content/concepts/authorization/authorizers.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,89 @@ description: >

# Authorizers

The following authorizers are configured in kcp:
In kcp, a request has four different ways of being admitted:

| Authorizer | Description |
|----------------------------------------|-----------------------------------------------------------------------------------|
| Top-Level organization authorizer | checks that the user is allowed to access the organization |
| Workspace content authorizer | determines additional groups a user gets inside of a workspace |
| Maximal permission policy authorizer | validates the maximal permission policy RBAC policy in the API exporter workspace |
| Local Policy authorizer | validates the RBAC policy in the workspace that is accessed |
| Kubernetes Bootstrap Policy authorizer | validates the RBAC Kubernetes standard policy |
* It can be made to one of the preconfigured paths that do not require authorization, like `/healthz`.
* It can be performed by a user in one of the configured always-allow groups, by default `system:masters`.
* It can pass through the RBAC chain and match configured Roles and ClusterRoles.
* It can be permitted by an external HTTPS webhook backend.

They are related in the following way:

1. top-level organization authorizer must allow
2. workspace content authorizer must allow, and adds additional (virtual per-request) groups to the request user influencing the follow authorizers.
3. maximal permission policy authorizer must allow
4. one of the local authorizer or bootstrap policy authorizer must allow.

```
┌──────────────┐
│ │
┌────►│ Local Policy ├──┐
┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │ │ authorizer │ │
request │ Workspace │ │ Required │ │ Max. Permission │ │ │ │ │
─────────►│ Content ├────►│ Groups ├────┤ Policy authorizer ├───┤ └──────────────┘ │
│ Authorizer │ │ Authorizer │ │ │ │ ▼
└──────────────┘ └──────────────┘ └───────────────────┘ │ OR───►
│ ┌──────────────┐ ▲
│ │ Bootstrap │ │
└────►│ Policy ├──┘
│ authorizer │
│ │
└──────────────┘
``` mermaid
graph TD
start(Request):::state --> main_alt[/one of\]:::or
main_alt --> aapa[Always Allow Paths Auth]
main_alt --> aaga[Always Allow Groups Auth]
main_alt --> wa[Webhook Auth]
main_alt --> rga[Required Groups Auth]
aapa --> decision(Decision):::state
aaga --> decision
wa --> decision
subgraph "RBAC"
rga --> wca[Workspace Content Auth]
wca --> scrda[System CRD Auth]
scrda --> mppa[Max. Permission Policy Auth]
mppa --- mppa_alt[/one of\]:::or
mppa_alt --> lpa[Local Policy Auth]
mppa_alt --> gpa[Global Policy Auth]
mppa_alt --> bpa[Bootstrap Policy Auth]
end
lpa --> decision
gpa --> decision
bpa --> decision
classDef state color:#F77
classDef or fill:none,stroke:none
```

[ASCIIFlow document](https://asciiflow.com/#/share/eJyrVspLzE1VslLydg5QcCwtycgvyqxKLVLSUcpJrATSVkrVMUoVMUpWhgYGBjoxSpVAppGlGZBVklpRAuTEKClQGzya0vNoSgPRaEJMTB4N3NCEIUBde9B9OW0XyE6f%2FOTEHIWA%2FJzM5EqgkjnYPfloyh6SENmaSNWDaQQsIEF0Ijx9wSSgoVqUWliaWlwCtk9BITy%2FKLu4IDE5VQEqAKODgMoyi1JTFBASIMo3sUJPISC1KDezuDgzPw8uiWw1ZuRCrMbnflCMAM1xzs8rSc0rwRqGUCXuRfmlBcW44gYWnUjeB0vAI38JVOMUUpL9DMw0CXaLI1Igo4QeFgmYPJbgwQw1hPy0PUM9NWIC%2FyDkrEjtrA5LiKQVbKCg3kQrpwBpp%2Fz8kuKSosQCBZQ8QVXrUNM0pJCDZQioEnghN4NmJXkiStqnsieR7EEToI09pBUTMUq1SrUA%2FWv8Mg%3D%3D)
[View graph on Kroki](https://kroki.io/mermaid/svg/eNqFkkFrwzAMhe_7Faa7dLBux0IOg7ZhvWxQusEOWRmKoyambuTZDln__RQnG02bUp-E3mfx9OzcginEe3wj-DgP1o_X-F2h83dRFHHDo5hMnsQeVPkF2iePVKKg7eeGZbLh2p8WQAADyUzXcHBipjXVYgW-4LryxWYIz0_wpaXKXORrSD4wLYh2lwjLA5sVlMWsPyywjb_AZSiVU1SO4674X7jj8j4XuvVJr42tSvMQ42g9ny1GoWe727Vkw2R3zoBEsaDSY-mPrLMeOCdtBsnbwXnci8U6PkKC1D6C4Wxf4edBrNDulWssiBVpJQ_HKzYY85NQXHy0TguDNc99IQm6P-2My5lbakqvgimDcyLvPAdzzmKZtVa1GQg5H2qmZih6qcG5GLei_amSNNno9nk67atkxVZpHZWcwz17oh2G-he4ue-L)

### Always Allow Paths Authorizer

Like in vanilla Kubernetes, this authorizer always grants access to the configured URL paths. This is
used for the health and liveness checks of kcp.

### Always Allow Groups Authorizer

This authorizer always permits access if the user is in one of the configured groups. By default this
only includes the `system:masters` group.

### RBAC Chain

The primary authorization flow is handled by a sequence of RBAC-based authorizers that a request must
satisfy all in order to be granted access.

The following authorizers work together to implement RBAC in kcp:

| Authorizer | Description |
|----------------------------------------|--------------------------------------------------------------------------------------------|
| Workspace content authorizer | validates that the user has `access` permission to the workspace |
| Required groups authorizer | validates that the user is in the annotation-based list of groups required for a workspace |
| System CRD authorizer | prevents undesired updates to certain core resources, like the status subresource on APIBindings |
| Maximal permission policy authorizer | validates the maximal permission policy RBAC policy in the API exporter workspace |
| Local Policy authorizer | validates the RBAC policy in the workspace that is accessed |
| Global Policy authorizer | validates the RBAC policy in the workspace that is accessed across shards |
| Kubernetes Bootstrap Policy authorizer | validates the RBAC Kubernetes standard policy |

### Workspace Content Authorizer
#### Required Groups Authorizer

The workspace content authorizer checks whether the user is granted access to the workspace.
A `authorization.kcp.io/required-groups` annotation can be added to a LogicalCluster
to specify additional groups that are required to access a workspace for a user to be member of.
The syntax is a disjunction (separator `,`) of conjunctions (separator `;`).

For example, `<group1>;<group2>,<group3>` means that a user must be member of `<group1>` AND `<group2>`, OR of `<group3>`.

The annotation is copied onto sub-workspaces during workspace creation, but is then not updated
automatically if it's changed.

#### Workspace Content Authorizer

The workspace content authorizer checks whether the user is granted access to the workspace.
Access is granted access through `verb=access` non-resource permission to `/` inside of the workspace.

The ClusterRole `system:kcp:workspace:access` is pre-defined which makes it easy
Expand Down Expand Up @@ -86,39 +129,30 @@ A service-account defined in a workspace implicitly is granted access to it.

A service-account defined in a different workspace is NOT given access to it.

### Required Groups Authorizer

A `authorization.kcp.io/required-groups` annotation can be added to a LogicalCluster
to specify additional groups that are required to access a workspace for a user to be member of.
The syntax is a disjunction (separator `,`) of conjunctions (separator `;`).

For example, `<group1>;<group2>,<group3>` means that a user must be member of `<group1>` AND `<group2>`, OR of `<group3>`.

The annotation is copied onto sub-workspaces during scheduling.
!!! note
By default, workspaces are only accessible to a user if they are in `Ready` phase. Workspaces that are initializing
can be accessed only by users that are granted `admin` verb on the `workspaces/content` resource in the
parent workspace.

#### Initializing Workspaces
Service accounts declared within a workspace don't have access to initializing workspaces.

By default, workspaces are only accessible to a user if they are in `Ready` phase. Workspaces that are initializing
can be access only by users that are granted `admin` verb on the `workspaces/content` resource in the
parent workspace.
#### System CRD Authorizer

Service accounts declared within a workspace don't have access to initializing workspaces.
This small authorizer simply prevents updates to the `status` subresource on APIExports or APIBindings. Note that this authorizer does not validate changes to the CustomResourceDefitions themselves, but to objects from those CRDs instead.

### Maximal Permission Policy Authorizer
#### Maximal Permission Policy Authorizer

If the requested resource type is part of an API binding, then this authorizer verifies that
the request is not exceeding the maximum permission policy of the related API export.
Currently, the "local policy" maximum permission policy type is supported.

#### Local Policy
##### Local Policy

The local maximum permission policy delegates the decision to the RBAC of the related API export.
To distinguish between local RBAC role bindings in that workspace and those for this these maximum permission policy,
every name and group is prefixed with `apis.kcp.io:binding:`.

Example:

Given an API binding for type `foo` declared in workspace `consumer` that refers to an API export declared in workspace `provider`
**Example:** Given an APIBinding for type `foo` declared in workspace `consumer` that refers to an APIExport declared in workspace `provider`
and a user `user-1` having the group `group-1` requesting a `create` of `foo` in the `default` namespace in the `consumer` workspace,
this authorizer verifies that `user-1` is allowed to execute this request by delegating to `provider`'s RBAC using prefixed attributes.

Expand All @@ -128,13 +162,13 @@ Using prefixed attributes prevents RBAC collisions i.e. if `user-1` is granted t
For the given example RBAC request looks as follows:

- Username: `apis.kcp.io:binding:user-1`
- Group: `apis.kcp.io:binding:group-1`
- Groups: [`apis.kcp.io:binding:group-1`]
- Resource: `foo`
- Namespace: `default`
- Workspace: `provider`
- Verb: `create`

The following role and role binding declared within the `provider` workspace will grant access to the request:
The following Role and RoleBinding declared within the `provider` workspace will grant access to the request:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
Expand Down Expand Up @@ -166,32 +200,81 @@ roleRef:
```

!!! note
The same authorization scheme is enforced when executing the request of a claimed resource via the virtual API Export API server,
The same authorization scheme is enforced when executing the request of a claimed resource via the virtual APIExport API server,
i.e. a claimed resource is bound to the same maximal permission policy. Only the actual owner of that resources can go beyond that policy.

TBD: Example

### Kubernetes Bootstrap Policy Authorizer
#### Local Policy Authorizer

The bootstrap policy authorizer works just like the local authorizer but references RBAC rules
defined in the `system:admin` system workspace.
This authorizer ensures that RBAC rules contained within a workspace are being applied
and work just like in a regular Kubernetes cluster.

### Local Policy Authorizer
It is possible to bind to Roles and ClusterRoles in the bootstrap policy from a local policy's
`RoleBinding` or `ClusterRoleBinding`, for example the `system:kcp:workspace:access` ClusterRole exists in the
`system:admin` logical cluster, but can still be bound from without any other logical cluster.

Once the top-level organization authorizer and the workspace content authorizer granted access to a
workspace, RBAC rules contained in the workspace derived from the request context are evaluated.
#### Global Policy Authorizer

This authorizer ensures that RBAC rules contained within a workspace are being applied
and work just like in a regular Kubernetes cluster.
This authorizer works identically to the Local Policy Authorizer, just with the difference
that it uses a global (i.e. across shards) getter for Roles and RoleBindings.

!!! note
Groups added by the workspace content authorizer can be used for role bindings in that workspace.
#### Bootstrap Policy Authorizer

The bootstrap policy authorizer works just like the local authorizer but references RBAC rules
defined in the `system:admin` system workspace. This workspace is where the classic Kubernetes
RBAC like the `cluster-admin` ClusterRole is being defined and the policy defined in this workspace
applies to every workspace in a kcp shard.

It is possible to bind to roles and cluster roles in the bootstrap policy from a local policy `RoleBinding` or `ClusterRoleBinding`.
### Webhook Authorizer

### Service Accounts
This authorizer can be enabled by providing the `--authorization-webhook-config-file` flag to the kcp process
and works identically to [how it works in vanilla Kubernetes](https://kubernetes.io/docs/reference/access-authn-authz/webhook/).

Kubernetes service accounts are granted access to the workspaces they are defined in and that are ready.
The given configuration file must be of the kubeconfg format and point to an HTTPS server, potentially including certificate information as needed:

E.g. a service account "default" in `root:org:ws:ws` is granted access to `root:org:ws:ws`, and through the
workspace content authorizer it gains the `system:kcp:clusterworkspace:access` group membership.
```yaml
apiVersion: v1
kind: Config
clusters:
- name: webhook
cluster:
server: https://localhost:8080/
current-context: webhook
contexts:
- name: webhook
context:
cluster: webhook
```

The webhook will receive every authorization request made in kcp, including internal ones. This means if the webhook
is badly configured, it can even prevent kcp from starting up successfully, but on the other hand this allows
a lot of influence over the authorization in kcp.

The webhook will receive JSON-marshalled `SubjectAccessReview` objects, that (compared to vanilla Kubernetes) include the name of target logical cluster as an `extra` field, like so:

```json
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "kittensandponies",
"verb": "get",
"group": "unicorn.example.org",
"resource": "pods"
},
"user": "jane",
"group": [
"group1",
"group2"
],
"extra": {
"authorization.kubernetes.io/cluster-name": ["root"]
}
}
}
```

!!! note
The extra field will contain the logical cluster _name_ (e.g. o43u2gh528rtfg721rg92), not the human-readable path. Webhooks need to resolve the name to a path themselves if necessary.
12 changes: 5 additions & 7 deletions docs/content/concepts/authorization/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ Generally, the same (cluster) role and (cluster) role binding principles apply e

In addition, additional RBAC semantics is implemented cross-workspaces, namely the following:

- **Top-Level Organization** access: the user must have this as pre-requisite to access any other workspace, or is
even member and by that can create workspaces inside the organization workspace.
- **Workspace Content** access: the user needs access to a workspace or is even admin.
- for some resources, additional permission checks are performed, not represented by local or Kubernetes standard RBAC rules. E.g.
- workspace creation checks for organization membership (see above).
- workspace creation checks for `use` verb on the `WorkspaceType`.
- API binding via APIBinding objects requires verb `bind` access to the corresponding `APIExport`.
- **Workspace Content** access: the user needs `access` permissions to a workspace or be even admin.
- for some resources, additional permission checks are performed, not represented by local or Kubernetes standard RBAC rules; for example
- workspace creation checks for organization membership (see above).
- workspace creation checks for `use` verb on the `WorkspaceType`.
- API binding via APIBinding objects requires verb `bind` access to the corresponding `APIExport`.
- **System Workspaces** access: system workspaces are prefixed with `system:` and are not accessible by users.

The details of the authorizer chain are documented in [Authorizers](./authorizers.md).
Expand Down
7 changes: 6 additions & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ theme:

# Palette toggle for light mode
- media: "(prefers-color-scheme: light)"
scheme: default
scheme: default
primary: white
toggle:
icon: material/brightness-7
Expand Down Expand Up @@ -92,6 +92,11 @@ markdown_extensions:
- pymdownx.highlight:
# Allows linking directly to specific lines in code blocks
anchor_linenums: true
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
# Inline code block highlighting
- pymdownx.inlinehilite
# Lets you embed content from another file
Expand Down

0 comments on commit 8ecf970

Please sign in to comment.