Sign-in
OIDC providers: what Harthelm needs from one, provider notes and the break-glass admin.
People sign in to Harthelm through an OpenID Connect provider. Harthelm keeps no passwords, except for one optional local emergency account.
What the provider must offer
| Requirement | Why |
|---|---|
| The authorization code flow with PKCE and a confidential client | how Harthelm signs people in |
Redirect URI https://<console>/auth/oidc/callback | the callback |
The scopes openid profile email groups | name, email, picture and groups. Harthelm always requests all four |
| A groups claim in the ID token | Members of oidc.adminGroup (default harthelm-admins) are cluster admins. The claim is oidc.groupsClaim (default groups), a list or a single string, and the flag is recomputed at every sign-in |
email_verified: true for addresses the provider has verified | Invitations attach to a user only by a verified address |
Configure it
kubectl -n harthelm-system create secret generic harthelm-oidc \
--from-literal=clientID=<client id> --from-literal=clientSecret=<client secret>oidc:
issuer: https://id.example.com/
displayName: Example ID # the button says "Sign in with Example ID"
adminGroup: harthelm-admins
existingSecret: harthelm-oidc| Value | Meaning |
|---|---|
oidc.issuer | The issuer URL. Empty turns OIDC off. |
oidc.existingSecret | The client's id and secret. oidc.clientIDKey and oidc.clientSecretKey name its keys, so an existing Secret needs no rewrite. |
oidc.caConfigMap | A ConfigMap with an extra CA (key oidc.caKey, default ca.crt) for an issuer behind a private CA. |
oidc.trustEmailVerified | false treats every address of the issuer as unverified. Invitations then cannot be accepted; use it only for an issuer that cannot tell. |
With networkPolicy.enabled, core reaches the provider through networkPolicy.egress.idp (port 443); narrow its
to to the provider's addresses.
The issuer is discovered at the first sign-in, not when core starts, so core comes up while the provider is down.
Provider notes
| Provider | Notes |
|---|---|
| Dex | Marks the addresses of static users and of connectors that verify addresses as verified. |
| Authentik | Its built-in email scope mapping sends email_verified: false for everyone. Use a scope mapping that sets it for users whose address you trust, for example users an admin created. |
| Keycloak and other full providers | Configure a confidential client with the redirect URI, a groups scope that puts the group names into the ID token, and verified emails. |
| Microsoft Entra ID | Sends no email_verified, emits group object IDs by default, and reportedly refuses the scope groups. Put a broker in front. See the AKS track. |
| Amazon Cognito | The groups claim is cognito:groups; set oidc.groupsClaim to it. Whether it accepts the scope groups is not verified. |
| No groups claim, so nobody becomes cluster admin through it; use a broker. |
A broker is an OIDC provider of your own (Dex, Keycloak, Authentik) that signs people in through the upstream provider and issues the groups and verified emails Harthelm needs.
Dex with a GitHub connector
Dex in front of GitHub gives a small installation sign-in through GitHub organizations and teams and no other user store. With one connector Dex skips its own sign-in page.
issuer: https://auth.example.org
storage: {type: kubernetes, config: {inCluster: true}}
web: {http: 0.0.0.0:5556}
oauth2: {skipApprovalScreen: true}
enablePasswordDB: false
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $GITHUB_CLIENT_ID
clientSecret: $GITHUB_CLIENT_SECRET
redirectURI: https://auth.example.org/callback # also the callback URL of the GitHub OAuth app
orgs:
- name: example-org
teamNameField: slug
staticClients:
- id: harthelm
name: Harthelm
secretEnv: HARTHELM_CLIENT_SECRET
redirectURIs: [https://console.example.org/auth/oidc/callback]oidc:
issuer: https://auth.example.org
displayName: GitHub
adminGroup: example-org:harthelm-admins
existingSecret: harthelm-oidc # clientID: harthelm, clientSecret: the value of HARTHELM_CLIENT_SECRET- Groups arrive as
<org>:<team>.teamNameField: slugmakes the team part the team's slug (harthelm-admins), not its display name. Only members oforgscan sign in at all, and the members of the organization'sharthelm-adminsteam are the cluster admins. Core recomputes admin rights fromadminGroupat every sign-in, so removing someone from the team takes the right away at their next sign-in. - Dex asks GitHub for the
read:orgscope to read teams; an organization that restricts third-party OAuth apps has to approve the app. - Dex marks the address of a verifying connector as verified (
email_verified: true), which invitations need. A GitHub user without a verified address has none to match. - With Dex in the cluster behind the same Ingress, Without a load balancer shows how core reaches it.
- When Dex or GitHub is down nobody can sign in. Enable the break-glass account as the way in for that case.
Sign-in errors
A failed sign-in returns to the sign-in page with a code that the page explains:
| Code | Meaning |
|---|---|
idp_unavailable | No issuer is configured, it cannot be reached, or core's database failed. |
login_expired | The sign-in took longer than 10 minutes, was used already, or another tab started a newer one. Start again. |
access_denied | The provider refused: the user cancelled or has no access to the application. |
account_disabled | The user is disabled in Harthelm. |
login_failed | Anything else: a token the provider would not issue, a bad ID token, a wrong issuer or nonce. |
Sessions
A console session ends after 12 hours without a request, or 7 days after sign-in. Disabling a user ends their sessions at their next request.
Break-glass admin
A local admin for when the OIDC provider is down. Make an argon2id hash of a password and store it with a user name:
echo -n "$PASSWORD" | argon2 "$(openssl rand -base64 16)" -id -t 3 -m 16 -p 4 -e
kubectl -n harthelm-system create secret generic harthelm-break-glass \
--from-literal=username=admin --from-literal=password-hash='<the hash>'auth:
breakGlass: {enabled: true, existingSecret: harthelm-break-glass}
mail:
breakGlassTo: [email protected]The sign-in page then links Use the break-glass account. A break-glass sign-in is a cluster admin, the console
shows it with a red banner, it is audited with the client address and mailed to mail.breakGlassTo. After 10
failures within 15 minutes every attempt is refused until the oldest failure leaves the window. The Secret is read at
every attempt, so a rotated password applies without a restart.