Skip to content

TrueBad0ur/kubevalet

Repository files navigation

kubevalet

kubevalet

Release Docker Image Version Docker Pulls Artifact Hub License: MIT

Lightweight Kubernetes user management with a web UI.

Creates x509 users via the Kubernetes CSR API, issues kubeconfigs, and manages RBAC bindings — no LDAP, no OIDC, no Dex.

What it does

  • Create Kubernetes users (x509 / CSR API)
  • Assign preset roles (cluster-admin, admin, edit, view) or define custom RBAC rules (API groups, resources, verbs)
  • Multi-namespace scoped bindings per user
  • Groups — manage k8s Group subjects with their own RBAC; users added to a group via x509 O field inherit permissions automatically
  • Download / view generated kubeconfigs
  • Graph view — visualise any user's full access tree: cluster-wide role and per-namespace bindings
  • Multi-cluster support — manage users and groups across multiple Kubernetes clusters; switch between clusters via the sidebar; each cluster has its own isolated users/groups
  • PostgreSQL as source of truth — all user state (RBAC config, cert PEM, private key) stored in postgres; Sync button recreates any missing k8s objects from DB
  • Kubeconfig API server address configurable per cluster
  • Private keys stored in cluster Secrets and postgres — never logged or exposed raw
  • Simple username/password auth backed by PostgreSQL

Screenshots

Users Groups
Users Groups
Graph — all users Graph — group filter
Graph all Graph group

Settings

Structure

cmd/server/        # entrypoint
internal/
  api/             # HTTP handlers (Gin)
  auth/            # JWT
  cert/            # x509 key + CSR generation
  config/          # env config
  db/              # PostgreSQL (pgx)
  k8s/             # CSR, RBAC, Secret helpers
  kubeconfig/      # kubeconfig builder
  models/          # shared types
web/               # Vue 3 frontend (embedded in binary)
charts/kubevalet/  # Helm chart (includes bundled PostgreSQL)

Development flow

Branch naming convention

Feature branches must follow the format x.x.x-suffix, e.g.:

0.3.13-dev
0.3.13-feature-oidc
0.3.13-fix-login

This is enforced by a pre-push git hook. Activate it once after cloning:

make hooks-setup

After that it activates automatically on every make commit and make release — no need to remember.

Work in progress — feature branch

git checkout -b 0.3.13-dev

# iterate freely — no version bumps, no file changes needed
make commit MSG="add feature X"
make commit MSG="fix bug Y"

Every push to a branch matching x.x.x-* builds a Docker image tagged with the branch name:

truebad0ur/kubevalet:0.3.13-dev
truebad0ur/kubevalet:0.3.13-feature-oidc

Deploy a branch image to test before releasing:

KUBECONFIG=~/.kube/local-config helm upgrade kubevalet ./charts/kubevalet \
  -n kubevalet --reuse-values --set image.tag=0.3.13-dev

Reviewing PRs

Your own PRs

  1. Open a PR — pr-validate runs automatically on every push: go build, tests, helm lint, docker build.
  2. When all checks pass, pr-validate automatically adds the ok-to-test label.
  3. pr-image triggers and pushes a test image tagged <next-version>-<branch-name> to DockerHub (e.g. 0.3.19-my-feature).
  4. On the next push the label is re-cycled automatically — no manual action needed.
  5. When satisfied — squash merge.

Requires: a PAT_TOKEN secret in repo Settings → Secrets → Actions (classic PAT with repo scope). This is needed because GitHub does not fire webhook events for labels added by the built-in GITHUB_TOKEN.

External contributor PRs

  1. pr-validate runs automatically — go build, tests, helm lint, docker build (no push). No secrets, safe for forks.
  2. Review the code. If it looks good, add the label ok-to-test manually.
  3. pr-image triggers and pushes the test image to DockerHub.
  4. If the contributor pushes new commits, ok-to-test is removed automatically — re-review and re-label to build again.
  5. When satisfied — squash merge.

Release — when ready to ship

Version lives only in the git tag — no version bumping in files. CI reads the tag and injects it into the Docker image and Helm chart at build time.

  1. Open a PR → squash merge to main.

  2. On main after merge — tag and trigger CI:

make release VER=0.3.13   # patch
make release VER=0.4.0    # minor — new feature set, no breaking changes
make release VER=1.0.0    # major — breaking changes

CI always injects whatever version you pass. The decision of patch/minor/major is yours — it has no effect on the build process itself.

GitHub Actions builds in parallel:

  • Docker image truebad0ur/kubevalet:0.3.13 + latest → DockerHub
  • Helm chart 0.3.13 → ghcr.io → Artifact Hub
  1. Deploy:
KUBECONFIG=~/.kube/local-config helm upgrade kubevalet ./charts/kubevalet \
  -n kubevalet --reuse-values --set image.tag=0.3.13
KUBECONFIG=~/.kube/local-config kubectl rollout status deployment/kubevalet -n kubevalet

Build

Prerequisites: Docker with buildx, a builder instance.

Set your own image repo in Makefile:

IMAGE := your-dockerhub-user/kubevalet

Then build & push:

# One-time buildx setup
make buildx-setup

# Build & push multi-arch image (linux/amd64 + linux/arm64)
TAG=0.1.0 make docker-buildx-push

And point the Helm chart at your image:

