Skip to content

Try it locally in 15 minutes

Does your company already have kubelatch?

You don't need this tutorial: go to Sign in and request access.

In this tutorial you set up kubelatch on your machine with a disposable kind cluster. You'll grant yourself a permission, use kubectl through kubelatch, see every request in the audit log, and revoke the credential. All with local passwords, no GitHub.

It will take about 15 minutes, plus the first-time downloads (Go and Node dependencies, the Postgres image and the kind node image).

Before you start

Tool Version What for
Go 1.26 or later Build kubelatch
Node.js 22.22 or later Build the web interface
Docker with Compose Recent Postgres and the kind cluster
kind 0.24 or later The test cluster. kind 0.20 creates Kubernetes 1.27, too old
kubectl 1.31 or later Talk to the cluster
git, make and openssl Any Clone, build and generate the test certificate

Check the versions with go version, node --version, kind version and kubectl version --client.

You'll use two terminals, both at the root of the repository. Terminal 1 runs kubelatch; everything else goes in terminal 2, in the same shell session, because the steps reuse variables.

Your ~/.kube/config is not touched

If your ~/.kube/config points at real clusters, a plain kubectl copied from here would act on them. That's why every kubectl in this tutorial carries --kubeconfig with a file from the repository itself. Don't run kubectl without it, and don't run kubectl config use-context while following it.

1. Clone the repository

git clone <repo-url> kubelatch && cd kubelatch   # the URL is given to you by the team that maintains kubelatch
cp .env.example .env

.env brings development configuration: Postgres on 127.0.0.1:5433 and a 32-byte all-zero encryption key. Never use those values in a real environment.

2. Start Postgres

make dev-db

Brings up a Postgres 17 in Docker that listens only on 127.0.0.1:5433, so it doesn't clash with a local Postgres on 5432. Data is stored in a Docker volume.

3. Build the web interface

make web

Installs the interface's dependencies and builds it into web/dist. The kubelatch binary embeds it at build time, so do this before the next step.

4. Start kubelatch with TLS

In terminal 1:

make dev-tls

Builds bin/kubelatch, generates a self-signed certificate in .dev/tls/ and leaves kubelatch listening on https://localhost:8443, with logs in JSON. HTTPS is required because kubectl only sends tokens over HTTPS. Leave it running.

Check it responds from terminal 2:

curl --cacert .dev/tls/server.crt https://localhost:8443/readyz    # ok

5. Create the first administrator

In terminal 2. The CLI reads the same variables as the server:

set -a && . ./.env && set +a
bin/kubelatch user create admin --admin --display-name "Admin"

It asks for the password twice, without echo (Password: and Repeat password:). It must be at least 12 characters. It ends with user admin created (id …, admin=true, break_glass=false).

6. Sign in to the interface

Open https://localhost:8443 in the browser. It will warn you that the certificate isn't trusted: it's the self-signed one from step 4, accept it for this test.

Type admin in Usuario (User), your password in Contraseña (Password) and click Entrar (Sign in). You'll see Hola, Admin (Hi, Admin) and the menu: Inicio (Home), Mi cuenta (My account), Clusters, Permisos (Permissions), Credenciales (Credentials), CI, Auditoría (Audit) and Usuarios (Users).

Use localhost as is, not 127.0.0.1: kubelatch only accepts interface requests from its own URL.

7. Create the kind cluster

e2e/kind.sh up
KIND_KC="$PWD/e2e/.kind/admin.kubeconfig"
kubectl --kubeconfig "$KIND_KC" get pods

The script creates the kubelatch-e2e cluster in about half a minute and writes its admin kubeconfig to e2e/.kind/admin.kubeconfig, never to ~/.kube/config. It also starts two test pods in default: web and ticker. get pods shows them as Running.

e2e/kind.sh uses ~/.local/bin/kind and ~/.local/bin/kubectl if they exist, and falls back to the ones on PATH otherwise. To use others: KIND=/path/to/kind KUBECTL=/path/to/kubectl e2e/kind.sh up.

Now label the default namespace with Pod Security Admission baseline:

kubectl --kubeconfig "$KIND_KC" label namespace default pod-security.kubernetes.io/enforce=baseline

You need this because in step 9 you'll grant yourself the debugger tier, which kubelatch only grants in namespaces that enforce PSA baseline or restricted. Permission tiers explains why.

