English | 简体中文
KubeUser is a Kubernetes-native way to manage users, certificates, RBAC, and kubeconfigs declaratively — without running an external identity provider.
Managing Kubernetes access often means manually creating kubeconfigs, handling certificates, and keeping RBAC in sync. This quickly becomes error-prone, hard to audit, and unfriendly to GitOps workflows.
KubeUser solves this by managing Kubernetes users through declarative custom resources. It automatically generates and rotates certificates, applies RBAC bindings, and produces ready-to-use kubeconfigs using native Kubernetes APIs.
Designed for self-managed clusters — bare metal, kubeadm, k3s/RKE2/Talos, edge sites, and air-gapped or disconnected environments — that want Kubernetes-native, GitOps-friendly access control without running an IAM or OIDC stack.
Before you install: KubeUser needs a cluster signer that issues
client authcertificates — true for upstream and self-managed distributions, not for Amazon EKS. Run the preflight check on managed clusters first.
┌──────────────┐
│ User CR │ kubectl apply -f user.yaml
└──────┬───────┘
│
▼
┌──────────────────────────┐
│ Admission Webhooks │ TLS via cert-manager
│ • Mutating (defaults) │
│ • Validating (rules) │
└──────┬───────────────────┘
│
▼
┌──────────────────────────┐
│ User Controller │ reconcile loop
│ (Reconciler) │
└──┬─────────┬──────────┬──┘
│ │ │
▼ ▼ ▼
┌──────┐ ┌─────────┐ ┌────────────┐
│ CSR │ │ Secrets │ │ RBAC │
│ API │ │ key + │ │ Role & │
│ │ │ kubecfg │ │ Cluster │
│signed│ │ │ │ Bindings │
└──────┘ └─────────┘ └────────────┘
Try KubeUser in a few commands on a cluster that passes the preflight check (upstream, kubeadm, k3s, kind, …):
# 1. Install cert-manager (required for webhook TLS)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.2/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=60s
# 2. Install KubeUser (uses the API server from your current kubeconfig)
helm repo add kubeuser https://openkube-hub.github.io/KubeUser
export KUBERNETES_API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
helm install kubeuser kubeuser/kubeuser \
--namespace kubeuser --create-namespace \
--set env.KUBERNETES_API_SERVER="$KUBERNETES_API_SERVER"
# 3. Create a User
cat <<EOF | kubectl apply -f -
apiVersion: auth.openkube.io/v1alpha1
kind: User
metadata:
name: alice
spec:
auth:
type: x509
clusterRoles:
- existingClusterRole: view
EOF
# 4. Retrieve the kubeconfig and use it
kubectl get secret alice-kubeconfig -n kubeuser \
-o jsonpath='{.data.config}' | base64 -d > alice.kubeconfig
kubectl --kubeconfig alice.kubeconfig get pods -AFor production installs, see Installation below.
- Declarative User CRD — status tracking, conditions, and finalizers for clean resource lifecycles
- Automatic Certificate Generation — seamless integration with the Kubernetes CSR API
- Stateful Rotation Engine — resumable, multi-step rotation via the Shadow Secret pattern; survives controller restarts
- Atomic Secret Updates — zero-downtime credential flip with rollback on failure
- Dynamic RBAC Reconciliation — automatic RoleBinding and ClusterRoleBinding management
- Mutating & Validating Webhooks — TLS-secured via cert-manager CA injection
- Configurable CSR Signer — point KubeUser at a custom signer whose CA the API server trusts for client auth
- Anti-Thundering-Herd Design — smart requeue with jitter, 24h TTL floor, 33% renew window, and an idempotent single-status-update reconcile path
- High Availability — leader election and multi-replica deployment shipped via Helm
- Prometheus Metrics & Alerting — rotation counters, duration histograms, expiry gauges, pre-built Grafana dashboard, and shipped PrometheusRule alerts
- Kubernetes Events — structured events surfaced via
kubectl describe user - Status Conditions — standard
Ready,Renewing, andAutoRenewalconditions for declarative status checks - kubectl Printer Columns —
kubectl get usersshows Phase, AutoRenew, Expiry, NextRenewal, Age, and Message
- Self-Service Kubeconfig Bootstrap & Sync —
kubeuser init/kubeuser syncCLI with email-delivered, single-use bootstrap tokens and mTLS-authenticated credential sync (tracking epic) - kubectl Plugin —
kubectl kubeuser kubeconfig <name>to replace the manualkubectl get secret | base64 -dflow - Deletion Warning Event — admission warning and Warning event on User delete clarifying that issued certs remain cryptographically valid until expiry (Kubernetes does not consult CRL/OCSP for client certs)
Deleting a User does NOT invalidate issued certificates.
When deleting a User:
- RBAC bindings are removed immediately (access revoked)
- Secrets are deleted
- Certificates remain cryptographically valid until natural expiry
Plan your TTL accordingly. For short-lived access, use a short ttl and autoRenew: false.
- Kubernetes v1.28+
- kubectl with cluster-admin permissions
- cert-manager (required for webhook certificates)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.2/cert-manager.yaml
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=60shelm repo add kubeuser https://openkube-hub.github.io/KubeUser
helm repo update
export KUBERNETES_API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
helm upgrade --install kubeuser kubeuser/kubeuser \
--create-namespace \
--namespace kubeuser \
--version <version> \
--set env.KUBERNETES_API_SERVER="$KUBERNETES_API_SERVER"
# Verify
kubectl get pods -n kubeuser
kubectl get certificates -n kubeuserAll resource names are prefixed by the Helm release name. Use
helm search repo kubeuser --versionsto list available versions.
git clone https://github.com/openkube-hub/KubeUser.git
cd KubeUser
kubectl create namespace kubeuser
kubectl apply -k config/default
kubectl wait --for=condition=ready pod -l control-plane=controller-manager -n kubeuser --timeout=120smake docker-build
kind load docker-image ghcr.io/openkube-hub/kubeuser-controller:latest --name <cluster-name>
kubectl apply -k config/default
kubectl patch deployment kubeuser-controller-manager -n kubeuser \
-p '{"spec":{"template":{"spec":{"containers":[{"name":"manager","imagePullPolicy":"Never"}]}}}}'KubeUser uses a mutating admission webhook to persist defaults into the User spec at creation time:
# You submit:
spec:
auth:
type: x509
# Webhook persists:
spec:
auth:
type: x509
ttl: "2160h" # from KUBEUSER_DEFAULT_TTL
autoRenew: true # from KUBEUSER_DEFAULT_AUTORENEWVerify applied defaults: kubectl get user <name> -o yaml
Customize defaults via Helm:
helm upgrade --install kubeuser kubeuser/kubeuser \
--set authDefaults.ttl=720h \
--set authDefaults.autoRenew=falseImportant:
authDefaultschanges only apply to users created after the upgrade. Existing users retain their persisted defaults.
apiVersion: auth.openkube.io/v1alpha1
kind: User
metadata:
name: alice
spec:
auth:
type: x509 # REQUIRED: currently only 'x509' is supported
ttl: "72h" # Optional: default 2160h (90 days)
autoRenew: false # Optional: default true
roles:
- namespace: "development"
existingRole: "developer"
- namespace: "staging"
existingRole: "viewer"apiVersion: auth.openkube.io/v1alpha1
kind: User
metadata:
name: bob-admin
spec:
auth:
type: x509
ttl: "2160h"
autoRenew: true
clusterRoles:
- existingClusterRole: "cluster-admin"apiVersion: auth.openkube.io/v1alpha1
kind: User
metadata:
name: contractor-jane
spec:
auth:
type: x509
ttl: "720h" # 30 days
autoRenew: true
renewBefore: "72h" # Renew 3 days before expiry (overrides 33% rule)
roles:
- namespace: "project-x"
existingRole: "developer"
- namespace: "monitoring"
existingClusterRole: "view" # ClusterRole bound to a specific namespace
clusterRoles:
- existingClusterRole: "view"kubectl get secret <username>-kubeconfig -n kubeuser \
-o jsonpath='{.data.config}' | base64 -d > /tmp/kubeconfig
kubectl --kubeconfig /tmp/kubeconfig get pods -n dev| Field | Type | Required | Description |
|---|---|---|---|
spec.auth |
AuthSpec |
Yes | Authentication configuration |
spec.auth.type |
string |
Yes | Auth method — only x509 is supported |
spec.auth.ttl |
string |
No | Certificate lifetime (default: 2160h) |
spec.auth.autoRenew |
boolean |
No | Enable automatic renewal (default: true) |
spec.auth.renewBefore |
string |
No | Renew this duration before expiry. Cannot exceed 90% of TTL |
spec.roles |
[]RoleSpec |
No | Namespace-scoped role bindings |
spec.roles[].namespace |
string |
Yes | Target namespace |
spec.roles[].existingRole |
string |
Yes (or existingClusterRole) |
Existing Role in the same namespace |
spec.roles[].existingClusterRole |
string |
Yes (or existingRole) |
ClusterRole bound into the namespace |
spec.clusterRoles |
[]ClusterRoleSpec |
No | Cluster-wide role bindings |
spec.clusterRoles[].existingClusterRole |
string |
Yes | Existing ClusterRole |
For each
spec.roles[]entry, exactly one ofexistingRoleorexistingClusterRolemust be set.
KubeUser issues client certificates via the Kubernetes CSR API, so the cluster needs a
signer that issues client auth certificates from a CA the API server trusts
(kubernetes.io/kube-apiserver-client by default).
- ✅ Verified on kubeadm, kind, minikube, Kubespray and RKE2; expected to work on any distribution that follows the kubeadm CA layout.
- ❌ Not supported on Amazon EKS — AWS does not sign client-auth CSRs, and no configuration works around it.
⚠️ GKE, AKS and other managed providers are unverified; EKS is an outlier, not the rule. Run the preflight check first.
Full matrix, the preflight script, EKS alternatives and custom-signer setup: Cluster Compatibility.
| Limit | Value | Notes |
|---|---|---|
| Minimum TTL | 24h | Enforced by validating webhook — prevents thundering herd loops |
| Maximum TTL | Bounded by the cluster signing duration (Kubernetes default: 8760h / 1 year) |
Configure --cluster-signing-duration on kube-controller-manager to allow longer |
| Default TTL | 2160h (90 days) | Applied by mutating webhook; configurable via authDefaults.ttl |
| Variable | Default | Description |
|---|---|---|
KUBERNETES_API_SERVER |
https://127.0.0.1:6443 |
API server address written into generated kubeconfigs |
CLUSTER_DOMAIN |
cluster.local |
Cluster DNS domain |
KUBEUSER_DEFAULT_TTL |
2160h |
Default certificate TTL |
KUBEUSER_DEFAULT_AUTORENEW |
true |
Default auto-renewal behaviour |
KUBEUSER_SIGNER_NAME |
kubernetes.io/kube-apiserver-client |
CSR signer name |
Ready-to-apply starter manifests live under examples/:
examples/rbac/— starterClusterRoles (viewer, developer, namespace-admin, readonly) that fill gaps left by Kubernetes' built-inview/edit/admin/cluster-admin.examples/users/— sampleUserCRs for common patterns (minimal, developer, on-call, multi-namespace).
kubectl apply -f examples/rbac/
kubectl apply -f examples/users/minimal-viewer.yaml- Cluster Compatibility
- Certificate Management
- Auto-Renewal
- Webhook Validation
- Metrics Reference
- Accessing Metrics
- Release Verification
- Troubleshooting
We welcome contributions of all kinds — bug reports, features, documentation, and tests.
See CONTRIBUTING.md for the full guide: prerequisites, local setup, code style, commit format, testing, and PR checklist.
| Document | Description |
|---|---|
| CONTRIBUTING.md | How to contribute |
| GOVERNANCE.md | Project roles, decision-making, and release process |
| MAINTAINERS.md | Current and emeritus maintainers |
| SECURITY.md | How to report security vulnerabilities |
| CODE_OF_CONDUCT.md | Community standards |
If you find KubeUser useful, please consider giving it a ⭐ on GitHub!