# cluster-inspector: credentials setup The `cluster-inspector` agent (`.claude/agents/cluster-inspector.md`) only ever talks to clusters through the read-only kubeconfigs in `kubeconfigs/` here. They're gitignored and minted by `make-kubeconfig.sh` from a short-lived ServiceAccount token. | File | Purpose | | --- | --- | | `rbac.yaml` | ServiceAccount `kube-system/readonly-inspector` + `view` + read on CAPI CRDs and pod logs (no Secrets) | | `make-kubeconfig.sh` | applies RBAC (once), mints a TokenRequest token, writes `kubeconfigs/.kubeconfig`, runs `can-i` checks | | `kubeconfigs/` | output, **gitignored**; one file per cluster, context `-ro` | ## 1. Management cluster (after phase 1 step 2) k3s writes `server: https://127.0.0.1:6443`, so pass the VM address. k3s puts node IPs into the API cert SANs, so the IP works; add `--tls-san` for a DNS name. ```bash scp ubuntu@:/etc/rancher/k3s/k3s.yaml /tmp/lab-mgmt-admin.yaml # or use your existing admin kubeconfig .claude/cluster-inspector/make-kubeconfig.sh lab-mgmt \ --admin-kubeconfig /tmp/lab-mgmt-admin.yaml \ --server https://:6443 \ --apply-rbac rm /tmp/lab-mgmt-admin.yaml ``` ## 2. Workload clusters (after phase 1 step 3) Add the cluster to `inspector_workload_clusters` in `ansible/group_vars/all.yml`. Then `ansible-playbook mgmt.yml --tags inspector` mints a read-only kubeconfig for lab-mgmt and for every listed cluster whose `-kubeconfig` Secret exists. Clusters without that Secret are skipped. Doing it by hand gives the same result. `--capi-cluster` reads the workload admin kubeconfig from the management cluster's Secret into a temp file that is deleted on exit: ```bash .claude/cluster-inspector/make-kubeconfig.sh lab1 \ --admin-kubeconfig ~/.kube/lab-mgmt.kubeconfig \ --capi-cluster default/lab1 \ --apply-rbac ``` ## Expected verification output ```text list pods yes get machines.cluster.x-k8s.io yes # "no" on workload clusters (no CAPI CRDs), fine delete machines.cluster.x-k8s.io no patch machinedeployments.cluster.x-k8s.io no get secrets no ``` Anything else means the RBAC is wrong. Don't use the agent until it matches. ## Refresh / revoke - **Refresh:** `ansible-playbook mgmt.yml --tags inspector` refreshes all clusters. You can also re-run the same script command, with or without `--apply-rbac`; it's idempotent. The default lifetime is 7 days (`--duration`); the API server may cap it (`--service-account-max-token-expiration`). - **Revoke everything:** `kubectl delete sa -n kube-system readonly-inspector` (as admin). That invalidates every minted token; re-apply `rbac.yaml` to start over. ## Making read-only the default for this repo (recommended) RBAC only protects you when the read-only kubeconfig is actually used. Your admin `~/.kube/config` is still reachable from any shell. To make Claude Code sessions in this repo default to the read-only files, add this to `.claude/settings.local.json` (personal, not committed): ```json { "env": { "KUBECONFIG": "/Users/jan.novak/srv/go/cluster-autoscaler-with-capi/.claude/cluster-inspector/kubeconfigs/lab-mgmt.kubeconfig:/Users/jan.novak/srv/go/cluster-autoscaler-with-capi/.claude/cluster-inspector/kubeconfigs/lab1.kubeconfig" } } ``` Then plain `kubectl --context lab-mgmt-ro …` / `--context lab1-ro …` works everywhere in the session, and mutations fail on RBAC even if something tries them.