8. Register the cluster in kubelatch

kubelatch needs two ServiceAccounts in each cluster: one to forward your requests and another to maintain RBAC. Registering the cluster creates them and passes their tokens to kubelatch.

  1. Go to Clusters. Type kind in Identificador (slug, va en la URL del proxy) (Identifier (slug, goes in the proxy URL)) and kind local in Nombre (Name), and click Registrar (Register). The cluster shows up as Sin tokens (No tokens) and the Bootstrap de kind local (Bootstrap for kind local) dialog opens.

  2. Click descargar bootstrap.yaml (download bootstrap.yaml). The browser saves kubelatch-bootstrap-kind.yaml. Apply it to the kind cluster, changing the path if your browser downloads elsewhere:

    kubectl --kubeconfig "$KIND_KC" apply -f ~/Downloads/kubelatch-bootstrap-kind.yaml
    

    Creates the kubelatch-system namespace and the two ServiceAccounts, with minimal permissions.

  3. Click Copiar (Copy) next to One-liner and run it like this, pasting it in place of <one-liner>:

    ( export KUBECONFIG="$KIND_KC"; <one-liner> )
    

    The one-liner uses kubectl's current context. The export inside the parentheses sets it to the kind cluster only for that subshell, without touching your session or ~/.kube/config. It prints a line of JSON: {"server":"https://127.0.0.1:…","ca":"…","proxyToken":"…","reconcilerToken":"…"}.

  4. Copy that whole line, paste it into JSON de los tokens (Tokens JSON) and click Guardar tokens (Save tokens).

kubelatch validates the tokens against the cluster, stores them encrypted and runs the first reconciliation. The dialog closes and the cluster moves to Listo (Ready), with the Kubernetes version under the URL. Última reconciliación (Last reconciliation) shows — until that first pass finishes: reload the page and you'll see the date. If you click Namespaces, default shows up with PSA baseline.

If you closed the dialog too soon, Bootstrap y tokens (Bootstrap and tokens) reopens it.

9. Grant yourself a permission

Being a kubelatch administrator doesn't grant cluster access: you also need a permission.

  1. Go to Permisos.
  2. Choose admin (Admin) in Sujeto (Subject), kind in Cluster, viewer in Nivel (Tier) and default (PSA baseline) in Ámbito (Scope). Leave Expira (Expires) empty and click Conceder (Grant).
  3. Repeat with debugger in Nivel.

The table shows both permissions as Activo (Active). viewer lets you read almost everything in default except secrets; debugger adds exec, attach and port-forward. Within a second or two, kubelatch creates the corresponding RoleBindings in the cluster.

