From 77c7d6cd4cb46bf3266a7b9b358b160dd1a2fa25 Mon Sep 17 00:00:00 2001 From: Verveiko Denys Date: Wed, 5 Jul 2023 10:56:09 +0300 Subject: [PATCH 1/2] Add design-doc-provider-identity-based-auth.md to docs --- ...design-doc-provider-identity-based-auth.md | 571 ++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 docs/design-doc-provider-identity-based-auth.md diff --git a/docs/design-doc-provider-identity-based-auth.md b/docs/design-doc-provider-identity-based-auth.md new file mode 100644 index 00000000..9f74a74c --- /dev/null +++ b/docs/design-doc-provider-identity-based-auth.md @@ -0,0 +1,571 @@ +# Identity Based Authentication for Crossplane Providers + +* Owner: Alper Rifat Uluçınar (@ulucinar) +* Reviewers: Crossplane Maintainers +* Status: Draft + +## Background +Crossplane providers need to authenticate themselves to their respective Cloud +providers. This establishes an identity for the Crossplane provider that's later +used by the Cloud provider to authorize the requests made by the Crossplane +provider and for various other purposes such as audit logging, etc. Each +Crossplane provider supports a subset of the underlying Cloud provider's +authentication mechanisms and this subset is currently implemented in-tree, +i.e., in the Crossplane provider's repo, there exists a CRD that's +conventionally named as `ProviderConfig` and each managed resource of the +provider has a [v1.Reference][v1.reference] to a `ProviderConfig` CR. This +`ProviderConfig` holds the authentication configuration (chosen authentication method, +any required credentials for that method, etc.) together with any other provider +specific configuration. Different authentication methods and/or different sets +of credentials can be configured using separate cluster-scoped `ProviderConfig` +CRs and by having different managed resources refer to these `ProviderConfig` +instances. + +The Crossplane provider establishes an identity for the requests it will issue +to the Cloud provider in the [managed.ExternalConnecter][managed.ExternalConnecter]'s `Connect` +implementation. This involves calling the associated authentication functions from +the Cloud SDK libraries (such as the [AWS SDK for Go][aws-sdk] or the [Azure +SDK for Go][azure-sdk]) with the supplied configuration and credentials from the +referenced `ProviderConfig` instance. + +Managed resources and `ProviderConfig`s are cluster-scoped, i.e., they do not exist within a +Kubernetes namespace but rather exist at the global (cluster) scope. This does +not fit well into a namespace-based multi-tenancy model, where each tenant is +confined to its own namespace. The cluster scope is shared between all +namespaces. In the namespace-based multi-tenancy model, the common approach is +to have Role-Based Access Control ([RBAC]) rules that disallow a tenant from +accessing API resources that do not reside in its namespace. Another dimension +to consider here is that all namespaced tenants are serviced by a shared +Crossplane provider deployment typically running in the `crossplane-system` +namespace. This shared provider instance (or more precisely, the [Kubernetes +ServiceAccount][k8s-sa] that the provider's pod uses) is allowed, via RBAC, to `get` the (cluster-scoped) +`ProviderConfig` resources. If tenant `subjects` (groups, users, +ServiceAccounts) are allowed to directly `create` managed resources, then we cannot +constrain them from referring to any `ProviderConfig` (thus to any Cloud +provider credential set) in the cluster solely using RBAC. This is because: +1. RBAC rules allow designated verbs (`get`, `list`, `create`, `update`, etc.) + on the specified API resources for the specified subjects. If a subject, + e.g., a `ServiceAccount`, is allowed to `create` a managed resource, RBAC + alone cannot be used to constrain the set of `ProviderConfig`s that can be + referenced by the `create`d managed resource. +1. The tenant subject itself does not directly access the `ProviderConfig` and in turn + the Cloud provider credential set referred by the `ProviderConfig`. It's the + Crossplane provider's `ServiceAccount` that accesses these resources, and as + mentioned above, this ServiceAccount currently serves all tenants. This + implies that we cannot isolate Cloud provider credentials among namespaced + tenants by only using RBAC rules if we allow tenant subjects to have `edit` + access (`create`, `update`, `patch`) to managed resources. Although it's + possible to prevent them from reading Cloud provider credentials of other + tenants in the cluster via RBAC rules, it's not possible to prevent them from + _using_ those credentials solely with RBAC. + +As discussed in detail in the [Crossplane Multi-tenancy Guide][xp-mt], +Crossplane is opinionated about the different personas in an organization adopting +Crossplane. We make a distinction between the _infrastructure operators_ (or +_platform builders_) who are expected to manage cluster-scoped resources (like +`ProviderConfig`s, XRDs and `Composition`s) and _application operators_, who are +expected to consume the infrastructure for their applications. And tenant +subjects are classified as _application operators_, i.e., it's the +infrastructure operator's responsibility to manage the infrastructure _across_ +the tenants via cluster-scoped Crossplane resources, and it's possible and +desirable from an isolation perspective to disallow application operators, who +are tenant subjects, to directly access these shared cluster-scoped resources. +This distinction is currently possible with Crossplane because: +1. Crossplane `Claim` types are defined via cluster-scoped XRDs by + infrastructure operators and _namespaced_ `Claim` instances are used by the + tenant subjects. This allows infrastructure operators to define RBAC rules + that allow tenant subjects to only access resources in their respective + namespaces, e.g., `Claim`s. +1. However, [1] is not sufficient on itself, as the scheme is still prone to + privilege escalation attacks if the API exposed by the XR is not well designed. The + (shared) provider `ServiceAccount` has access to all Cloud provider + credentials in the cluster and if the exposed XR API allows a `Claim` to + reference cross-tenant `ProviderConfig`s, then a misbehaving tenant subject + can `create` a `Claim` which references some other tenant's credential set. + Thus in our multi-tenancy [guide][xp-mt], we propose a security scheme where: + 1. The infrastructure operator follows a specific naming convention for the + `ProviderConfig`s she provisions: The `ProviderConfig`s for different + tenants are named after those tenants' namespaces. + 2. The infrastructure operator carefully designs `Composition`s that patch + `spec.providerConfigRef` of composed resources using the `Claim`'s + namespace. + 3. Tenant subjects are **not** allowed to provision managed resources directly (and also + XRDs or `Composition`s) but only `Claim`s in their namespaces. And any + `Composition` they can select with their `Claim`s will compose resources + that refer to a `ProviderConfig` provisioned for their tenant (the + `ProviderConfig` with the same name as the tenant's namespace). + 4. We also suggest that the naming conventions imposed by this scheme on + `ProviderConfig`s can be relaxed to some degree by using `Composition`'s + [patching capabilities][ref-compositions]. For instance, a string + [transform][patch-transform] of type `Format` can be used to combine the + `Claim`'s namespace with an XR field's value to allow multiple + `ProviderConfig`s per tenant and to allow selection of the + `ProviderConfig` with the `Claim`. + +As explained above, RBAC rules can only impose restrictions on the actions +(`get`, `update`, etc.) performed on the API resource endpoints but they cannot +impose constraints on the API resources themselves (objects) available at these +endpoints. Thus, we also discuss using one of the available policy engines that +can run integrated with the Kubernetes API server to further impose restrictions +on the resources. For example, the following [kyverno] [policy][kyverno-policy] +prevents a tenant subject (`tenant1/user1`) from specifying in `Claim`s any +`ProviderConfig` names without the prefix `tenant1` (_please do not use this +example policy in production environments as it has a security vulnerability as +we will discuss shortly_): + +```yaml +# XRD +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: compositeresourcegroups.example.org +spec: + group: example.org + names: + kind: CompositeResourceGroup + plural: compositeresourcegroups + claimNames: + kind: ClaimResourceGroup + plural: claimresourcegroups + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + name: + type: string + providerConfigName: + type: string + required: + - name + +--- + +# kyverno ClusterPolicy +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: tenant1 +spec: + validationFailureAction: enforce + background: false + rules: + - name: check-for-providerconfig-ref + match: + any: + - resources: + kinds: + # G/V/K for the Claim type + - example.org/v1alpha1/ClaimResourceGroup + subjects: + - kind: User + name: tenant1/user1 + validate: + message: "Only ProviderConfig names that have the prefix tenant1 are allowed for users under tenant1" + pattern: + spec: + providerConfigName: tenant1* + +--- + +# related patch in a Composition +... + patches: + - fromFieldPath: spec.providerConfigName + toFieldPath: spec.providerConfigRef.name +``` + + + +### Limitations of Naming Convention-based or Admission Controller-based Approaches +The naming convention-based or admission controller-based approaches described +above are not straightforward to configure, especially if you also consider that +in addition to the RBAC configurations needed to isolate the tenants +(restricting access to the cluster-wide resources), resource quotas and network +policies are also needed to properly isolate and fairly distribute the worker +node resources and the network resources, respectively. Also due to the +associated complexity, it's easy to misconfigure the cluster and difficult to +verify a given security configuration guarantees proper isolation between +the tenants. + +As an example, consider the Kyverno `ClusterPolicy` given above: +While the intent is to restrict the users under `tenant1` to using only the +`ProviderConfig`s installed for them (e.g., those with names `tenant1*`), the +scheme is broken if there exists a tenant in the system with `tenant1` as a +prefix to its name, such as `tenant10`. + +Organizations, especially with hard multi-tenancy requirements (i.e., with +tenants assumed to be untrustworthy or actively malicious), may not prefer or +strictly forbid such approaches. The architectural problem here, from a security +perspective, is that the Crossplane provider (and also the core Crossplane +components) is a shared resource itself and it requires cross-tenant privileges +such as accessing cluster-wide resources and accessing each tenant's namespaced +resources (especially tenant Cloud credentials). This increases the attack +surface in the dimensions of: +- Logical vulnerabilities (see the above example for a misconfiguration) +- Isolation vulnerabilities: For instance, controller *workqueue*s become shared + resources between the tenants. How can we ensure, for instance, that the workqueue capacity + is fairly shared between the tenants? +- Code vulnerabilities: As an example, consider a hypothetical Crossplane + provider bug in which the provider fetches another `ProviderConfig` than the + one declared in the managed resource, or other credentials than the ones + declared in the referred `ProviderConfig`. Although the logical barriers + enforced by the `Composition`s or the admission controllers as descibed above + are not broken, the too privileged provider itself breaks the cross-tenant + barrier. + +In the current Crossplane provider deployment model, when a Crossplane provider +package is installed, there can be a single *active* `ProviderRevision` +associated with it, which owns (via an owner reference) the Kubernetes +deployment for running the provider. This single deployment, in turn, specifies +a single Kubernetes service account under which the provider runs. + +Apart from a vulnerability perspective, there are also some other limitations to +this architecture, which are related to identity-based authentication. + +**Note**: The [multi-tenancy guide][xp-mt] also mentions multi-cluster +multi-tenancy, where tenants are run on their respective Kubernetes clusters. +This form of multi-tenancy is out of scope in this document. + +### Identity-based Authentication Schemes +Various Cloud providers, such as AWS, Azure and GCP, have some means of +identity-based authentication. With identity-based authentication an entity, +such as a Cloud service (a database server, a Kubernetes cluster, etc.) or a +workload (an executable running in a VM, a pod running in a Kubernetes cluster) +is assigned a Cloud identity and further authorization checks are performed +against this identity. The advantage with identity-based authentication is that +no manually provisioned credentials are required. + +The traditional way for authenticating a Crossplane provider to the Cloud +provider is to first provision a Cloud identity such as an AWS IAM user or a GCP +service account or an Azure AD service principal and a set of credentials +associated with that identity (such as an AWS access key or a GCP service +account key or Azure client ID & secret) and then to provision a Kubernetes +secret containing these credentials. Then a `ProviderConfig` refers to this +Kubernetes secret. There are some undesirable consequences of this flow: +- The associated Cloud credentials are generally long-term credentials and + require manual rotation. +- For fine-grained access control, you need multiple identities with such + credentials to be manually managed & rotated. +- These generally result in reusing such credentials, which in turn prevents + fine-grained access control and promotes aggregation of privileges. + +Different Cloud providers have different identity-based authentication +implementations: + +**AWS**: [EKS node IAM roles][aws-eks-node-iam], or IAM roles for service +accounts ([IRSA]) both allow for identity-based authentication. IRSA has +eliminated the need for some third-party solutions such as [kiam] or [kube2iam] +and associates an IAM role with a Kubernetes service account. Using IRSA for +authenticating `provider-aws` is [possible][provider-aws-irsa]. IRSA leverages +the [service account token volume projection][k8s-sa-projection] support +introduced with Kubernetes 1.12. When enabled, `kubelet` +[projects][k8s-volume-projection] a signed OIDC JWT for a pod's service account +at the requested volume mount path in a container and periodically rotates the +token. An AWS client can then exchange this token (issued by the API server) +with *temporary* credentials for an IAM role via the AWS Security Token Service +([STS]) [AssumeRoleWithWebIdentity] API operation. The IAM role to be associated +with the Kubernetes service account can be specified via an annotation on the +service account (`eks.amazonaws.com/role-arn`). As we will discuss later, this +can also be used in conjunction with IAM role chaining to implement fine-grained +access control. + +As of this writing, `provider-aws` [supports][provider-aws-auth] `IRSA`, role +chaining (via the [STS] [AssumeRole] API operation), and the [STS +AssumeRoleWithWebIdentity][AssumeRoleWithWebIdentity] API operation. This allows +us to authenticate `provider-aws` using the projected service account token by +exhanging it with a set of temporary credentials associated with an IAM role. +This set of temporary credentials consists of an access key ID, a secret access +key and a security token. Also the target IAM role ARN (Amazon Resource Name) is +configurable via the `provider-aws`'s `ProviderConfig` API. This allows +Crossplane users to implement a fine-grained access policy for different +tenants possibly using different AWS accounts: +- The initial IAM role, which is the target IAM role for the `IRSA` + authentication (via the `AssumeRoleWithWebIdentity` STS API + operation) does not need privileges on the managed external resources when + role chaining is used. +- `provider-aws` then assumes another IAM role by exchanging the initial set of + temporary credentials via STS role chaining. However, currently the + `ProviderConfig` API does not allow chains of length greater than one, i.e., + `provider-aws` can only call the STS `AssumeRole` API once in a given chain. + This is currently an artificial limitation in `provider-aws` imposed by the + `ProviderConfig` API. +- The target role ARN for the initial IRSA `AssumeRoleWithWebIdentity` operation + is configurable via the `ProviderConfig` API. Thus, if a proper cross-AWS + account trust policy exists between the EKS cluster's OIDC provider and a + target IAM role in a different account (than the account owning the EKS + cluster and the OIDC provider), then it's possible to switch to an IAM role in + that target AWS account. +- Privileges on the managed external resources need to be defined on the target + IAM roles of the STS `Assume*` operations. And as mentioned, fine-grained + access policies can be defined on these target roles which are configurable + with the `ProviderConfig` API. +- When combined with the already available single-cluster multi-tenancy + techniques discussed above, this allows `provider-aws` users to isolate their + tenant identities and the privileges required for those identities. + +From the relevant discussions on `provider-aws` surveyed for this writing, this +level of tenant isolation has mostly been sufficient for `provider-aws` users. +But as discussed above, a deeper isolation is still possible. Especially in the +currently feasible `provider-aws` authentication scheme, the initial +`AssumeRoleWithWebIdentity` target IAM role is still shared by the tenants +although it does not require privileges on the managed external resources. But +due to vulnerabilities discussed in the [Limitations of Naming Convention-based +or Admission Controller-based Approaches] section above, it could still be +possible for a tenant to assume an IAM role with more privileges than it needs, +starting with the shared `AssumeRoleWithWebIdentity` target IAM role. A deeper +isolation between tenants would be possible if it were possible to have a +Kubernetes service account and an associated (initial) non-shared IAM role +assigned to each tenant. + +As of this writing, `provider-jet-aws` supports IRSA authentication with support +for role chaining via the STS `AssumeRole` API operation. Similar to +`provider-aws`, only chains of length `1` are allowed. Also, `provider-jet-aws` +does not currently support specifying the target `AssumeRoleWithWebIdentity` IAM +role via the `ProviderConfig` API. And unlike `provider-aws`, `provider-jet-aws` +does not support specifying external IDs, session tags or transitive tag keys +for the `AssumeRole` operation, or specifying session names for the +`AssumeRoleWithWebIdentity` operation. + +**Azure**: Azure has the notion of system-assigned or user-assigned [managed +identities][azure-msi], which allow authentication to any resource that supports +Azure AD authentication. Some Azure services, such as EKS, allow a managed +identity to be enabled directly on a service's instance (system-assigned). Or a +user-assigned managed identity can be provisioned and assigned to the service +instance. Similar to AWS IRSA, Azure has also introduced [Azure AD workload +identities][azure-wi], which work in a similar way to IRSA: + +| | +| :-: | +| drawing | +| Azure AD Workload Identities (reproduced from [[1]]) | + +In Azure AD workload identities, similar to IRSA, a Kubernetes service account +is associated with an Azure AD application client ID via the +`azure.workload.identity/client-id` annotation on the service account object. + +As of this writing, none of `provider-azure` or `provider-jet-azure` supports +Azure workload identities. Terraform native `azurerm` provider itself currently +does *not* support workload identities, thus there are technical challenges if +we would like to introduce support for workload identities in +`provider-jet-azure`. However, using lower level APIs (then the [Azure Identity +SDK for Go][azidentity]), it should be possible to [implement][azure-329] +workload identities for `provider-azure`. + +Both `provider-azure` and `provider-jet-azure` support system-assigned and +user-assigned managed identitites as an alternate form of identity-based +authentication (with `provider-azure` support being introduced by this +[PR][azure-330]). + +Using system-assigned managed identities, it's *not* possible to implement an +isolation between tenants (see the discussion above for `provider-aws`) by using +separate Azure AD (AAD) applications (service principals) for them, because the +system-assigned managed identity is shared between those tenants and currently +it's not possible to switch identities within the Crossplane Azure providers*. +However, using user-assigned managed identities and per-tenant `ProviderConfig`s +as discussed above in the context of single-cluster multi-tenancy, it's possible +to implement fine-grained access control for tenants again with the same +limitations mentioned there. + +*: Whether there exists an Azure service (similar to the [STS] of AWS) that allows +us to exchange credentials of an AAD application with (temporary) credentials of +another AAD application needs further investigation. + + +**GCP**: GCP also [recommends][gcp-wi] workload identities for assigning +identities to workloads running in GKE clusters. With GKE workload identities, a +Kubernetes service account is associated with a GCP IAM service account. And +similar to AWS and Azure, GCP also uses an annotation +(`iam.gke.io/gcp-service-account`) on the Kubernetes service account object +which specifies the GCP service account to be impersonated. + +As of this writing, both `provider-gcp` and `provider-jet-gcp` support workload +identities, which are based on Kubernetes service accounts similar to AWS IRSA +and Azure AD workload identities. Thus, current implementations share the same +limitations detailed in [Limitations of Naming Convention-based or Admission +Controller-based Approaches]. + +**Summary for the existing Crossplane AWS, Azure & GCP providers**: + +In all the three Kubernetes workload identity schemes introduced above, a +Kubernetes service account is mapped to a Cloud provider identity (IAM +role/service account, AD application, etc.) And as explained in depth above, the +current Crossplane provider deployment model allows the provider to be run under +a single Kubernetes service account. + +Users of `provider-aws` have so far combined [IRSA] with AWS STS role chaining +(`AssumeRoleWithWebIdentity` and `AssumeRole` STS API operations) to meet their +organizational requirements around least-privilege and fine-grained access +control, and they have isolated their tenants sharing the same Crossplane +control-plane using the single-cluster multi-tenancy techniques described above. +However, currently lacking similar semantics for "role chaining", to the best of +our knowledge, users of AKS and GKE workload identities cannot implement +similar fine-grained access control scenarios because the Crossplane provider is +running as a single Kubernetes deployment, which in turn is associated with a +single Kubernetes service account. And for `provider-aws` users who would like +to have more strict tenant isolation, we need more flexibility in the Crossplane +deployment model. + +## Decoupling Crossplane Provider Deployment +Flexibility in Crossplane provider deployment has been discussed especially in +[[2]] and [[3]]. [[2]] proposes a provider partitioning scheme on +`ProviderConfig`s and [[3]] calls for a *Provider Runtime Interface* for +decoupling the runtime aspects of a provider (where & how a provider is deployed +& run) from the core Crossplane package manager. We can combine these two +approaches to have an extensible, flexible and future-proof deployment model for +Crossplane providers that would also better meet the requirements around tenant +isolation. Instead of partitioning based on `ProviderConfig`s, as an +alternative, we could have an explicit partitioning API based on provider +runtime configurations specified in `Provider.pkg`s: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: crossplane-provider-azure +spec: + package: crossplane/provider-azure:v0.19.0 + ... + runtimeConfigs: + - name: deploy-1 + runtime: + apiVersion: runtime.crossplane.io/v1alpha1 + kind: KubernetesDeployment + spec: + # ControllerConfig reference that defines the corresponding Kubernetes deployment + controllerConfigRef: + name: cc-1 + - name: deploy-2 + runtime: + apiVersion: runtime.crossplane.io/v1alpha1 + kind: KubernetesDeployment + spec: + # ControllerConfig reference that defines the corresponding Kubernetes deployment + controllerConfigRef: + name: cc-2 + - name: container-1 + runtime: + apiVersion: runtime.crossplane.io/v1alpha1 + kind: DockerContainer + spec: + # some Docker client options + host: /var/run/docker.sock + config: ... + # some docker run options + runOptions: + user: ... + network: ... + - ... +``` + +In the proposed scheme, the `PackageRevision` controller would no longer +directly manage a Kubernetes deployment for the active revision. Instead it +would provision, for the active revision, a number of Kubernetes resources +corresponding to each runtime configuration specified in the `runtimeConfigs` +array. For the above example, the `PackageRevision` controller would provision +two `KubernetesDeployment` and one `DockerContainer` *runtime configuration* +resources for the active revision. An example `KubernetesDeployment` object +provisioned by the `PackageRevision` controller could look like the following: + +```yaml +apiVersion: runtime.crossplane.io/v1alpha1 +kind: KubernetesDeployment +metadata: + name: deploy-1 + ownerReferences: + - apiVersion: pkg.crossplane.io/v1 + controller: true + kind: ProviderRevision + name: crossplane-provider-azure-91818efefdbe + uid: 3a58c719-019f-43eb-b338-d6116e299974 +spec: + crossplaneProvider: crossplane/provider-azure-controller:v0.19.0 + # ControllerConfig reference that defines the corresponding Kubernetes deployment + controllerConfigRef: + name: cc-1 +``` + +As an alternative, in order to deprecate the `ControllerConfig` API, the +`KubernetesDeployment` could also be defined as follows: + +```yaml +... + runtimeConfigs: + - name: deploy-1 + runtime: + apiVersion: runtime.crossplane.io/v1alpha1 + kind: KubernetesDeployment + spec: + template: + # metadata that defines the corresponding Kubernetes deployment's metadata + metadata: + ... + # spec that defines the corresponding Kubernetes deployment's spec + spec: + ... +``` + +This scheme makes the runtime implementation pluggable, i.e., in different +environments we can have different *provider runtime configuration* contollers +running (as Kubernetes controllers) with different capabilities. For instance, +the existing deployment implementation embedded into the `PackageRevision` +controller can still be shipped with the core Crossplane with a corresponding +runtime configuration object. But another runtime configuration controller, +which is also based on Kubernetes deployments, can implement advanced isolation +semantics. + + +[1]: https://azure.github.io/azure-workload-identity/docs/introduction.html +[2]: https://github.com/crossplane/crossplane/issues/2411 +[3]: https://github.com/crossplane/crossplane/issues/2671 + + +[v1.Reference]: TODO +[managed.ExternalConnecter]: TODO +[aws-sdk]: https://github.com/aws/aws-sdk-go-v2 +[azure-sdk]: https://github.com/Azure/azure-sdk-for-go +[RBAC]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/ +[k8s-sa]: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +[xp-mt]: https://crossplane.io/docs/v1.7/guides/multi-tenant.html +[xp-2093]: https://github.com/crossplane/crossplane/pull/2093 +[ref-compositions]: https://crossplane.io/docs/v1.7/reference/composition.html +[patch-transform]: + https://github.com/crossplane/crossplane/blob/6c1b06507db47801c7a1c7d91704783e8d13856f/apis/apiextensions/v1/composition_transforms.go#L64 +[kyverno]: https://kyverno.io/ +[kyverno-policy]: https://kyverno.io/docs/kyverno-policies/ +[aws-eks-node-iam]: + https://docs.aws.amazon.com/eks/latest/userguide/create-node-role.html +[IRSA]: + https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html +[kiam]: https://github.com/uswitch/kiam +[kube2iam]: https://github.com/jtblin/kube2iam +[provider-aws-auth]: https://github.com/crossplane/provider-aws/blob/36299026cd9435c260ad13b32223d2e5fef3c443/AUTHENTICATION.md +[provider-aws-irsa]: + https://github.com/crossplane/provider-aws/blob/36299026cd9435c260ad13b32223d2e5fef3c443/AUTHENTICATION.md#using-iam-roles-for-serviceaccounts +[k8s-sa-projection]: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection +[azure-msi]: + https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview +[azure-wi]: + https://azure.github.io/azure-workload-identity/docs/introduction.html +[k8s-volume-projection]: + https://kubernetes.io/docs/concepts/storage/projected-volumes/ +[STS]: https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html +[AssumeRoleWithWebIdentity]: + https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html +[AssumeRole]: + https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html +[gcp-wi]: + https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity +[azidentity]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity +[azure-329]: https://github.com/crossplane/provider-azure/issues/329 +[azure-330]: https://github.com/crossplane/provider-azure/pull/330 + +[hnc]: https://github.com/kubernetes-sigs/hierarchical-namespaces \ No newline at end of file From 0caa002288a415bcd36b9299c0e0640352ac8ac4 Mon Sep 17 00:00:00 2001 From: Verveiko Denys Date: Sun, 9 Jul 2023 21:12:22 +0300 Subject: [PATCH 2/2] Added image & Fix links --- docs/design-doc-provider-identity-based-auth.md | 16 ++++++++-------- docs/images/azure-wi.png | Bin 0 -> 57112 bytes 2 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 docs/images/azure-wi.png diff --git a/docs/design-doc-provider-identity-based-auth.md b/docs/design-doc-provider-identity-based-auth.md index 9f74a74c..80c98f0f 100644 --- a/docs/design-doc-provider-identity-based-auth.md +++ b/docs/design-doc-provider-identity-based-auth.md @@ -13,7 +13,7 @@ Crossplane provider supports a subset of the underlying Cloud provider's authentication mechanisms and this subset is currently implemented in-tree, i.e., in the Crossplane provider's repo, there exists a CRD that's conventionally named as `ProviderConfig` and each managed resource of the -provider has a [v1.Reference][v1.reference] to a `ProviderConfig` CR. This +provider has a [v1.Reference](https://docs.crossplane.io/v1.12/concepts/managed-resources/#providerconfigref) to a `ProviderConfig` CR. This `ProviderConfig` holds the authentication configuration (chosen authentication method, any required credentials for that method, etc.) together with any other provider specific configuration. Different authentication methods and/or different sets @@ -22,7 +22,7 @@ CRs and by having different managed resources refer to these `ProviderConfig` instances. The Crossplane provider establishes an identity for the requests it will issue -to the Cloud provider in the [managed.ExternalConnecter][managed.ExternalConnecter]'s `Connect` +to the Cloud provider in the [managed.ExternalConnecter](https://pkg.go.dev/github.com/crossplane/crossplane-runtime@v0.19.2/pkg/reconciler/managed#ExternalConnecter)'s `Connect` implementation. This involves calling the associated authentication functions from the Cloud SDK libraries (such as the [AWS SDK for Go][aws-sdk] or the [Azure SDK for Go][azure-sdk]) with the supplied configuration and credentials from the @@ -59,7 +59,7 @@ provider credential set) in the cluster solely using RBAC. This is because: tenants in the cluster via RBAC rules, it's not possible to prevent them from _using_ those credentials solely with RBAC. -As discussed in detail in the [Crossplane Multi-tenancy Guide][xp-mt], +As discussed in detail in the [Crossplane Multi-tenancy Guide](https://docs.crossplane.io/knowledge-base/guides/multi-tenant/), Crossplane is opinionated about the different personas in an organization adopting Crossplane. We make a distinction between the _infrastructure operators_ (or _platform builders_) who are expected to manage cluster-scoped resources (like @@ -82,7 +82,7 @@ This distinction is currently possible with Crossplane because: credentials in the cluster and if the exposed XR API allows a `Claim` to reference cross-tenant `ProviderConfig`s, then a misbehaving tenant subject can `create` a `Claim` which references some other tenant's credential set. - Thus in our multi-tenancy [guide][xp-mt], we propose a security scheme where: + Thus in our multi-tenancy [guide](https://docs.crossplane.io/knowledge-base/guides/multi-tenant/), we propose a security scheme where: 1. The infrastructure operator follows a specific naming convention for the `ProviderConfig`s she provisions: The `ProviderConfig`s for different tenants are named after those tenants' namespaces. @@ -96,7 +96,7 @@ This distinction is currently possible with Crossplane because: `ProviderConfig` with the same name as the tenant's namespace). 4. We also suggest that the naming conventions imposed by this scheme on `ProviderConfig`s can be relaxed to some degree by using `Composition`'s - [patching capabilities][ref-compositions]. For instance, a string + [patching capabilities](https://docs.crossplane.io/v1.12/concepts/composition/#compositions). For instance, a string [transform][patch-transform] of type `Format` can be used to combine the `Claim`'s namespace with an XR field's value to allow multiple `ProviderConfig`s per tenant and to allow selection of the @@ -229,7 +229,7 @@ a single Kubernetes service account under which the provider runs. Apart from a vulnerability perspective, there are also some other limitations to this architecture, which are related to identity-based authentication. -**Note**: The [multi-tenancy guide][xp-mt] also mentions multi-cluster +**Note**: The [multi-tenancy guide](https://docs.crossplane.io/knowledge-base/guides/multi-tenant/) also mentions multi-cluster multi-tenancy, where tenants are run on their respective Kubernetes clusters. This form of multi-tenancy is out of scope in this document. @@ -533,9 +533,9 @@ semantics. [RBAC]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/ [k8s-sa]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ -[xp-mt]: https://crossplane.io/docs/v1.7/guides/multi-tenant.html +[xp-mt]: https://docs.crossplane.io/knowledge-base/guides/multi-tenant/ [xp-2093]: https://github.com/crossplane/crossplane/pull/2093 -[ref-compositions]: https://crossplane.io/docs/v1.7/reference/composition.html +[ref-compositions]: https://docs.crossplane.io/v1.12/concepts/composition/#compositions [patch-transform]: https://github.com/crossplane/crossplane/blob/6c1b06507db47801c7a1c7d91704783e8d13856f/apis/apiextensions/v1/composition_transforms.go#L64 [kyverno]: https://kyverno.io/ diff --git a/docs/images/azure-wi.png b/docs/images/azure-wi.png new file mode 100644 index 0000000000000000000000000000000000000000..44625ed276e888de5dda271c45284a26ed1531f7 GIT binary patch literal 57112 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

GZjVLc+W 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