Skip to content

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.

The recommended path on macOS is Homebrew:

Terminal window
brew install kubelogin

If 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.

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: v1
kind: Config
clusters:
- name: kestrel
cluster:
server: https://proxy.kestrel.arbutus.cloud
contexts:
- name: kestrel
context:
cluster: kestrel
user: kestrel-oidc
current-context: kestrel
users:
- 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: false

Your tenant name is 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. See Identity chain for why the tenant name matches the Cloud RAP group.

Run any read-only kubectl command that hits the apiserver. The simplest is:

Terminal window
kubectl get ns

On the first command, kubelogin opens your default browser to the Keycloak login page. Authenticate with your Alliance CCDB credentials — Keycloak’s upstream identity provider is Alliance LDAP, so the same CCDB username and password you use at ccdb.alliancecan.ca work here. Keycloak redirects back to a local port (localhost:8000 by default) that kubelogin is listening on, exchanges the authorization code for an id_token, caches it under ~/.kube/cache/oidc-login/, and returns the token to kubectl. The kubectl get ns command then completes and prints the namespaces you are allowed to see — at least one will start with your Cloud RAP group name (for example, crg-profname-01-experiments if your PI’s Cloud RAP is crg-profname-01).

Subsequent kubectl commands reuse the cached token until it expires, at which point kubelogin silently refreshes it against Keycloak without prompting you again.

Symptom: browser shows “Unable to connect” after the Keycloak login page; kubelogin in the terminal hangs.

Cause: another process already bound localhost:8000 — a dev server, a previous kubelogin session, or a Docker port publish.

Fix: override the listen address with --listen-address=localhost:18000 in the kubeconfig exec.args, or set KUBELOGIN_LISTEN_ADDRESS=localhost:18000 in your shell environment before running kubectl. RCS has registered multiple candidate ports with Keycloak, so the alternate port is accepted as a redirect target.

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:

Terminal window
kubectl oidc-login clean
kubectl get ns

If it persists, open a fresh browser window (or an incognito session) so the Keycloak session cookie is new.

Symptom: kubectl get ns hangs; no browser window appears.

Cause: you are running inside WSL2, tmux, or a remote SSH session with no local GUI — kubelogin has no default browser to open.

Fix: add --skip-open-browser to the exec.args in your kubeconfig, then copy the printed URL into a local browser manually and complete the OAuth flow. If the shell is remote, forward the callback port first:

Terminal window
ssh -L 8000:localhost:8000 <user>@<remote-host>

The browser on your local workstation will then reach localhost:8000 and hand the authorization code back to the kubelogin process running on the remote host.

kubectl get ns returns nothing after a clean login

Section titled “kubectl get ns returns nothing after a clean login”

Symptom: the OIDC browser flow completes and kubelogin writes a token cache successfully, but kubectl get ns returns zero namespaces — not an error, just an empty result.

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 Cloud RAP group that maps to a Capsule Tenant on Kestrel. The two common reasons:

  1. Your PI has not added you to the Cloud RAP in CCDB. Cloud RAPs do not auto-add sponsored users — this is the single most common failure mode on first login. Ask your PI to confirm you are listed on their Cloud RAP at ccdb.alliancecan.ca.
  2. Alliance LDAP propagation delay. Your PI added you, but the LDAP read happened before propagation finished. Usually immediate, occasionally a few minutes.

Fix: once your PI has added you (or after waiting for propagation), clear the cached token and force a fresh login:

Terminal window
# You own this — clears the stale OIDC cache so the next kubectl triggers a fresh login
kubectl oidc-login clean
kubectl get ns

If kubectl get ns still returns nothing 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.