10. Issue your credential

  1. Go to Inicio. Under Mis accesos (My access) you'll see your two permissions, each with its group: kubelatch:ns:default:viewer and kubelatch:ns:default:debugger.
  2. Under Mis credenciales (My credentials), type tutorial in Nombre. Leave Duración (Duration) at 7 días (7 days) and Cluster at Todos los que tenga permitidos (All I'm allowed to use). Click Emitir credencial (Issue credential).
  3. Credencial emitida (Credential issued) opens with the token and the kubeconfig. They're only shown this once. Click Descargar kubeconfig (Download kubeconfig): the browser saves kubelatch-tutorial.yaml.

Save it in a file of its own, inside .dev/ (git ignores it), and add the self-signed certificate to it:

mv ~/Downloads/kubelatch-tutorial.yaml .dev/tutorial.kubeconfig
KL_KC="$PWD/.dev/tutorial.kubeconfig"
kubectl --kubeconfig "$KL_KC" config set-cluster kind --certificate-authority="$PWD/.dev/tls/server.crt" --embed-certs=true

The kubeconfig kubelatch issues carries no CA, because in a real deployment kubelatch serves a public certificate (or a private CA with KUBELATCH_KUBECONFIG_CA). set-cluster only modifies that file.

11. Use kubectl through kubelatch

kubectl --kubeconfig "$KL_KC" auth whoami
kubectl --kubeconfig "$KL_KC" get pods
kubectl --kubeconfig "$KL_KC" exec web -- sh -c 'echo hola'
ATTRIBUTE   VALUE
Username    user:admin
UID         35b57553-…
Groups      [kubelatch:ns:default:debugger kubelatch:ns:default:viewer system:authenticated]
NAME     READY   STATUS    RESTARTS   AGE
ticker   1/1     Running   0          3m
web      1/1     Running   0          3m
hola

Every request reaches kubelatch with your token. kubelatch checks that the credential is valid, logs it and forwards it to the cluster with impersonation: it acts as user:admin with the groups from your permissions. The cluster decides with its own RBAC. The journey of a request walks through it step by step.

12. Try something you can't do

kubectl --kubeconfig "$KL_KC" get secrets
Error from server (Forbidden): secrets is forbidden: User "user:admin" cannot list resource "secrets" in API group "" in the namespace "default"

viewer doesn't include secrets, and neither does debugger. It's the cluster itself that answers 403: kubelatch only tells it who you are.

13. Look at the audit log

  • On Inicio, Mi actividad (My activity) lists every request made with your credentials: create of selfsubjectreviews (the whoami), list of pods (default) with 200, exec of pods/exec web (default) with 101, and list of secrets (default) with 403.
  • Click a row's date to see the Detalle de la petición (Request detail): the Audit-ID (the same one the cluster gets), the Grupos suplantados (Impersonated groups), the IP and the User-Agent.
  • On Auditoría, the Peticiones a los clusters (Requests to clusters) tab shows the same for every subject, with filters. The Plano de control (Control plane) tab shows what you did in the interface: user.create, cluster.create, cluster.tokens, grant.create, credential.issue…

14. Revoke the credential

  1. On Inicio, under Mis credenciales, click Revocar (Revoke) on the tutorial row.
  2. The browser asks for a reason (optional). Type fin del tutorial (end of tutorial) and confirm. The status changes to Revocada el … (Revoked on …).

Try again:

kubectl --kubeconfig "$KL_KC" get pods
error: You must be logged in to the server (credencial revocada)

That's a 401: the credential stopped being valid instantly. An exec, logs -f or port-forward that was open with it would have been cut off in under 10 seconds.

The attempt also shows up in Auditoría. Check Incluir descubrimiento (Include discovery) and click Buscar (Search): a row with no subject appears, with the token's prefix (klt_…) and status 401 credencial revocada (401 credential revoked).

15. Clean up

Stop kubelatch with Ctrl-C in terminal 1. Then, in terminal 2:

e2e/kind.sh down                  # deletes the kubelatch-e2e cluster and e2e/.kind/
make dev-db-down                  # stops Postgres; data stays in the volume
rm .dev/tutorial.kubeconfig ~/Downloads/kubelatch-bootstrap-kind.yaml

If you repeat the tutorial with the same database, your admin already exists: skip step 5. To start from scratch, also delete the volume with docker compose down -v.

If something goes wrong

Symptom What to do
make dev-db fails because port 5433 is taken Change the port in docker-compose.yml and in DATABASE_URL in .env.
/readyz answers 503 Postgres isn't started: make dev-db.
The browser shows SPA no construida (SPA not built) You skipped make web. Run it, stop kubelatch with Ctrl-C and relaunch make dev-tls.
The interface answers petición de otro origen rechazada (cross-origin request rejected) You opened https://127.0.0.1:8443. Use https://localhost:8443.
user create answers that login already exists The admin is from a previous run. Sign in with it, or set another password with bin/kubelatch user set-password admin.
Conceder answers el nivel debugger exige que el namespace default tenga la etiqueta … (tier debugger requires namespace default to have the label …) The PSA label from step 7 is missing. Apply it and grant again.
kind creates Kubernetes 1.27 and Guardar tokens fails Your kind is old. Install kind 0.24 or later (or point to it with KIND=), run e2e/kind.sh down and go back to step 7.
kubectl answers x509: certificate signed by unknown authority The set-cluster from step 10 is missing.
kubectl answers the server has asked for the client to provide credentials The kubeconfig points at http://…: kubectl doesn't send the token without TLS. Use make dev-tls and https://localhost:8443.
kubectl answers certificate has expired The make dev-tls certificate lasts 30 days. Delete .dev/tls/, relaunch make dev-tls and repeat the set-cluster from step 10.

For everyday errors with a credential, see If something fails. When you're done, Next steps tells you what to read depending on your role.