mirror of
https://github.com/clastix/kamaji.git
synced 2026-02-14 18:10:03 +00:00
feat: kubeconfig generator (#933)
* feat(api): kubeconfig generator Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * refactor: abstracting enqueue to channel function Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * fix: avoiding multiple context registration Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * feat: kubeconfig generator Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * docs: kubeconfig generator Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> * feat(helm): deployment for kubeconfig generator Signed-off-by: Dario Tranchitella <dario@tranchitella.eu> --------- Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>
This commit is contained in:
committed by
GitHub
parent
4bace03fc3
commit
cb2152d5a7
114
docs/content/guides/kubeconfig-generator.md
Normal file
114
docs/content/guides/kubeconfig-generator.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Kubeconfig Generator
|
||||
|
||||
The **Kubeconfig Generator** is a Kamaji extension that simplifies the distribution of Kubeconfig files for tenant clusters managed through Kamaji.
|
||||
|
||||
Instead of manually exporting and editing credentials, the generator automates the creation of kubeconfigs aligned with your organizational policies.
|
||||
|
||||
## Motivation
|
||||
|
||||
When managing multiple Tenant Control Planes (TCPs), cluster administrators often face two challenges:
|
||||
|
||||
1. **Consistency**: ensuring kubeconfigs are generated with the correct user identity, groups, and endpoints
|
||||
2. **Scalability**: distributing kubeconfigs to users across potentially dozens of tenant clusters without manual steps
|
||||
|
||||
The `KubeconfigGenerator` resource addresses these problems by:
|
||||
|
||||
- Selecting which TCPs to target via label selectors.
|
||||
- Defining how to build user and group identities in kubeconfigs.
|
||||
- Automatically maintaining kubeconfigs as new tenant clusters are created or updated.
|
||||
|
||||
This provides a single, declarative way to manage kubeconfig lifecycle across all your tenants,
|
||||
especially convenient if those cases where an Identity Provider can't be used to delegate access to Tenant Control Plane clusters.
|
||||
|
||||
## How it Works
|
||||
|
||||
### Selection
|
||||
|
||||
- `namespaceSelector` filters the namespaces from which Tenant Control Planes are discovered.
|
||||
- `tenantControlPlaneSelector` further refines which TCPs to include.
|
||||
|
||||
### Identity Definition
|
||||
|
||||
The `user` and `groups` fields use compound values, which can be either:
|
||||
|
||||
- A static string (e.g., `developer`)
|
||||
- A dynamic reference resolved from the TCP object (e.g., `metadata.name`)
|
||||
|
||||
This allows kubeconfigs to be tailored to the cluster’s context or a fixed organizational pattern.
|
||||
|
||||
### Endpoint Resolution
|
||||
|
||||
The generator pulls the API server endpoint from the TCP’s `admin` kubeconfig.
|
||||
|
||||
By default it uses the `admin.svc` template, but this can be overridden with the `controlPlaneEndpointFrom` field.
|
||||
|
||||
### Status and Errors
|
||||
|
||||
The resource keeps track of how many kubeconfigs were attempted, how many succeeded,
|
||||
and provides detailed error reports for failed generations.
|
||||
|
||||
## Typical Use Cases
|
||||
|
||||
- **Platform Operators**: automatically distribute kubeconfigs to developers as new tenant clusters are provisioned.
|
||||
- **Multi-team Environments**: ensure each team gets kubeconfigs with the correct groups for RBAC authorization.
|
||||
- **Least Privilege Principle**: avoid distributing `cluster-admin` credentials with a fine-grained RBAC
|
||||
- **Dynamic Access**: use `fromDefinition` references to bind kubeconfig identities directly to tenant metadata
|
||||
(e.g., prefixing users with the TCP's name).
|
||||
|
||||
## Example Scenario
|
||||
|
||||
A SaaS provider runs multiple Tenant Control Planes, each corresponding to a different customer.
|
||||
Instead of manually managing kubeconfigs for every customer environment, the operator defines a single `KubeconfigGenerator`:
|
||||
|
||||
```yaml
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: KubeconfigGenerator
|
||||
metadata:
|
||||
name: tenant
|
||||
spec:
|
||||
# Select only Tenant Control Planes living in namespaces
|
||||
# labeled as production environments
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
environment: production
|
||||
# Match all Tenant Control Planes in those namespaces
|
||||
tenantControlPlaneSelector: {}
|
||||
# Assign a static group "customer-admins"
|
||||
groups:
|
||||
- stringValue: "customer-admins"
|
||||
# Derive the user identity dynamically from the TenantControlPlane metadata
|
||||
user:
|
||||
fromDefinition: "metadata.name"
|
||||
# Use the public admin endpoint from the TCP’s kubeconfig
|
||||
controlPlaneEndpointFrom: "admin.conf"
|
||||
```
|
||||
|
||||
- Matches all TCPs in namespaces labeled `environment=production`.
|
||||
- Generates kubeconfigs with group `customer-admins`.
|
||||
- Derives the user identity from the TCP’s `metadata.name`.
|
||||
|
||||
As new tenants are created, their kubeconfigs are generated automatically and kept up to date.
|
||||
|
||||
```
|
||||
$: kubectl get secret --all-namespaces -l kamaji.clastix.io/managed-by=tenant
|
||||
NAMESPACE NAME TYPE DATA AGE
|
||||
alpha-tnt env-133-tenant Opaque 1 12h
|
||||
alpha-tnt env-130-tenant Opaque 1 2d
|
||||
bravo-tnt prod-tenant Opaque 1 2h
|
||||
charlie-tnt stable-tenant Opaque 1 1d
|
||||
```
|
||||
|
||||
## Observability
|
||||
|
||||
The generator exposes its status directly in the CRD:
|
||||
- `resources`: total number of TCPs targeted.
|
||||
- `availableResources`: successfully generated kubeconfigs.
|
||||
- `errors`: list of failed kubeconfig generations, including the affected resource and error message.
|
||||
|
||||
This allows quick debugging and operational awareness.
|
||||
|
||||
## Deployment
|
||||
|
||||
The _Kubeconfig Generator_ is **not** enabled by default since it's still in experimental state.
|
||||
|
||||
It can be enabled using the Helm value `kubeconfigGenerator.enabled=true` which is defaulted to `false`.
|
||||
@@ -27271,6 +27271,8 @@ Resource Types:
|
||||
|
||||
- [DataStore](#datastore)
|
||||
|
||||
- [KubeconfigGenerator](#kubeconfiggenerator)
|
||||
|
||||
- [TenantControlPlane](#tenantcontrolplane)
|
||||
|
||||
|
||||
@@ -27985,6 +27987,415 @@ DataStoreStatus defines the observed state of DataStore.
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
### KubeconfigGenerator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
KubeconfigGenerator is the Schema for the kubeconfiggenerators API.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>apiVersion</b></td>
|
||||
<td>string</td>
|
||||
<td>kamaji.clastix.io/v1alpha1</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>kind</b></td>
|
||||
<td>string</td>
|
||||
<td>KubeconfigGenerator</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#objectmeta-v1-meta">metadata</a></b></td>
|
||||
<td>object</td>
|
||||
<td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspec">spec</a></b></td>
|
||||
<td>object</td>
|
||||
<td>
|
||||
<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorstatus">status</a></b></td>
|
||||
<td>object</td>
|
||||
<td>
|
||||
KubeconfigGeneratorStatus defines the observed state of KubeconfigGenerator.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspec">`KubeconfigGenerator.spec`</span>
|
||||
|
||||
|
||||
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspecuser">user</a></b></td>
|
||||
<td>object</td>
|
||||
<td>
|
||||
User resolves to a string to identify the client, assigned to the x509 Common Name field.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>controlPlaneEndpointFrom</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
ControlPlaneEndpointFrom is the key used to extract the Tenant Control Plane endpoint that must be used by the generator.
|
||||
The targeted Secret is the `${TCP}-admin-kubeconfig` one, default to `admin.svc`.<br/>
|
||||
<br/>
|
||||
<i>Default</i>: admin.svc<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspecgroupsindex">groups</a></b></td>
|
||||
<td>[]object</td>
|
||||
<td>
|
||||
Groups is resolved a set of strings used to assign the x509 organisations field.
|
||||
It will be recognised by Kubernetes as user groups.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspecnamespaceselector">namespaceSelector</a></b></td>
|
||||
<td>object</td>
|
||||
<td>
|
||||
NamespaceSelector is used to filter Namespaces from which the generator should extract TenantControlPlane objects.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspectenantcontrolplaneselector">tenantControlPlaneSelector</a></b></td>
|
||||
<td>object</td>
|
||||
<td>
|
||||
TenantControlPlaneSelector is used to filter the TenantControlPlane objects that should be address by the generator.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspecuser">`KubeconfigGenerator.spec.user`</span>
|
||||
|
||||
|
||||
User resolves to a string to identify the client, assigned to the x509 Common Name field.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>fromDefinition</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
FromDefinition is used to generate a dynamic value,
|
||||
it uses the dot notation to access fields from the referenced TenantControlPlane object:
|
||||
e.g.: metadata.name<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b>stringValue</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
StringValue is a static string value.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspecgroupsindex">`KubeconfigGenerator.spec.groups[index]`</span>
|
||||
|
||||
|
||||
CompoundValue allows defining a static, or a dynamic value.
|
||||
Options are mutually exclusive, just one should be picked up.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>fromDefinition</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
FromDefinition is used to generate a dynamic value,
|
||||
it uses the dot notation to access fields from the referenced TenantControlPlane object:
|
||||
e.g.: metadata.name<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b>stringValue</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
StringValue is a static string value.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspecnamespaceselector">`KubeconfigGenerator.spec.namespaceSelector`</span>
|
||||
|
||||
|
||||
NamespaceSelector is used to filter Namespaces from which the generator should extract TenantControlPlane objects.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspecnamespaceselectormatchexpressionsindex">matchExpressions</a></b></td>
|
||||
<td>[]object</td>
|
||||
<td>
|
||||
matchExpressions is a list of label selector requirements. The requirements are ANDed.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b>matchLabels</b></td>
|
||||
<td>map[string]string</td>
|
||||
<td>
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspecnamespaceselectormatchexpressionsindex">`KubeconfigGenerator.spec.namespaceSelector.matchExpressions[index]`</span>
|
||||
|
||||
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>key</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
key is the label key that the selector applies to.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>operator</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>values</b></td>
|
||||
<td>[]string</td>
|
||||
<td>
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspectenantcontrolplaneselector">`KubeconfigGenerator.spec.tenantControlPlaneSelector`</span>
|
||||
|
||||
|
||||
TenantControlPlaneSelector is used to filter the TenantControlPlane objects that should be address by the generator.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorspectenantcontrolplaneselectormatchexpressionsindex">matchExpressions</a></b></td>
|
||||
<td>[]object</td>
|
||||
<td>
|
||||
matchExpressions is a list of label selector requirements. The requirements are ANDed.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr><tr>
|
||||
<td><b>matchLabels</b></td>
|
||||
<td>map[string]string</td>
|
||||
<td>
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorspectenantcontrolplaneselectormatchexpressionsindex">`KubeconfigGenerator.spec.tenantControlPlaneSelector.matchExpressions[index]`</span>
|
||||
|
||||
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>key</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
key is the label key that the selector applies to.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>operator</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>values</b></td>
|
||||
<td>[]string</td>
|
||||
<td>
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorstatus">`KubeconfigGenerator.status`</span>
|
||||
|
||||
|
||||
KubeconfigGeneratorStatus defines the observed state of KubeconfigGenerator.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>availableResources</b></td>
|
||||
<td>integer</td>
|
||||
<td>
|
||||
AvailableResources is the sum of successfully generated resources.
|
||||
In case of a different value compared to Resources, check the field errors.<br/>
|
||||
<br/>
|
||||
<i>Default</i>: 0<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>resources</b></td>
|
||||
<td>integer</td>
|
||||
<td>
|
||||
Resources is the sum of targeted TenantControlPlane objects.<br/>
|
||||
<br/>
|
||||
<i>Default</i>: 0<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b><a href="#kubeconfiggeneratorstatuserrorsindex">errors</a></b></td>
|
||||
<td>[]object</td>
|
||||
<td>
|
||||
Errors is the list of failed kubeconfig generations.<br/>
|
||||
</td>
|
||||
<td>false</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<span id="kubeconfiggeneratorstatuserrorsindex">`KubeconfigGenerator.status.errors[index]`</span>
|
||||
|
||||
|
||||
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td><b>message</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
Message is the error message recorded upon the last generator run.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr><tr>
|
||||
<td><b>resource</b></td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
Resource is the Namespaced name of the errored resource.<br/>
|
||||
</td>
|
||||
<td>true</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
### TenantControlPlane
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ nav:
|
||||
- guides/datastore-migration.md
|
||||
- guides/gitops.md
|
||||
- guides/console.md
|
||||
- guides/kubeconfig-generator.md
|
||||
- guides/upgrade.md
|
||||
- guides/monitoring.md
|
||||
- guides/terraform.md
|
||||
|
||||
Reference in New Issue
Block a user