Install kubelogin
kubelogin is a kubectl credential plugin that runs the OIDC browser flow against Keycloak, caches the token, and feeds it to the Kestrel kube-apiserver. The upstream project is int128/kubelogin. There is an unrelated binary also called kubelogin maintained by Microsoft for Azure AD — that is not what Kestrel uses. If your package manager offers both, pick the int128/kubelogin build.
For the mental model of how kubelogin sits between Keycloak, the kube-apiserver, and Capsule, see Identity chain.
Install
Section titled “Install”The recommended path on macOS is Homebrew:
brew install kubeloginIf Homebrew is not available, download the kubelogin_darwin_arm64.zip or kubelogin_darwin_amd64.zip archive from the upstream release page, extract it, and put the kubelogin binary on your PATH.
The recommended path on Linux is Krew:
kubectl krew install oidc-loginWhen installed via Krew, the plugin is invoked as kubectl oidc-login (not kubelogin). Your kubeconfig exec.command should be kubectl with args: [oidc-login, get-token, ...].
Alternatively, install via Linuxbrew (brew install kubelogin) or download the kubelogin_linux_amd64.zip archive from the upstream release page. kubelogin is not packaged in apt, yum, or dnf repositories.
The recommended path on Windows is Chocolatey:
choco install kubeloginAlternatively, install via Krew (kubectl krew install oidc-login) or download the kubelogin_windows_amd64.zip archive from the upstream release page, extract it, and put kubelogin.exe on your PATH.
Configure your kubeconfig
Section titled “Configure your kubeconfig”Paste the following block into ~/.kube/config (merge with any existing config carefully — or use KUBECONFIG=~/.kube/config-kestrel kubectl ... to keep it isolated while you test).
apiVersion: v1kind: Configclusters: - name: kestrel cluster: server: https://proxy.kestrel.arbutus.cloudcontexts: - name: kestrel context: cluster: kestrel user: kestrel-oidccurrent-context: kestrelusers: - name: kestrel-oidc user: exec: apiVersion: client.authentication.k8s.io/v1 command: kubectl args: - oidc-login - get-token - --oidc-issuer-url=https://keycloak.arbutus.alliancecan.ca/realms/atmosphere - --oidc-client-id=kubernetes - --token-cache-storage=keyring interactiveMode: IfAvailable provideClusterInfo: falseFirst login
Section titled “First login”Run any read-only kubectl command that hits the apiserver. The simplest is:
kubectl auth whoamiOn the first command:
kubeloginopens your default browser to the Keycloak login page.- Log in with your Alliance CCDB username and password — the same ones you use at
ccdb.alliancecan.ca. - Keycloak redirects the browser back to
localhost:8000, wherekubeloginis listening. Once the page says login is complete, you can close the tab. kubelogincaches the token under~/.kube/cache/oidc-login/and hands it tokubectl, and the command completes:
❯ kubectl auth whoamiATTRIBUTE VALUEUsername testuserGroups [oidc:def-testuser system:authenticated]The oidc: group is your tenant name — your Cloud RAP’s Alliance LDAP group name, one of def-profname, crg-profname-xx, or cpp-profname-xx (ask your PI if you don’t know yours). This group claim in the token is what grants you access to your tenant — nothing in your kubeconfig names it. See Identity chain for how the pieces connect.
Subsequent kubectl commands reuse the cached token until it expires, at which point kubelogin silently refreshes it against Keycloak without prompting you again.
Troubleshooting
Section titled “Troubleshooting”Port 8000 already in use
Section titled “Port 8000 already in use”Symptom: browser shows “Unable to connect” after the Keycloak login page; kubelogin in the terminal hangs.
Cause: kubelogin listens on localhost:8000 and automatically falls back to localhost:18000 if 8000 is taken. This symptom therefore means both default ports are already bound — dev servers, previous kubelogin sessions, Docker port publishes — or the fallback port is blocked.
Fix: free up one of the two default ports, or override the listen address with --listen-address=localhost:18000 in the kubeconfig exec.args (the flag can be repeated to list multiple candidate ports). RCS has registered multiple candidate ports with Keycloak, so the alternate port is accepted as a redirect target.
State does not match
Section titled “State does not match”Symptom: browser reaches the Keycloak login page but returns an error page after you authenticate (“State does not match” or a similar OIDC state-mismatch message).
Cause: stale OIDC state in the local cache — usually from a previous aborted login, or a duplicate browser tab that followed an old callback link.
Fix: clear the cache and retry:
kubectl oidc-login cleankubectl auth whoamiIf it persists, open a fresh browser window (or an incognito session) so the Keycloak session cookie is new.
Browser does not open, or opens but the login never completes
Section titled “Browser does not open, or opens but the login never completes”Symptom: kubectl auth whoami hangs with no browser window, or a browser opens but errors out before or after the Keycloak login page.
Cause: kubelogin launches whatever the OS reports as the default browser. This fails when there is no GUI at all (WSL2, tmux, remote SSH sessions) and can misbehave with sandboxed browser installs (Flatpak/Snap browsers on Linux are a known culprit) or a stale default-browser registration.
Fix (local machine): add --skip-open-browser to the exec.args in your kubeconfig. Instead of launching a browser, kubelogin prints the login URL — copy it into any working browser on the same machine and complete the login. No tunnelling or SSH is involved: the URL points at localhost:8000, where kubelogin is already listening.
Fix (remote shell — WSL2, SSH, tmux): same --skip-open-browser, but because the browser runs on a different machine than kubelogin, forward the callback port first:
ssh -L 8000:localhost:8000 <user>@<remote-host><remote-host> is the machine you run kubectl on — not a Kestrel or Keycloak address. The browser on your local workstation then reaches localhost:8000 through the tunnel and hands the authorization code back to the kubelogin process on the remote host.
kubectl auth whoami is missing your tenant group
Section titled “kubectl auth whoami is missing your tenant group”Symptom: the OIDC browser flow completes and kubelogin writes a token cache successfully, but the Groups row in kubectl auth whoami only shows system:authenticated — no oidc:<your-tenant> entry.
(An empty kubectl get ns is not this failure — a fresh tenant has no namespaces until you create one. If whoami shows your oidc: tenant group, login is working; carry on to Your first deployment.)
Cause: you have successfully authenticated to Keycloak and the kube-apiserver has accepted your identity, but the groups claim in your OIDC id_token does not contain any tenant group that maps to a Capsule Tenant on Kestrel. The two common reasons:
- You have not been added to the tenant’s group yet. Tenant membership is currently managed by RCS staff in Keycloak — sponsorship in CCDB alone does not grant access. This is the single most common failure mode on first login. Ask your PI to confirm the add-user request to RCS was made and completed — see Managing membership.
- Your CCDB account is brand-new. Keycloak’s user store federates from the Alliance (CCDB) LDAP; a just-created account can take a sync cycle before RCS can add it to the tenant group.
Fix: once RCS has confirmed the add, clear the cached token and force a fresh login:
# You own this — clears the stale OIDC cache so the next kubectl triggers a fresh loginkubectl oidc-login cleankubectl auth whoamiIf the tenant group is still missing after a fresh token, the problem is upstream of Keycloak. See Requesting access for the end-to-end CCDB flow, and contact accounts@tech.alliancecan.ca for CCDB-level issues.