Skip to main content

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 Concierge HelmRelease.
  • infrastructure/pinniped/config/ — the JWTAuthenticator and the RBAC ClusterRoleBindings.

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
  • issuer must be Keycloak's canonical public hostname.
  • audience matches the pinniped-cli client ID.
  • claims map the token's email to the Kubernetes username and groups to 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 ClusterRoleEffective access
k8s-adminscluster-adminFull cluster administration
k8s-developerseditRead/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:

  1. config/overlays/<cluster>/patches/jwt-authenticator.yaml — set issuer to the cluster's Keycloak hostname (audience, claims, and the RBAC bindings are already standard).
  2. controller/overlays/<cluster>/patches/helm-release.yaml — set the impersonation proxy externalEndpoint.
  3. controller/overlays/<cluster>/tlsroute-concierge.yaml — set the pinniped.<cluster>.example hostname.

Granting a user kubectl access

  1. Add the user to a group in the havenplus realm of the cluster's Keycloak (k8s-admins or k8s-developers) — see the Grafana page for the step-by-step Keycloak Admin Console walkthrough; the groups are shared.
  2. 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,groups to - --scopes=openid.

  1. 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 → ClusterRole mapping above.

Local development notes

The local (Kind) overlay differs from real clusters in two ways:

  • Impersonation proxy is disabled (mode: disabled) and there is no TLSRoute — a Kind cluster's API server is reachable directly, so the externally reachable impersonation proxy isn't needed.
  • The JWTAuthenticator issuer is the local Keycloak hostname http://keycloak.local/realms/havenplus (HTTP, matching the local Keycloak overlay).