Keycloak (Identity Provider / SSO)

OIDC authentication for kubectl and Single Sign-On for Grafana

Keycloak provides OIDC authentication for kubectl and Single Sign-On (SSO) for Grafana. It runs inside the Kubernetes cluster with a PostgreSQL backend and is exposed via Istio at https://keycloak.local.

Access

# Via Istio Gateway (requires /etc/hosts — see Quick Start step 5)
open https://keycloak.local

# Via NodePort (no /etc/hosts needed)
open http://192.168.56.10:30080

Admin credentials

URL:https://keycloak.local(orhttp://192.168.56.10:30080)Username:adminPassword:(fromVaultsecret/k8sprovisioner/apikeyskeycloakadminpassword)URL: https://keycloak.local (or http://192.168.56.10:30080) Username: admin Password: (from Vault → secret/k8s-provisioner/api-keys → keycloak_admin_password)

All Keycloak credentials are stored in Vault at secret/k8s-provisioner/api-keys:

Vault keyDescription
keycloak_admin_usernameAdmin console username (admin)
keycloak_admin_passwordAdmin console password
keycloak_postgres_usernamePostgreSQL username (keycloak)
keycloak_postgres_passwordPostgreSQL password
keycloak_grafana_client_secretGrafana OIDC client secret
keycloak_k8sadmin_passwordRealm user k8sadmin password
keycloak_developer_passwordRealm user developer password

To customize passwords before provisioning:

export VAULT_ADDR=http://192.168.56.20:8200
export VAULT_TOKEN=$(cat /etc/k8s-provisioner/vault-init.json | jq -r .root_token)

vault kv patch secret/k8s-provisioner/api-keys \
  keycloak_k8sadmin_password="MinhaSenh@Forte" \
  keycloak_developer_password="OutraSenha@123"

Note: Run this after Vault is initialized but before Keycloak is installed (or recreate the cluster).

Pre-configured realm: k8s

ResourceDetails
Realmk8s
kubectl clientkubectl (public, PKCE enabled)
Grafana clientgrafana (confidential)
Admin groupk8s-adminscluster-admin RBAC
Developer groupk8s-developersview RBAC
Test user (admin)k8sadmin / (Vault: keycloak_k8sadmin_password)
Test user (dev)developer / (Vault: keycloak_developer_password)

kubectl access

Quick access (admin)

# 1. Copy kubeconfig and fix the API server address
vagrant ssh controlplane -c "cat /etc/kubernetes/admin.conf" \
  | sed 's|https://.*:6443|https://192.168.56.10:6443|' \
  > ~/.kube/k8s-lab.conf

# 2. Use it
export KUBECONFIG=~/.kube/k8s-lab.conf
kubectl get nodes

This uses cluster-admin credentials. Suitable for day-to-day lab usage.

OIDC login via Keycloak (kubelogin)

Use this for role-based access — each user logs in with their own Keycloak credentials and gets the permissions of their group.

Keycloak groupKubernetes accessGrafana role
k8s-adminscluster-adminAdmin
k8s-developersview (read-only)Viewer

The kubeconfig-oidc file contains no credentials — it is safe to distribute. It is stored in Vault automatically during provisioning.

How authentication works

UserkubectlkubeloginbrowserKeycloakloginJWTtoken(containsgroupsclaim)kubeapiservervalidatestokenviaAuthenticationConfigurationgroupsprefix"oidc:"ClusterRoleBindingRBACUser → kubectl → kubelogin → browser → Keycloak login ↓ JWT token (contains groups claim) ↓ kube-apiserver validates token via AuthenticationConfiguration ↓ groups prefix "oidc:" → ClusterRoleBinding → RBAC

The API server uses AuthenticationConfiguration (/etc/kubernetes/pki/auth-config.yaml) to validate tokens:

apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
    url: https://keycloak.local/realms/k8s
    audiences:
    - kubectl
    - account
    audienceMatchPolicy: MatchAny
  claimMappings:
    username:
      claim: preferred_username
      prefix: "oidc:"
    groups:
      claim: groups
      prefix: "oidc:"

Group-based ClusterRoleBindings are applied automatically during provisioning:

