From ede6785e6b2eca0e7f9cd1c0e7a6c05a411038aa Mon Sep 17 00:00:00 2001
From: Michael Bridgen <michael@weave.works>
Date: Wed, 15 Dec 2021 15:52:41 +0000
Subject: [PATCH] RFC on authorisation model

This gives a baseline for future changes, e.g., expanding where
namespace ACLs are used, switching access control to
untrusted-by-default.

The "Security considerations" section  was adapted from

    https://github.com/fluxcd/flux2/pull/2086

Signed-off-by: Michael Bridgen <michael@weave.works>
---
 rfcs/0001-authorization/README.md | 166 ++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
 create mode 100644 rfcs/0001-authorization/README.md

diff --git a/rfcs/0001-authorization/README.md b/rfcs/0001-authorization/README.md
new file mode 100644
index 00000000..4410dd4d
--- /dev/null
+++ b/rfcs/0001-authorization/README.md
@@ -0,0 +1,166 @@
+# RFC-0001 Memorandum on Flux Authorization
+
+## Summary
+
+This RFC describes in detail, for [Flux version 0.24][] (Nov 2021), how Flux determines which
+operations are allowed to proceed, and how this interacts with Kubernetes' access control.
+
+## Motivation
+
+To this point, the Flux project has provided [examples of how to make a multi-tenant
+system](https://github.com/fluxcd/flux2-multi-tenancy/tree/v0.1.0), but not explained exactly how
+they relate to Flux's authorization model; nor has the authorization model itself been
+documented. Further work on support for multi-tenancy, among other things, requires a full account
+of Flux's authorization model as a baseline.
+
+### Goals
+
+- Give a comprehensive account of Flux's authorization model
+
+### Non-Goals
+
+- Justify the model as it stands; this RFC simply records the state as at v0.24.
+
+## Flux's authorization model
+
+The Flux controllers undertake operations as specified by custom resources of the kinds defined in
+the [Flux API][]. Most of the operations are through the Kubernetes API. Authorization for
+operations on external systems is not accounted for here.
+
+Flux controllers defer to [Kubernetes' native RBAC][k8s-rbac] and [namespace isolation][k8s-ns] to
+determine which operations are authorized, when processing the custom resources in the Flux API.
+
+In general, **Kubernetes API operations are constrained by the service account under which each
+controller pod runs**. In the [default deployment of Flux][flux-rbac] each controller has its own
+service account; and, the service accounts for the Kustomize controller and Helm controller have the
+[`cluster-admin` cluster role][k8s-cluster-admin] bound to it.
+
+Both the Kustomize controller and the Helm controller create, update and delete arbitrary sets of
+configuration that they take as user input. For example, a Kustomization object that references a
+GitRepository is processed by taking whatever is in the specified Git repository and applying it to
+the cluster. This is informally called "syncing", and these user-supplied configurations will be
+called "sync configurations" in the following.
+
+There are five types of access that have a distinct treatment with respect to RBAC and namespace
+isolation:
+
+ - reading and writing the Flux API object to be processed
+ - accessing dependencies of a Flux API object; for example, a secret that holds a decryption key
+ - accessing Flux API objects related to the object being processed; for example, a GitRepository
+   referenced by a Kustomization
+ - creating, updating and deleting Flux API objects as part of processing; for example, each
+   `HelmRelease` object contains a template for a Helm chart spec, which the Helm controller uses to
+   create a `HelmChart` object
+ - creating, updating, deleting, and health-checking of arbitrary objects as specified by _sync
+   configurations_ (as mentioned above).
+
+This table summarises how these operations are subject to RBAC and namespace isolation.
+
+| Type of operation                              | Accessed via               | Namespace isolation          |
+|------------------------------------------------|----------------------------|------------------------------|
+| Reading and writing the object to be processed | Controller service account | N/A                          |
+| Dependencies of object to be processed         | Controller service account | Same namespace only          |
+| Access to related Flux API objects             | Controller service account | Some cross-namespace refs[1] |
+| CRUD of Flux API objects                       | Controller service account | Created in same namespace    |
+| CRUD and healthcheck of sync configurations    | Impersonation[2]           | As directed by spec[2]       |
+
+[1] See "Cross-namespace references" below<br>
+[2] See "Impersonation" below
+
+There are two related mechanisms that affect the service account used for the operations marked with
+"Impersonation" above: "impersonation" and "remote apply". These are explained in the following
+sections.
+
+### Impersonation
+
+The Kustomize controller and Helm controller both apply arbitrary sets of Kubernetes configuration
+("_synced configuration_" as above) to a cluster. These controllers use the service account named in
+the field `.spec.serviceAccountName` in the `Kustomization` and `HelmRelease` objects respectively,
+while applying and health-checking the synced configuration. This mechanism is called
+"impersonation".
+
+The `.spec.serviceAccountName` field is optional. If empty, the controller's service account is
+used.
+
+### Remote apply
+
+The Kustomize controller and Helm controller are able to apply a set of configuration to a cluster
+other than the cluster in which they run. If the `Kustomization` or `HelmRelease` object [refers to
+a secret containing a "kubeconfig" file][kubeconfig], the controller will construct a client using
+that kubeconfig, and the client is used to apply the prepared set of configuration. The effect of
+this is that the configuration will be applied as the user given in the kubeconfig; often this is a
+user with the `cluster-admin` role bound to it, but not necessarily so.
+
+All accesses that would use impersonation use the remote client instead.
+
+### Cross-namespace references
+
+Some Flux API kinds have fields which can refer to a Flux API object in another namespace. The Flux
+controllers do not respect namespace isolation when dereferencing these fields. The following are
+fields that are not restricted to the namespace of the containing object, listed by API kind.
+
+| API kind | field | explanation |
+|----------|-------|-------------|
+| **`kustomizations.kustomize.toolkit.fluxcd.io/v1beta2`** | `.spec.dependsOn` | Items are references that can include a namespace |
+|                                                          | `.spec.healthChecks` | Items are references that can include a namespace (note: these are accessed using impersonation) |
+|                                                          | `.spec.sourceRef` | This is a reference that can include a namespace |
+|                                                          | `.spec.targetNamespace` | This sets or overrides the namespace given in the top-most `kustomization.yaml` |
+| **`helmreleases.helm.toolkit.fluxcd/v2beta1`** | `.spec.dependsOn` | Items are references that can include a namespace |
+|                                                | `.spec.targetNamespace` | This gives the namespace into which a Helm chart is installed (note: using impersonation) |
+|                                                | `.spec.storageNamespace` | This gives the namespace in which the record of a Helm install is created (note: using impersonation) |
+|                                                | `.spec.chart.spec.sourceRef` | This is a reference (in the created `HelmChart` object) that can include a namespace |
+| **`alerts.notification.toolkit.fluxcd.io/v1beta1`** | `.spec.eventSources` | Items are references that can include a namespace |
+| **`receivers.notification.toolkit.fluxcd.io/v1beta1`** | `.spec.resources` | Items in this field are references that can include a namespace |
+| **`imagepolicies.image.toolkit.fluxcd.io/v1beta1`** | `.spec.imageRepositoryRef` | This reference can include a namespace[1] |
+
+[1] This particular cross-namespace reference is subject to additional access control; see "Access
+control for cross-namespace references" below.
+
+Note that the field `.spec.sourceRef` of **`imageupdateautomation.image.toolkit.fluxcd.io`** does
+_not_ include a namespace.
+
+#### Access control for cross-namespace references
+
+In v0.24, an `ImagePolicy` object can refer to a `ImageRepository` object in another
+namespace. Unlike most cross-namespace references, the controller processing `ImagePolicy` objects
+applies additional access control, as given in the referenced `ImageRepository`: the field
+[`.spec.accessFrom`][access-from-ref] grants access to the namespaces selected therein. Access is
+denied unless granted.
+
+## Security considerations
+
+### Impersonation is optional
+
+Flux does not insist on a service account to be supplied in `Kustomization` and `HelmRelease`
+specifications, and the default is to use the controller's service account. That means a user with
+the ability to create either of those objects can trivially arrange for a configuration to be
+applied with the controller service account, which in the default deployment of Flux will have
+`cluster-admin` bound to it. This represents a privilege escalation vulnerability in the default
+deployment of Flux. To guard against it, an admission controller can be used to make the
+`.spec.serviceAccountName` field mandatory; an example which uses Kyverno is given in [the
+multi-tenancy implementation][multi-tenancy-eg].
+
+### Cross-namespace references side-step namespace isolation
+
+`HelmRelease` and `Kustomization` objects can refer to `GitRepository`, `HelmRepository`, or
+`Bucket` (collectively "sources") in any other namespace. The referenced objects are accessed
+through the controller's service account, which by default has `cluster-admin` bound to it. This
+means all sources in a cluster are by default usable as a synced configuration, from any
+namespace. To restrict access, an admission controller can be used to block cross-namespace
+references; the [example using Kyverno][multi-tenancy-eg] from above also does this.
+
+## References
+
+-  [CVE-2021-41254](https://github.com/fluxcd/kustomize-controller/security/advisories/GHSA-35rf-v2jv-gfg7)
+  "Privilege escalation to cluster admin on multi-tenant environments" was fixed in flux2 **v0.15.0**.
+
+[Flux version 0.24]: https://github.com/fluxcd/flux2/releases/tag/v0.24.0
+[serviceAccountName]: https://fluxcd.io/docs/components/kustomize/api/#kustomize.toolkit.fluxcd.io/v1beta2.KustomizationSpec
+[kubeconfig]: https://fluxcd.io/docs/components/kustomize/api/#kustomize.toolkit.fluxcd.io/v1beta2.KubeConfig
+[access-from-ref]: https://fluxcd.io/docs/components/image/imagerepositories/#allow-cross-namespace-references
+[Flux API]: https://fluxcd.io/docs/components/
+[flux-rbac]: https://github.com/fluxcd/flux2/tree/v0.24.0/manifests/rbac
+[k8s-ns]: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
+[k8s-rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
+[k8s-cluster-admin]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles
+[multi-tenancy-eg]: https://github.com/fluxcd/flux2-multi-tenancy/blob/main/infrastructure/kyverno-policies/flux-multi-tenancy.yaml
-- 
GitLab