From 9d02f032d68008c8fe09df58febebe0d02683503 Mon Sep 17 00:00:00 2001 From: Fabian Fischer Date: Thu, 23 Dec 2021 17:02:12 +0100 Subject: [PATCH 1/3] Add specification for Organization Access Control --- .../architecture/control-api-org.adoc | 70 +++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc index a8b8dee..837ff98 100644 --- a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc +++ b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc @@ -42,7 +42,7 @@ metadata: <1> Identify resource type, used by the API server to filter for namespaces representing organizations <2> Reflected in the `Organization` object as `spec.displayName` -== Labels and Annotations +.Labels and Annotations [cols="2,1,1,3",options="header"] |=== @@ -63,7 +63,7 @@ metadata: |=== -== Resource filter +=== Resource filter The virtual resource is a filtered view of `Namespaces`. The filter uses the following heuristic: @@ -71,16 +71,74 @@ The filter uses the following heuristic: * API version: `v1` * Kind: `Namespace` * Label: `appuio.io/resource-type=organization` -* Subject is bound to one of the defined `ClusterRole` resources. -== RBAC and Cluster roles +== Access Control -These are `ClusterRole` resources which are bound to a subject by a namespaced `RoleBinding`: +Access control for `Organizations` uses standard Kubernetes RBAC rules, but `Organizations` are represented by two different resources. + +. The `organizations.organizations.appuio.io` resource is the cluster-scoped resource we actually access. +The access control for this resource is done by the Kubernetes API server. +RBAC works the same as for any other cluster-scoped resources and can be configured through `ClusterRoles` and `ClusterRoleBindings`. + +. The `organizations.rbac.appuio.io` resource is a custom namespace-scoped resource known only to the `Organization` API server, which is only used for access control. +Being namespace-scoped, permissions can also be configured through `Roles` and `RoleBindings`. +Similarly to `Namespaces`, permissions configured by a `Role` in namespace `foo` only affects `Organization` `foo`. + +A user can only perform an action on an `Organization`, if the action is allowed on both of these resources. +The `list` and `watch` verbs are special cases and the API server will only return resources that the user also has permission to `get` + +In practice this means, we will allow any user to perform any action on `organizations.organizations.appuio.io` and manage access to `Organizations` through `organizations.rbac.appuio.io`. + +[NOTE] +==== +This trick of using `organizations.rbac.appuio.io` allows us to handle and especially delegate access to `Organizations` on a namespace level. +So we can allow users to configure access without having to expose `ClusterRoleBindings`. +==== + +=== Generated RBAC + +By default there are two `ClusterRoles` that configure access for organization members `appuio-organization-viewer`:: View (read only) access to an organization ++ +[source,yaml] +---- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: appuio-organization-viewer +rules: +- apiGroups: ["rbac.appuio.io"] + resources: ["organizations"] + verbs: ["get"] +... +# Get and list permission for other resources +---- + `appuio-organization-admin`:: Admin (read / write) access to an organization ++ +[source,yaml] +---- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: appuio-organization-admin +rules: +- apiGroups: ["rbac.appuio.io"] + resources: ["organizations"] + verbs: ["get", "patch", "edit"] +- apiGroups: ["rbac.authorization.k8s.io"] + resources: ["rolebindings"] + verbs: ["get", "list", "watch", "patch", "edit", "delete"] +... +# Edit permission for other resources +---- + +Creating, listing, and watching organizations can be done by all authenticated users. -By default, creating organizations can be done by all authenticated users. +When creating an `Organization`, a `RoleBinding` in the created `Namespace` is generated. +This `RoleBinding` assigns the `appuio-organization-admin` `ClusterRole` to the creating user. +This allows the creator to manage the new `Organization` and assign permissions to new members. == Organization Membership From 096402d0b3de98ce1e4e23db133d51e29fa26046 Mon Sep 17 00:00:00 2001 From: Fabian Fischer Date: Tue, 11 Jan 2022 13:22:09 +0100 Subject: [PATCH 2/3] Restructure access control description --- .../architecture/control-api-org.adoc | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc index 837ff98..94c45c2 100644 --- a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc +++ b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc @@ -15,7 +15,7 @@ It is assumed that the `Organization` resource is used for all operations, the r .Virtual resource [source,yaml] ---- -apiVersion: appuio.io/v1 +apiVersion: organization.appuio.io/v1 kind: Organization metadata: name: acme-corp <1> @@ -74,25 +74,22 @@ The filter uses the following heuristic: == Access Control -Access control for `Organizations` uses standard Kubernetes RBAC rules, but `Organizations` are represented by two different resources. - -. The `organizations.organizations.appuio.io` resource is the cluster-scoped resource we actually access. -The access control for this resource is done by the Kubernetes API server. -RBAC works the same as for any other cluster-scoped resources and can be configured through `ClusterRoles` and `ClusterRoleBindings`. +We use standard Kubernetes role-based access control for `Organizations` with two distinct differences. -. The `organizations.rbac.appuio.io` resource is a custom namespace-scoped resource known only to the `Organization` API server, which is only used for access control. -Being namespace-scoped, permissions can also be configured through `Roles` and `RoleBindings`. +. Access needs to be granted for `organizations` resources in API group `rbac.appuio.io` and not for resources in API group `organization.appuio.io`. +Permissions can be configured through both `Roles` and `RoleBindings`, as well as `ClusterRoles` and `ClusterRoleBindings`- Similarly to `Namespaces`, permissions configured by a `Role` in namespace `foo` only affects `Organization` `foo`. -A user can only perform an action on an `Organization`, if the action is allowed on both of these resources. -The `list` and `watch` verbs are special cases and the API server will only return resources that the user also has permission to `get` +. For `list` and `watch` verbs the API server will only return resources that the user also has permission to `get` -In practice this means, we will allow any user to perform any action on `organizations.organizations.appuio.io` and manage access to `Organizations` through `organizations.rbac.appuio.io`. [NOTE] ==== -This trick of using `organizations.rbac.appuio.io` allows us to handle and especially delegate access to `Organizations` on a namespace level. -So we can allow users to configure access without having to expose `ClusterRoleBindings`. +The `rbac.appuio.io` API group allows us to delegate access control to the aggregate API server. +With the new API group we can still use the powerful RBAC engine of Kubernetes, while bypassing the Kubernetes API server's access control. + +The Kubernetes API server will still perform classical access control for `organizations.organization.appuio.io` resources. +In practice any user is allowed to perform any action on `organizations.organization.appuio.io` and access control is handled by the aggregate API server. ==== === Generated RBAC From a989ba298bb7afa928a51126f5fd00954d4c24f0 Mon Sep 17 00:00:00 2001 From: Fabian Fischer <10788152+glrf@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:07:06 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Chris --- .../ROOT/pages/references/architecture/control-api-org.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc index 94c45c2..0ded539 100644 --- a/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc +++ b/docs/modules/ROOT/pages/references/architecture/control-api-org.adoc @@ -77,7 +77,7 @@ The filter uses the following heuristic: We use standard Kubernetes role-based access control for `Organizations` with two distinct differences. . Access needs to be granted for `organizations` resources in API group `rbac.appuio.io` and not for resources in API group `organization.appuio.io`. -Permissions can be configured through both `Roles` and `RoleBindings`, as well as `ClusterRoles` and `ClusterRoleBindings`- +Permissions can be configured through both `Roles` and `RoleBindings`, as well as `ClusterRoles` and `ClusterRoleBindings`. Similarly to `Namespaces`, permissions configured by a `Role` in namespace `foo` only affects `Organization` `foo`. . For `list` and `watch` verbs the API server will only return resources that the user also has permission to `get` @@ -87,7 +87,7 @@ Similarly to `Namespaces`, permissions configured by a `Role` in namespace `foo` ==== The `rbac.appuio.io` API group allows us to delegate access control to the aggregate API server. With the new API group we can still use the powerful RBAC engine of Kubernetes, while bypassing the Kubernetes API server's access control. - +As a consequence, by introducing this logical RBAC group, no custom code needs to be written in order to implement the access control required for multi-tenancy. The Kubernetes API server will still perform classical access control for `organizations.organization.appuio.io` resources. In practice any user is allowed to perform any action on `organizations.organization.appuio.io` and access control is handled by the aggregate API server. ====