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> by default, 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.

If your organization signs in with Google Workspace, Microsoft Entra ID, Okta or another OpenID Connect provider instead, see OIDC login (SSO). Both can be on at once: see GitHub and OIDC together.

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).
  • Mandatory 2FA for the whole organization in GitHub: Settings → Authentication security → Require two-factor authentication for everyone in the organization. kubelatch has no two-factor of its own (Two-factor authentication).
  • 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.
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-server 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-server user set-break-glass admin
# or a new account:
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch exec -it deploy/kubelatch -- /kubelatch-server user create rescue --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:

  1. Put the secrets in a Secret:

    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>'
    

    Or add those keys to kubelatch-secrets, which is already loaded whole.

  2. In your values, load the Secret with extraEnvFrom: [{secretRef: {name: kubelatch-github}}] and set config.github.org (plus config.github.requireOrg2FA, config.github.url and config.github.apiURL if you need them). Do both in the same helm upgrade: on its own, GITHUB_ORG turns GitHub login on and kubelatch won't start without the other four.

  3. Run helm upgrade with the same values. A later change to either Secret doesn't redeploy by itself: restart with kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch rollout restart deploy/kubelatch.
  4. Check the log: the listening line shows github_login as true (and github_webhook as true if there's a secret). If no admin can sign in, kubelatch still starts but logs the error … but no admin can log in: …, which names the logins that are on and the command that fixes it.

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: Users → Create an account → Create and invite. The invitation link (72 h) leads to 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: Users → the account's page → Linking link, in its menu (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 My account → Identities → Link with GitHub.

An account that's already linked can't be linked again from My account. An admin unlinks it first with Users → the account's page → Unlink GitHub, in its menu. 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 "That GitHub account isn't linked to any kubelatch account. Ask an administrator for a linking link.": 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 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 Users. It also never re-enables: if someone rejoins the organization, an admin re-enables them by hand.

If the App is uninstalled or suspended, the webhook logs it and 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 Audit → 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).

GitHub and OIDC together

GitHub login and OIDC login can be on at the same time. The login page then offers both buttons, the OIDC provider's first, and each person can have a GitHub identity, an OIDC identity or both: either one opens the same kubelatch account, with the same login and permissions.

  • Invitation and linking links offer both ways to link, and My account shows both in its Identities card.
  • The membership sync and the member_removed webhook keep working as above: whoever leaves the organization is disabled, even if they also sign in through the OIDC provider.
  • The OIDC provider gives kubelatch no member list. An account with only an OIDC identity is never disabled on its own: disable it in Users.
  • Passwords stay reserved to break-glass accounts.

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. Notices, the bell in the top bar, says "kubelatch can't check whether the GitHub organization requires 2FA: look at the last sync in Users."

When kubelatch does know the organization doesn't enforce 2FA, Notices warns: "The GitHub organization does not require 2FA: (...)". Both notices lead to Users, and Installation health in Edition shows the same check (Editions and license).

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 with "password": false, "github": true and "org": "<org>" (the whole answer, with oidc, oidc_name and access_contact, is in HTTP API), and the login screen shows Sign in with GitHub.
  • As a break-glass admin, generate a Linking link from your account's page in Users (or create a user). Open it in a private window, click Link with GitHub and sign in and authorize. You end up in Home, signed in.
  • In Users, the GitHub column shows login (id). In Audit → Control plane there's a link.complete and a login.success with via: github.
  • Sign out and sign in with Sign in with GitHub: you get a session directly. An account that isn't a member (or has a pending invitation) sees "Your GitHub account is not an active member of the organization."; an unlinked one, "That GitHub account isn't linked to any kubelatch account. Ask an administrator for a linking link."
  • 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 Sign in with a password.
  • One minute after startup, the log shows membership sync: done with members and Users 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 Users 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).

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