# k8s-admins → cluster-admin
kind: ClusterRoleBinding
metadata:
  name: oidc-k8s-admins
subjects:
- kind: Group
  name: "oidc:k8s-admins"
---
# k8s-developers → view
kind: ClusterRoleBinding
metadata:
  name: oidc-k8s-developers
subjects:
- kind: Group
  name: "oidc:k8s-developers"

Admin: adding a new user

Access is group-based — no Kubernetes changes needed. Just create the user in Keycloak and assign the group.

Option A — Keycloak Admin Console:

  1. Open https://keycloak.local/admin → realm k8s → Users → Add user
  2. Set username, email, enable the user, save
  3. Go to Credentials tab → Set password
  4. Go to Groups tab → Join k8s-admins or k8s-developers

Option B — CLI (from controlplane):

KCADM="kubectl exec -n keycloak deploy/keycloak -- /opt/keycloak/bin/kcadm.sh"
$KCADM config credentials --server http://localhost:8080 --realm master \
  --user "$KEYCLOAK_ADMIN" --password "$KEYCLOAK_ADMIN_PASSWORD"

NEW_UID=$($KCADM create users -r k8s \
  -s username=alice \
  -s email=alice@example.com \
  -s firstName=Alice \
  -s lastName=Smith \
  -s enabled=true -i)
$KCADM set-password -r k8s --username alice --new-password 'Alice@K8s123'

GID=$($KCADM get groups -r k8s | grep -A1 k8s-developers | grep id | cut -d'"' -f4)
$KCADM update users/$NEW_UID/groups/$GID -r k8s -s realm=k8s -s userId=$NEW_UID -s groupId=$GID -n

New user: first-time setup (once)

# 1. Install kubelogin
brew install int128/kubelogin/kubelogin   # macOS

# 2. Add keycloak.local to /etc/hosts (required to reach the OIDC issuer)
INGRESS_IP=$(vagrant ssh controlplane -c "kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'" 2>/dev/null | tr -d '\r')
echo "$INGRESS_IP keycloak.local" | sudo tee -a /etc/hosts

# 3. Save the kubeconfig received from admin
mkdir -p ~/.kube
cp kubeconfig-oidc ~/.kube/k8s-lab.conf
export KUBECONFIG=~/.kube/k8s-lab.conf   # add to ~/.zshrc to persist

# 4. First login — opens browser at Keycloak
kubectl get nodes

Log in with your Keycloak credentials. The token is cached locally; the browser will not open again until it expires (24h).

Verify who you are authenticated as:

kubectl auth whoami
# ATTRIBUTE   VALUE
# Username    oidc:alice
# Groups      [oidc:k8s-developers system:authenticated]

Retrieving the kubeconfig-oidc from Vault

vagrant ssh storage

export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=$(sudo cat /etc/vault.d/vault-init.json | grep root_token | cut -d'"' -f4)

vault kv get -field=config secret/k8s-provisioner/kubeconfig-oidc > /tmp/kubeconfig-oidc
exit

Copy to your Mac:

vagrant scp storage:/tmp/kubeconfig-oidc ./kubeconfig-oidc

Grafana SSO

Grafana is configured to use Keycloak for SSO. Click “Sign in with Keycloak” on the Grafana login page.

UserGrafana Role
Users in k8s-admins groupAdmin
All other OIDC usersViewer

Local admin login still works: admin / (password from Vault).

Registering a new application in Keycloak

To protect a new application with Keycloak SSO, create a client in the k8s realm:

Via Admin Console:

  1. Open https://keycloak.local/admin → realm k8s → Clients → Create client
  2. Set clientId, choose OpenID Connect
  3. Enable Client authentication for confidential clients (backend apps)
  4. Set Valid redirect URIs to your app’s callback URL
  5. Copy the client secret from the Credentials tab

Via CLI:

KCADM="kubectl exec -n keycloak deploy/keycloak -- /opt/keycloak/bin/kcadm.sh"

CLIENT_ID=$($KCADM create clients -r k8s \
  -s clientId=my-app \
  -s publicClient=false \
  -s 'redirectUris=["https://my-app.local/*"]' \
  -i)

$KCADM get clients/$CLIENT_ID/client-secret -r k8s