Pinniped
Reference implementation of Pinniped, providing OIDC-based kubectl access to the cluster backed by the cluster's own Keycloak.
This implementation deploys only the Pinniped Concierge and authenticates users with a JWTAuthenticator against the cluster's Keycloak instance. The Pinniped Supervisor is not deployed — Keycloak is already the platform's OIDC provider, so the Concierge validates Keycloak-issued tokens directly.
The component is split into two Kustomize layers:
infrastructure/pinniped/controller/— the ConciergeHelmRelease.infrastructure/pinniped/config/— theJWTAuthenticatorand the RBACClusterRoleBindings.
Authentication with Keycloak
Users authenticate with the preconfigured pinniped-cli OIDC client in the havenplus realm in Keycloak.
The Concierge is told to trust that issuer through the JWTAuthenticator:
# infrastructure/pinniped/config/overlays/<cluster>/patches/jwt-authenticator.yaml
apiVersion: authentication.concierge.pinniped.dev/v1alpha1
kind: JWTAuthenticator
metadata:
name: jwt-authenticator
spec:
issuer: "https://keycloak.<cluster>.example/realms/havenplus"
audience: "pinniped-cli"
claims:
username: email
groups: groups
issuermust be Keycloak's canonical public hostname.audiencematches thepinniped-cliclient ID.claimsmap the token'semailto the Kubernetes username andgroupsto Kubernetes groups which we use in the RBAC bindings.
Authorization (RBAC)
Cluster permissions are configured using RBAC and maps the preconfigured groups in Keycloak to preconfigured ClusterRoles in Kubernetes:
Keycloak group (havenplus realm) | Bound ClusterRole | Effective access |
|---|---|---|
k8s-admins | cluster-admin | Full cluster administration |
k8s-developers | edit | Read/write most namespaced resources |
Impersonation proxy and networking
The Concierge uses the impersonation proxy with a ClusterIP Service, so no additional LoadBalancer is provisioned. On real clusters it is reached through the Gateway API with TLS passthrough (the proxy terminates its own mTLS, so the gateway must not decrypt):
# infrastructure/pinniped/controller/overlays/<cluster>/tlsroute-concierge.yaml
kind: TLSRoute
spec:
parentRefs:
- name: public-gateway
namespace: istio-gateway
sectionName: tls-passthrough
hostnames:
- "pinniped.<cluster>.example"
rules:
- backendRefs:
- name: pinniped-concierge-impersonation-proxy-cluster-ip
port: 443
The matching external endpoint is set in the overlay's patches/helm-release.yaml:
spec:
values:
concierge:
credentialIssuerConfig: |
impersonationProxy:
mode: enabled
externalEndpoint: "pinniped.<cluster>.example:443"
service:
type: ClusterIP
Configuring a cluster
For each cluster overlay:
config/overlays/<cluster>/patches/jwt-authenticator.yaml— setissuerto the cluster's Keycloak hostname (audience,claims, and the RBAC bindings are already standard).controller/overlays/<cluster>/patches/helm-release.yaml— set the impersonation proxyexternalEndpoint.controller/overlays/<cluster>/tlsroute-concierge.yaml— set thepinniped.<cluster>.examplehostname.
Granting a user kubectl access
- Add the user to a group in the
havenplusrealm of the cluster's Keycloak (k8s-adminsork8s-developers) — see the Grafana page for the step-by-step Keycloak Admin Console walkthrough; the groups are shared. - Distribute a Pinniped kubeconfig. An administrator generates it once per cluster with
pinniped get kubeconfig(pointed at the Concierge) and hands it to the user.
Before handing out the kubeconfig to the user, ensure to change the following:
- --scopes=offline_access,openid,pinniped:request-audience,username,groupsto- --scopes=openid.
- Log in. When the user runs
kubectl, Pinniped opens a browser against Keycloak, the user signs in, and the resulting token is exchanged with the Concierge for short-lived cluster credentials. Their permissions follow the group →ClusterRolemapping above.
Local development notes
The local (Kind) overlay differs from real clusters in two ways:
- Impersonation proxy is disabled (
mode: disabled) and there is noTLSRoute— a Kind cluster's API server is reachable directly, so the externally reachable impersonation proxy isn't needed. - The
JWTAuthenticatorissuer is the local Keycloak hostnamehttp://keycloak.local/realms/havenplus(HTTP, matching the local Keycloak overlay).