Multi-tenancy
Starting from v0.6.5, single controller multi-tenancy is supported that allows using a different AWS Identity for each workload cluster. For details, see the multi-tenancy proposal.
For multi-tenancy support, a reference field (identityRef
) is added to AWSCluster
, which describes the identity to be used when reconciling the cluster.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSCluster
metadata:
name: "test"
namespace: "test"
spec:
region: "eu-west-1"
identityRef:
kind: <IdentityType>
name: <IdentityName>
Identity resources are used to describe IAM identities that will be used during reconciliation. There are three identity types: AWSClusterControllerIdentity, AWSClusterStaticIdentity, and AWSClusterRoleIdentity. Once an IAM identity is created in AWS, the corresponding values should be used to create a identity resource.
AWSClusterControllerIdentity
Before multi-tenancy support, all AWSClusters were being reconciled using the credentials that are used by Cluster API Provider AWS Controllers.
AWSClusterControllerIdentity
is used to restrict the usage of controller credentials only to AWSClusters that are in allowedNamespaces
.
Since CAPA controllers use a single set of credentials, AWSClusterControllerIdentity
is a singleton, and can only be created with name: default
.
For backward compatibility, AutoControllerIdentityCreator
experimental feature is added, which is responsible to create the AWSClusterControllerIdentity
singleton if it does not exist.
- Feature status: Experimental
- Feature gate: AutoControllerIdentityCreator=true
AutoControllerIdentityCreator
createsAWSClusterControllerIdentity
singleton with emptyallowedNamespaces
(allowedNamespaces: {}) to grant access to theAWSClusterControllerIdentity
from all namespaces.
Example:
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSCluster
metadata:
name: "test"
namespace: "test"
spec:
region: "eu-west-1"
identityRef:
kind: AWSClusterControllerIdentity
name: default
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
metadata:
name: "default"
spec:
allowedNamespaces:{} # matches all namespaces
AWSClusterControllerIdentity
is immutable to avoid any unwanted overrides to the allowed namespaces, especially during upgrading clusters.
AWSClusterStaticIdentity
AWSClusterStaticIdentity
represents static AWS credentials, which are stored in a Secret
.
Example: Below, an AWSClusterStaticIdentity
is created that allows access to the AWSClusters
that are in “test” namespace.
The identity credentials that will be used by “test” AWSCluster are stored in “test-account-creds” secret.
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSCluster
metadata:
name: "test"
namespace: "test"
spec:
region: "eu-west-1"
identityRef:
kind: AWSClusterStaticIdentity
name: test-account
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterStaticIdentity
metadata:
name: "test-account"
spec:
secretRef:
name: test-account-creds
namespace: capa-system
allowedNamespaces:
selector:
matchLabels:
cluster.x-k8s.io/ns: "testlabel"
---
apiVersion: v1
kind: Namespace
metadata:
labels:
cluster.x-k8s.io/ns: "testlabel"
name: "test"
---
apiVersion: v1
kind: Secret
metadata:
name: "test-account-creds"
namespace: capa-system
stringData:
AccessKeyID: AKIAIOSFODNN7EXAMPLE
SecretAccessKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWSClusterRoleIdentity
AWSClusterRoleIdentity
allows CAPA to assume a role either in the same or another AWS account, using the STS::AssumeRole API.
The assumed role could be used by the AWSClusters that is in the allowedNamespaces
.
Example:
Below, an AWSClusterRoleIdentity
instance, which will be used by AWSCluster “test”, is created.
This role will be assumed by the source identity at runtime. Source identity can be of any identity type.
Role is assumed in the beginning once and after, whenever the assumed role’s credentials are expired.
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSCluster
metadata:
name: "test"
namespace: "test"
spec:
region: "eu-west-1"
identityRef:
kind: AWSClusterRoleIdentity
name: test-account-role
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterRoleIdentity
metadata:
name: "test-account-role"
spec:
allowedNamespaces:
list: # allows only "test" namespace to use this identity
"test"
roleARN: "arn:aws:iam::123456789:role/CAPARole"
sourceIdentityRef:
kind: AWSClusterStaticIdentity
name: test-account-creds
Nested role assumption is also supported.
Example: Below, “multi-tenancy-nested-role” will be assumed by “multi-tenancy-role”, which will be assumed by the “default” AWSClusterControllerIdentity
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterRoleIdentity
metadata:
name: multi-tenancy-role
spec:
allowedNamespaces:
list: []
durationSeconds: 900 # default and min value is 900 seconds
roleARN: arn:aws:iam::11122233344:role/multi-tenancy-role
sessionName: multi-tenancy-role-session
sourceidentityRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
name: default
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterRoleIdentity
metadata:
name: multi-tenancy-nested-role
spec:
allowedNamespaces:
list: []
roleARN: arn:aws:iam::11122233355:role/multi-tenancy-nested-role
sessionName: multi-tenancy-nested-role-session
sourceidentityRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterRoleIdentity
name: multi-tenancy-role
Secure Access to Identitys
allowedNamespaces
field is used to grant access to the namespaces to use Identitys.
Only AWSClusters that are created in one of the Identity’s allowed namespaces can use that Identity.
allowedNamespaces
are defined by providing either a list of namespaces or label selector to select namespaces.
Note that the capa-eks-control-plane-system
namespace will need to be included in the allow namespace list and/or have labels added to allow access to identities used by AWSClusters.
Examples
An empty allowedNamespaces
indicates that the Identity can be used by all namespaces.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
spec:
allowedNamespaces:{} # matches all namespaces
Having a nil list
and a nil selector
is the same with having an empty allowedNamespaces
(Identity can be used by all namespaces).
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
spec:
allowedNamespaces:
list: nil
selector: nil
A nil allowedNamespaces
indicates that the Identity cannot be used from any namespace.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
spec:
allowedNamespaces: # this is same with not providing the field at all or allowedNamespaces: null
The union of namespaces that are matched by selector
and the namespaces that are in the list
is granted access to the identity.
The namespaces that are not in the list and not matching the selector will not have access.
Nil or empty list
matches no namespaces. Nil or empty selector
matches no namespaces.
If list
is nil and selector
is empty OR list
is empty and selector
is nil, Identity cannot be used from any namespace.
Because in this case, allowedNamespaces
is not empty or nil, and neither list
nor selector
allows any namespaces, so the union is empty.
# Matches no namespaces
allowedNamespaces:
list: []
# Matches no namespaces
allowedNamespaces:
selector: {}
# Matches no namespaces
allowedNamespaces:
list: null
selector: {}
# Matches no namespaces
allowedNamespaces:
list: []
selector: {}
Important The default behaviour of an empty label selector is to match all objects, however here we do not follow that behavior to avoid unintended access to the identitys. This is consistent with core cluster API selectors, e.g., Machine and ClusterResourceSet selectors. The result of matchLabels and matchExpressions are ANDed.
In Kubernetes selectors, matchLabels
and matchExpressions
are ANDed.
In the example below, list is empty/nil, so does not allow any namespaces and selector matches with only default
namespace.
Since list
and selector
results are ORed, default
namespace can use this identity.
kind: namespace
metadata:
name: default
labels:
environment: dev
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterControllerIdentity
spec:
allowedNamespaces:
list: null # or []
selector:
matchLabels:
namespace: default
matchExpressions:
- {key: environment, operator: In, values: [dev]}