Skip to content

GitHub login

With a GitHub App belonging to your organization, people sign in to kubelatch with their GitHub account, and their accounts are disabled automatically when they leave the organization. This guide creates the App, installs it and connects it to kubelatch.

What changes when you turn it on:

  • Only active members of the organization can sign in. A pending invitation doesn't count.
  • Each kubelatch account gets linked to a GitHub account's numeric id. Its kubelatch login, the one clusters see as user:<login>, doesn't change.
  • Passwords stop working, except for break-glass accounts, which are marked from the CLI.
  • Two-factor authentication is enforced by the GitHub organization, not by kubelatch.

The reasoning behind this model is in Identity and Security model.

Before you start

  • Owner permissions on the GitHub organization. Everything on the GitHub side is done once.
  • kubelatch installed and an administrator who signs in with a password (Install).
  • Turn on mandatory 2FA for the whole organization in GitHub: Settings → Authentication security → Require two-factor authentication for everyone in the organization.
  • The commands use $MGMT_KUBECONFIG, the path to the management cluster's kubeconfig (see Install).

1. Create the GitHub App

In the organization: Settings → Developer settings → GitHub Apps → New GitHub App (https://github.com/organizations/<org>/settings/apps/new).

Field Value
GitHub App name kubelatch or whatever you prefer. People will see it when authorizing.
Homepage URL Your KUBELATCH_BASE_URL, for example https://kubelatch.example.com.
Callback URL <KUBELATCH_BASE_URL>/api/auth/github/callback.
Expire user authorization tokens Doesn't matter: kubelatch uses the person's token only during login and discards it.
Request user authorization (OAuth) during installation Unchecked.
Setup URL Empty.
Webhook → Active Checked if you want immediate offboarding (recommended).
Webhook URL <KUBELATCH_BASE_URL>/v1/github/webhook.
Webhook secret A random string (head -c32 /dev/urandom \| base64). This becomes GITHUB_WEBHOOK_SECRET.
Permissions → Organization → Members Read-only. Required: each person's membership and the member list.
Permissions → Organization → Administration Read-only. Lets kubelatch check that the organization enforces 2FA. Without it, it can't verify this and only warns.
Subscribe to events Organization (member added/removed) and Installation.
Where can this GitHub App be installed? Only on this account.

No repository or user-account permission is needed. GitHub Apps don't use scopes: when authorizing, the person sees exactly these permissions.

In the webhook configuration, choose Content type: application/json. With any other type, kubelatch responds 415.

After creating the App, on its page:

  1. Note the App ID (GITHUB_APP_ID) and the Client ID (GITHUB_APP_CLIENT_ID).
  2. Click Generate a new client secret: this is GITHUB_APP_CLIENT_SECRET and is only shown once.
  3. Under Private keys → Generate a private key, download the .pem: this is GITHUB_APP_PRIVATE_KEY, the whole file with its headers.

2. Install it in the organization

On the App's page: Install App → your organization → Install. It doesn't matter whether you choose "All repositories" or "Only select repositories" with none selected: the permissions it uses are organization-level.

Without installation, kubelatch can't check the membership of whoever signs in or read the member list. Sync fails with app not installed in the organization and disables no one.

3. Prepare a break-glass account

Do this before restarting with the GITHUB_* variables

From that moment on, no password works except break-glass accounts'. Without one, no one will be able to sign in until you mark one from the CLI (kubelatch user set-break-glass).

Mark your current administrator, or create an account just for emergencies:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch exec -it deploy/kubelatch -- /kubelatch user set-break-glass admin
# or a new account:
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch exec -it deploy/kubelatch -- /kubelatch user create rescate --admin --break-glass

The CLI works with or without the GITHUB_* variables and never talks to GitHub. Break-glass accounts have no two-factor: keep them few, with long passwords in the secret manager, and only for when GitHub is down. More in Accounts and recovery.

4. Configure kubelatch

Variable Value
GITHUB_APP_ID The App ID (a number).
GITHUB_APP_CLIENT_ID The Client ID (Iv1.… or Iv23…).
GITHUB_APP_CLIENT_SECRET The client secret.
GITHUB_APP_PRIVATE_KEY The key's PEM (PKCS#1 RSA PRIVATE KEY, as GitHub downloads it, or PKCS#8). Accepted on one line with literal \ns.
GITHUB_ORG The organization's login (acme-corp).
GITHUB_WEBHOOK_SECRET Optional. Turns on /v1/github/webhook.
GITHUB_REQUIRE_ORG_2FA true by default: rejects GitHub logins if kubelatch knows the organization doesn't enforce 2FA.
GITHUB_URL, GITHUB_API_URL Only for GitHub Enterprise Server: https://<host> and https://<host>/api/v3.

The first five go together. If any of them is set (or GITHUB_WEBHOOK_SECRET), kubelatch requires all five and won't start if one is missing. That way a half-finished deployment doesn't leave passwords active by accident. All of them are in Configuration.

On Kubernetes, put the secrets in a Secret and leave GITHUB_ORG, GITHUB_REQUIRE_ORG_2FA and the URLs in config.env:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch create secret generic kubelatch-github \
  --from-literal=GITHUB_APP_ID=<app-id> \
  --from-literal=GITHUB_APP_CLIENT_ID=<client-id> \
  --from-literal=GITHUB_APP_CLIENT_SECRET='<client-secret>' \
  --from-file=GITHUB_APP_PRIVATE_KEY=<file>.private-key.pem \
  --from-literal=GITHUB_WEBHOOK_SECRET='<webhook-secret>'

Add that Secret to the Deployment's envFrom, or put the keys into kubelatch-secrets, which is already loaded whole. In the second case, restart with kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch rollout restart deploy/kubelatch: changing a Secret doesn't redeploy by itself. On startup, the log shows github_login=true (and github_webhook=true if there's a secret). If no admin can sign in, the log says so with a startup error.

kubelatch accounts don't change: same login, permissions and credentials. You only need to link each one to its GitHub account. There are three ways:

  • New accounts: Usuarios (Users) → Crear e invitar (Create and invite). The invitation link (72 h) leads to Vincular con GitHub y entrar (Link with GitHub and sign in). The person authorizes the App, kubelatch checks that they're an active member, and the account ends up linked and signed in.
  • Existing accounts: Usuarios → Enlace de vinculación (Linking link; 24 h). Also used to change someone's GitHub account, and it closes their sessions.
  • From the account itself: whoever can already sign in (for example, a break-glass account) goes to Mi cuenta (My account) → Vincular con GitHub (Link with GitHub).

An account that's already linked can't be linked again from Mi cuenta. An admin unlinks it first with Usuarios → Desvincular GitHub (Unlink GitHub). If linking returns github_taken, that GitHub account is already linked to someone else: unlink it first.

There's no automatic sign-up. An unlinked GitHub account is rejected with no está vinculada a ninguna cuenta de kubelatch (not linked to any kubelatch account): an admin creates the account and invites them.

On every GitHub login, kubelatch checks that the person is an active member of the organization. The link stores the numeric id, which GitHub never recycles; the GitHub login is informational and is refreshed if the person renames themselves. The flow's details are in Identity.

How offboarding works

It only affects accounts linked to GitHub. Unlinked break-glass accounts are left out, on purpose. A linked break-glass account loses access just like anyone else if the person leaves the organization.

  • Hourly sync. The first pass runs one minute after startup. It lists the organization's members and disables anyone no longer there, with the same effects as Deshabilitar (Disable): sessions closed, credentials revoked and removed from the proxy's RBAC on the next reconciliation. It also refreshes renamed GitHub logins.
  • member_removed webhook. Offboarding is immediate. It's signed with HMAC-SHA256 in X-Hub-Signature-256. Without GITHUB_WEBHOOK_SECRET the endpoint responds 404.

Sync never disables on an error: a 5xx, a timeout, a page that fails, the App being uninstalled, or a response with zero members abort the whole pass. The error shows up in the log and in Usuarios. It also never re-enables: if someone rejoins the organization, an admin re-enables them by hand.

Sync uses an App installation token. It only disables an account if it's still linked to the same GitHub id it observed, and it doesn't evaluate accounts linked during the pass itself. The webhook only logs installation deleted or suspend events: from that moment every GitHub login fails. Any other event is accepted and ignored.

The last administrator who can sign in is never disabled through these paths. An error is left in the log (the webhook still responds 200, so GitHub doesn't retry): review it.

In Auditoría (Audit) → Plano de control (Control plane), each offboarding is a user.disable with no actor, with via: github-sync or github-webhook.

The webhook doesn't deduplicate deliveries: a repeated one can disable again someone you re-enabled (see Security model).

Two-factor authentication

kubelatch has no two-factor of its own: it relies on the organization's mandatory 2FA. It can only check it if the App has Organization → Administration: read:

  • With the permission and GITHUB_REQUIRE_ORG_2FA=true: rejects GitHub logins while the organization doesn't enforce 2FA (no_2fa at login) and logs it as an error on every sync.
  • Without the permission: it can't tell "doesn't enforce 2FA" from "don't know". It lets people in and warns on every pass. Usuarios shows no se puede comprobar el 2FA de la organización (can't check the organization's 2FA).

kubelatch caches the response for up to 10 minutes.

Check that it works

With the App created and installed and kubelatch restarted with the variables:

  • GET /api/auth/methods responds {"github":true,"org":"<org>"} and the login screen shows Entrar con GitHub (Sign in with GitHub).
  • As a break-glass admin, generate an Enlace de vinculación for your account in Usuarios (or create a user). Open it in a private window, click Vincular con GitHub y entrar and authorize. You end up in Inicio (Home), signed in.
  • In Usuarios, the GitHub column shows login (id). In Auditoría → Plano de control there's a link.complete and a login.success with via: github.
  • Sign out and sign in with Entrar con GitHub: you get a session directly. An account that isn't a member (or has a pending invitation) sees no es miembro activo de la organización (not an active member of the organization); an unlinked one, no está vinculada a ninguna cuenta de kubelatch (not linked to any kubelatch account).
  • The password of a linked, non-break-glass account no longer works (a generic 401 and login.failure with reason: password_disabled). The break-glass one still does, from Cuenta de emergencia (contraseña) (Break-glass account (password)).
  • One minute after startup, the log shows membership sync: done with members=… and Usuarios summarizes the last pass.
  • Remove a test account from the organization: with the webhook it's disabled instantly; without it, on the next pass. Re-enabling it in Usuarios and adding it back to the organization lets it sign in again.
  • GET /api/github/status (admin) shows configured, org, webhook, require_org_2fa and last_sync with two_factor_required (null if the App can't read it).

Testing it locally

With make dev (http://localhost:8080), a test App whose Callback URL is http://localhost:8080/api/auth/github/callback works: GitHub accepts http on localhost. The webhook needs a public URL or a tunnel, but that's not necessary to test login.

If something fails (redirect_uri, not_member, no_2fa, webhook with 401, 413 or 415), see Troubleshooting.