Identity¶
How kubelatch knows who you are, how your account is linked to GitHub, and what happens to your access when you leave the organization. This also explains why break-glass accounts exist.
Subjects: people and bots¶
Anything that can hold permissions in kubelatch is a subject. There are two kinds:
- People. They sign in to the web interface, request credentials for themselves, and can be administrators.
- Bots. They have no password or session: only credentials. An admin issues theirs from Credenciales (Credentials), or CI obtains them by exchanging its GitHub Actions OIDC token (see trust rules).
Each subject has an immutable login that's never reused, not even after the account is disabled. It's the name clusters see it by: user:<login> for people and bot:<name> for bots, plus the subject's internal identifier as uid. If a login were recycled, the cluster's native audit log would mix up two different people.
There's no self-registration. An admin creates accounts from Usuarios (Users) and shares a single-use invitation link (<KUBELATCH_BASE_URL>/cuenta#kli_…, valid for 72 h). The token travels in the URL fragment, so it never reaches any server's logs. The other route is the binary's CLI (see CLI).
Two modes: passwords or GitHub¶
kubelatch runs in one of two modes, depending on whether the GITHUB_* variables are set:
| Without GitHub | With the GitHub App | |
|---|---|---|
| How people sign in | Local login and password | Entrar con GitHub (Sign in with GitHub) |
| Passwords | All accounts | Only break-glass accounts |
| Two-factor | No | Whatever the GitHub organization enforces |
| Offboarding | Manual | Automatic on leaving the organization |
The mode without GitHub has neither two-factor nor automatic offboarding: it's meant for trying out kubelatch or for environments without GitHub (see Security model). In production, configure the GitHub App (GitHub login).
Switching modes doesn't touch accounts: same login, same permissions, same credentials. You just need to link each account with its GitHub account.
Linking an account with GitHub¶
Linking is associating a kubelatch account with the numeric id of a GitHub account. kubelatch doesn't use the GitHub login as identity, because GitHub allows renaming and recycling it; it only stores it for display and refreshes it when it changes. A GitHub account can only be linked to one kubelatch account.
There are three paths, all with the same result:
- The invitation link for a new account leads to Vincular con GitHub y entrar (Link with GitHub and sign in).
- The Enlace de vinculación (Linking link) that an admin generates in Usuarios (24 h) links an existing account, or switches it to a different GitHub account. Generating it immediately closes that person's sessions.
- A person who can already sign in uses Mi cuenta (My account) → Vincular con GitHub (Link with GitHub), only if their account isn't linked yet.
Unlinking is an admin's job, with Desvincular GitHub (Unlink GitHub), and it closes that person's sessions. There's no automatic sign-up: a GitHub account that isn't linked to anyone can't sign in, even if it's a member of the organization.
What signing in with GitHub checks¶
sequenceDiagram
participant N as Browser
participant K as kubelatch
participant G as GitHub
N->>K: "Entrar con GitHub" (Sign in with GitHub)
K-->>N: Signed state cookie + redirect
N->>G: Authorizes the GitHub App
G-->>N: Redirect to the callback with code and state
N->>K: /api/auth/github/callback
K->>K: state == cookie
K->>G: Exchanges the code (client secret)
K->>G: GET /user (numeric id)
K->>G: Active organization membership
K->>G: Does the organization require 2FA?
K->>K: Account linked to that id and enabled
K-->>N: Session and redirect to Inicio (Home)
Each step can fail with a code that the login page translates (the list is in Errors). Details that matter:
- Only active members of the configured organization. A pending invitation doesn't count.
- The
statetravels in a cookie signed with a key derived fromKUBELATCH_ENCRYPTION_KEY, valid for 10 minutes and scoped to the callback route. The code is exchanged server-side with the client secret, and the person's GitHub token is discarded after login. - The callback shares the same per-IP rate limit as password login.
- GitHub login deliberately ignores the failed-password lockout. If it respected it, anyone could lock out a person who signs in with GitHub by trying passwords against their login.
What kubelatch asks GitHub for¶
| What for | With which token | Call |
|---|---|---|
| Identity of who's signing in | The person's, only during login | GET /user |
| Organization membership | The person's | GET /user/memberships/orgs/<org>, which must return state: active |
| App installation token | An RS256 JWT signed with the App's private key | GET /orgs/<org>/installation and POST /app/installations/<id>/access_tokens |
| Member list (sync) | The installation's | GET /orgs/<org>/members?per_page=100, following pagination |
| Does the organization require 2FA? | The installation's | The two_factor_requirement_enabled field of GET /orgs/<org>. GitHub only returns it to an App with Administration: read. |
Two-factor: enforced by the organization¶
kubelatch has no two-factor of its own. It relies on whatever the GitHub organization enforces on all its members. If the organization requires it, anyone who signs in with GitHub has passed it.
kubelatch can only verify this if the App has the Organization → Administration: read permission. With that, and with GITHUB_REQUIRE_ORG_2FA set to true, it rejects logins while the organization doesn't require it. Without that permission, GitHub tells it nothing: kubelatch lets people in and warns on every sync. If it can't ask because of a real failure (a 5xx, a timeout), it rejects the login.
In short: 2FA is guaranteed by the organization's configuration, not by kubelatch.
Break-glass accounts¶
With GitHub active, no password opens a session except those of break-glass accounts. They exist for when GitHub isn't available: an outage, the App uninstalled, a configuration error.
- They can only be marked from the CLI (
kubelatch user create --break-glassorkubelatch user set-break-glass), never from the interface or the API. - They have no two-factor. There should be few of them, with long passwords in a secrets manager, used only in emergencies.
- On the login page, their form is under Cuenta de emergencia (contraseña) (Break-glass account (password)).
- A break-glass account that gets linked to GitHub becomes subject to automatic offboarding like any other. Ones that are never linked stay outside the sync.
kubelatch prevents removing the role, disabling, unlinking, or unmarking as break-glass for the last administrator who can sign in. On startup with GitHub active and no admin able to sign in, it logs this as an error.
This doesn't replace native emergency access to each cluster, which doesn't depend on kubelatch (see Accounts and recovery).
Automatic offboarding¶
With the GitHub App configured, whoever leaves the organization gets disabled automatically, through two paths:
- Hourly sync. Each replica tries once an hour (the first, one minute after starting) and they take turns with an advisory lock. It lists the organization's members with an App installation token and disables linked accounts whose id no longer appears. It also refreshes renamed GitHub logins.
member_removedwebhook. WithGITHUB_WEBHOOK_SECRET, GitHub notifies immediately and the offboarding is instant. The HMAC signature is the authentication.
The sync is deliberately conservative. It never disables on an error: a 5xx, a timeout, a failing page, the App being uninstalled, or a list with zero members all abort the pass without touching anyone. And it never re-enables: if someone rejoins the organization, an admin re-enables them by hand.
That's why the sync doesn't fix an unwarranted offboarding, such as one caused by a repeated webhook delivery, which isn't deduplicated (see Security model). What it does fix is a missed webhook: it disables whoever left even if the delivery never arrived.
What happens when an account is disabled¶
Whoever disables it (an admin with Deshabilitar (Disable), the sync, or the webhook), the effects are the same and happen in the same transaction:
- Sessions: all closed instantly. Every API request reloads the account and rejects the cookie.
- Pending links: invitation or linking links are revoked.
- Credentials: all valid ones are revoked. The next request to the proxy gets
401, and openexecorwatchsessions are cut within 10 s at most. - Clusters: the next reconciliation removes the person from the list of users the proxy can impersonate, and their bindings too if no one else holds them.
- Permissions: kept, but they don't count while the account is disabled.
Re-enabling restores the effect of the permissions, but not the credentials: those stay revoked and new ones have to be issued. The credentials' maximum TTL (30 days for people by default) limits what an oversight could leave open.
Sessions¶
The web session is a signed kubelatch_session cookie, 12 h, HttpOnly and SameSite=Lax (Secure with an https base URL). Signing out, changing the password, using a reset or linking link, unlinking GitHub, disabling the account, or an admin's Cerrar sesiones (Close sessions) invalidate all of that person's sessions at once.
The web session isn't usable to talk to clusters. That requires a credential, which is independent of the session.