helm install kubevalet ./charts/kubevalet \
  --set image.repository=your-dockerhub-user/kubevalet \
  --set image.tag=0.1.0 \
  ...

Single-arch local build:

make web-build   # build Vue frontend
make build       # compile Go binary → bin/kubevalet

Install (Helm)

helm install kubevalet ./charts/kubevalet \
  --namespace kubevalet --create-namespace \
  --set cluster.server=https://<your-api-server>:6443 \
  --set auth.adminPassword=changeme

Upgrade:

helm upgrade kubevalet ./charts/kubevalet --namespace kubevalet

Access UI:

kubectl port-forward svc/kubevalet 8080:80 -n kubevalet
# http://localhost:8080

Key values to change

Value Default Description
image.tag 0.3.16 Image tag
cluster.server https://kubernetes.default.svc.cluster.local API server URL embedded in kubeconfigs — set to the external address users will connect to (can also be changed at runtime in Settings UI)
cluster.name kubernetes Cluster name in kubeconfig context
auth.adminPassword admin Initial admin password
auth.jwtSecret (auto-generated) JWT signing secret — auto-generated on first install, preserved across upgrades
postgres.persistence.enabled false Enable PVC for PostgreSQL (requires a StorageClass)
ingress.enabled false Expose via Ingress
ingress.host kubevalet.example.com Ingress hostname

Local run (without Helm)

The binary is configured via environment variables. For production use Helm values instead — they map to the same vars automatically.

export POSTGRES_DSN=postgres://kubevalet:pass@localhost:5432/kubevalet
export JWT_SECRET=changeme
export CLUSTER_SERVER=https://your-api:6443   # URL that goes into kubeconfigs
export ADMIN_USERNAME=admin
export ADMIN_PASSWORD=changeme
# optional:
export KUBECONFIG=/path/to/kubeconfig         # defaults to in-cluster service account
export NAMESPACE=kubevalet                    # namespace where key Secrets are stored
export TOKEN_TTL=24h

make build
./bin/kubevalet

Multi-cluster

kubevalet can manage users and groups across multiple Kubernetes clusters from a single UI.

How it works

  • The default cluster is the one where kubevalet itself runs (in-cluster service account). It is created automatically on startup.
  • Additional clusters are added via the Clusters page (admin only) by pasting a kubeconfig with cluster-admin permissions.
  • Users and groups are isolated per cluster — switching clusters in the sidebar reloads all views for that cluster.
  • Kubeconfigs generated for users embed the api_server URL configured for each cluster (set at add time, editable in Settings).

Security note: Kubeconfigs for external clusters are stored as plaintext in PostgreSQL. Encrypt the database at rest before using in production.

Adding a cluster

  1. Go to Clusters in the sidebar.
  2. Click + Add Cluster.
  3. Fill in:
    • Name — identifier (DNS label format, e.g. prod-eu)
    • API Server — external URL that will be embedded in generated kubeconfigs (e.g. https://api.prod.example.com:6443)
    • Kubeconfig — paste a kubeconfig with cluster-admin access to the target cluster
  4. After adding, select the cluster from the sidebar dropdown to start managing its users and groups.

Switching clusters

The sidebar shows a cluster dropdown when more than one cluster is configured. Selecting a cluster reloads Users, Groups, Graph, and Settings for that cluster. Templates are global across all clusters.

Custom RBAC rules — API groups

Each rule covers exactly one API group. Resources from different groups must be split into separate rules:

Resource API Group
pods, secrets, configmaps, services `` (empty = core)
deployments, replicasets, statefulsets, daemonsets apps
cronjobs, jobs batch
ingresses networking.k8s.io
clusterroles, roles, rolebindings rbac.authorization.k8s.io

Wrongpods won't work because it's not in the apps group:

API Groups: apps    Resources: pods, deployments

Correct — two separate rules:

Rule 1 — API Groups: (empty)   Resources: pods
Rule 2 — API Groups: apps      Resources: deployments

Known limitations

  • Cluster kubeconfigs stored in plaintext. Multi-cluster support stores kubeconfigs (including credentials) in PostgreSQL without encryption. Anyone with read access to the database can extract cluster credentials. Encrypt the database at rest (e.g. via LUKS, cloud provider encryption, or pgcrypto) before using this in production. Application-level encryption may be added in a future release.

  • JWT role changes take effect only after token expiry. When an admin demotes another admin to viewer (or changes any role), the existing JWT is not invalidated — the affected user retains their previous role until their token expires (default TTL: 24 h). To force immediate effect, the user must log out and log in again. This is an inherent trade-off of stateless JWT auth; adding server-side token blacklisting would require a shared revocation store.

Roadmap

  • Screenshots in README
  • RBAC for local users (is_admin flag → admin can manage all users/passwords, regular users can only change own password)
  • Keycloak / OIDC integration
  • Multi-cluster support
  • Audit log (who created/deleted which user and when)
  • User expiry / certificate rotation reminders
  • Role templates (save and reuse custom RBAC configs)
  • LDAP sync
  • CVE scanning in CI (Trivy)
  • CI workflow for external PRs: pull_request (go build + test + helm lint + docker build --no-push, no secrets) and pull_request_target gated by ok-to-test label (build + push pr-{N} image to DockerHub, auto-remove label on new commits)
  • Write tests for all

About

Lightweight Kubernetes user management with a web UI

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors