Skip to main content

GitHub SSH Certificate Authority

GitHub lets an organization register one or more SSH Certificate Authorities. Once a CA is registered, a valid SSH certificate signed by that CA can authenticate the GitHub user named in its extension. GitHub still checks that user's repository permissions, with no per-user public key upload on github.com. This page wires AlmaForge in as the CA: an engineer runs alma login, almad signs a short-lived SSH certificate, and ssh-agent (or git directly) presents it on every connection to github.com.

Three user-visible changes compared to the default "paste a public key into your GitHub profile" flow:

  • No long-lived key on github.com. Engineers never upload a personal SSH key. Their certificate stops authorizing new connections when it expires.
  • SSO identity maps to a GitHub account. The role's login@github.com extension names the GitHub user. Map it from a trusted IdP claim so the certificate identifies the intended account.
  • Issuance and GitHub trust are separate. An AlmaForge lock stops new certificate issuance. GitHub can still accept an existing certificate until expiry or removal of its issuing CA from GitHub.

The integration is a GitHub feature on top of a standard AlmaForge role option. There is no AlmaForge-side plugin, no daemon, no GitHub API token to store, and no webhook.

TL;DR​

  1. Export the AlmaForge user CA (Step 1).
  2. Register the CA in your GitHub organization's settings (Step 2).
  3. Add a certExtensions entry with the login@github.com extension to a role and assign the role to the engineers who need GitHub access (Step 3).
  4. Issue certificates through the normal alma login flow and reach GitHub with the engineer's organization user (Step 4).

Prerequisites​

Before you start, make sure you have:

  • A running AlmaForge cluster with an Auth Service that you can reach over the network and a working alma CLI pointed at it.
  • Admin access to a GitHub organization. SSH Certificate Authorities are an organization-level feature. You need to be an organization owner, or have explicit Settings > Authentication security permission on the organization, to register a CA. Personal GitHub accounts cannot use this feature.
  • A supported GitHub edition. SSH Certificate Authorities are available on GitHub Enterprise Cloud and GitHub Enterprise Server. They are not available on free organizations on github.com, and they are not available for personal accounts.
  • At least one engineer user in the GitHub organization. Each engineer who will sign in with a AlmaForge certificate must already be a member of the organization under their normal GitHub username. This integration changes the credential the engineer presents. It does not create or sync users.
  • Permission on the AlmaForge side to create and edit role resources.

Step 1. Export the AlmaForge user CA​

Run the following on any host that has alma configured against the target AlmaForge cluster:

Terminal
alma auth export --type=usercert-authority ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1... clustername=almaforge.example.com&type=user

A few details about what this command does:

  • --type=user selects the SSH user CA, which is the CA that signs end-user certificates. Do not use --type=host here: that's the CA used to sign node certificates for in-cluster SSH, which is a different key and has nothing to do with GitHub.
  • The output is a single line in the OpenSSH authorized_keys format, prefixed with cert-authority and suffixed with a comment that identifies the cluster.
  • The key is non-secret. It is the public half of the CA signing keypair. You can commit it to a repository or put it in a shared drive, but you cannot use it to sign certificates. Only almad holds the private half.

GitHub wants the public key on its own, without the cert-authority prefix. Strip it inline:

Terminal
alma auth export --type=user | sed 's/^cert-authority //'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1... clustername=almaforge.example.com&type=user

Copy the whole output line, prefix and comment and all except the stripped cert-authority keyword. GitHub's "New CA" form accepts it as-is.

Algorithm

The key your cluster returns may be ssh-rsa, ssh-ed25519, or an ECDSA variant depending on when the cluster was first initialized. GitHub accepts all three. If you have not rotated the user CA since the cluster first started, you are looking at whatever it chose at first boot.

Step 2. Register the CA with GitHub​

  1. Open your organization on github.com.
  2. Go to Settings > Authentication security > SSH Certificate Authorities.
  3. Click New CA.
  4. Paste the output from Step 1 into the Key field.
  5. Click Add CA.

GitHub displays the CA's fingerprint and the date it was added. Keep that page open for a minute. The next step will not work until the CA is registered.

Test certificate access before requiring it

Below the CA list, GitHub shows a Require SSH certificates toggle. Turning it on forces every engineer in the organization to authenticate with a certificate signed by one of the registered CAs. Personal SSH keys stop working immediately. Do not turn it on the first time you add a CA. Roll out the CA to engineers first, confirm that Step 4 works for every user, and only then flip the toggle. Turning it on before any engineer holds a valid certificate locks your entire organization out of SSH access to its own repositories.

Step 3. Add the GitHub extension to a role​

When AlmaForge issues an SSH user certificate, by default it embeds AlmaForge-specific metadata (the user's roles, their principals, the cluster name, and so on). GitHub expects one additional custom extension, login@github.com, whose value is the GitHub organization member username the certificate should authenticate as. Add that extension to a role via the certExtensions option.

Edit an existing role or create a new one:

github-ssh-ca-role.yaml:

examples/integrations/github-ssh-ca-role.yaml
# Role that places the engineer's GitHub username in SSH certificates.
apiVersion: almaforge.com/v1
kind: Role
metadata:
name: github-dev
spec:
options:
certExtensions:
- name: login@github.com
value: '{{ external.github_username }}'

Every entry is written to the SSH certificate's extensions map, where GitHub looks for it. The option applies only to SSH certificates, so it does not need a certificate type or mode.

Field-by-field:

  • name: login@github.com: must be exactly this string, including the @github.com suffix and including the lowercase. GitHub looks up this specific extension name and ignores anything else.
  • value: the engineer's GitHub organization username. It can be a literal string (one role per user is possible but not recommended) or, much more commonly, a template that pulls the username from an SSO trait. The example above uses external.github_username, which is a convention for a claim populated by your SSO connector. Pick whatever trait your SSO already carries.

Apply the role:

Terminal
alma apply -f github-ssh-ca-role.yaml

Assign it to engineers through the SSO connector's claimsToRoles mapping. Direct edits to temporary SSO user records are replaced on the next login. See the role reference for the full syntax.

Trait mapping is where most setups go wrong

The SSH certificate is only as correct as the trait it pulls from. If your SSO connector does not populate a trait with the engineer's GitHub organization username, the rendered extension value will be empty, GitHub will refuse the certificate with "user not found", and the engineer will assume the CA itself is broken. Before rolling this out to the team, inspect the test user's stored traits with alma get user/<username> -o yaml as an admin. Confirm the claim contains the intended GitHub username.

Step 4. Use the certificate with git​

From the engineer's workstation, log in as usual:

Terminal
alma login --proxy=almaforge.example.com

alma login drops a fresh SSH certificate into the engineer's AlmaForge profile directory and loads it into the local ssh-agent (or the SSH identity file, depending on your client configuration). No extra flags are needed for GitHub access.

Confirm the certificate has the GitHub extension:

Terminal
alma status> Profile URL:        https://almaforge.example.com:443  Logged in as:       alice  Cluster:            almaforge.example.com  Roles:              github-dev  Logins:             alice  Valid until:        2026-04-11 22:00:00  Extensions:         login@github.com, permit-agent-forwarding, permit-port-forwarding, permit-pty

If login@github.com is missing from the Extensions list, the role was either not applied or not assigned to the user. Go back to Step 3.

Test the GitHub connection using the organization-specific SSH URL shown by GitHub. Instead of connecting as git@github.com, you connect as org-<ID>@github.com, where <ID> is the numeric ID of your GitHub organization. The GitHub UI shows this value on the same SSH Certificate Authorities page where you registered the CA.

Terminal
ssh -T org-12345@github.comHi alice! You've successfully authenticated over SSH using a certificate signed by your organization's certificate authority.

Point git at GitHub using the same user. Either edit the remote URLs directly:

Terminal
git clone org-12345@github.com:acme/backend.gitgit remote set-url origin org-12345@github.com:acme/backend.git

Or add a one-time entry to ~/.ssh/config so the right user is used transparently for every repository under that host:

~/.ssh/config
Host github.com
HostName github.com
User org-12345

From here, git push, git pull, and git clone work as normal for as long as the AlmaForge certificate is valid. When it expires, re-run alma login.

Troubleshooting​

GitHub says Permission denied (publickey)​

Check, in order:

  1. The CA is still registered. Revisit Settings > Authentication security > SSH Certificate Authorities and confirm the CA from Step 1 is still listed. Rotating the AlmaForge user CA on the AlmaForge side without re-exporting to GitHub breaks every certificate that was signed by the new key.

  2. The certificate carries the extension. Run alma status and look for login@github.com under Extensions. If missing, the role in Step 3 is not applied to this user.

  3. The extension value is a real GitHub user. Inspect the public certificate file in your local profile:

    Terminal
    ssh-keygen -L -f <path-to-user-cert.pub>

    The value must match the engineer's GitHub username in the organization exactly, case-sensitive. An empty string means the trait is missing. An unknown username means the GitHub account is not a member of the organization.

  4. Use the organization-specific SSH URL. The org-<ID> form helps select certificate authentication when the agent holds several keys. Copy it from the repository's Code > SSH menu.

GitHub says "user not found in organization"​

The extension value in Step 3 does not match any member of the GitHub organization. Either the engineer is not a member under that username, or the template in the role is pulling the wrong trait. Confirm the engineer is listed under People in the organization and that the name there matches, case-sensitive, what alma status reports for the login@github.com extension.

alma auth export returns nothing​

auth export is an admin command. The user running it needs a role with allow.rules that permits reading the CertificateAuthority resource, typically the admin or equivalent preset role. Without that permission the command exits with an access denied error, not an empty response. If you are seeing an empty response, the cluster is reachable but no CA is configured for the given --type yet. That should not happen on a cluster that has served a single login, so investigate the auth server logs before continuing.

Operations​

Break-glass access during the initial rollout​

Do not enable GitHub's Require SSH certificates toggle until every engineer who needs SSH access has completed Step 4 at least once. Until the toggle is on, personal SSH keys on github.com keep working side-by-side with certificates, and that overlap is your rollback path if Step 3 turns out to populate the extension value incorrectly for some subset of users. Once every engineer is authenticated through the CA, flip the toggle. Flipping it back off later is possible but requires the same organization owner permission and takes effect immediately, which is useful if you ever need to force a fall-back to personal keys.

Revocation​

Three levers, in order of how broad they are:

  • Expire the certificate. AlmaForge certificates are short-lived by default. The engineer's access to GitHub stops authorizing new connections at the Valid until time shown by alma status. Reducing a role's TTL affects future certificates, not one already issued. There is no way to revoke a single already-issued SSH certificate while GitHub still trusts its CA. GitHub does not support SSH certificate revocation lists for this path today.
  • Lock the AlmaForge user and remove their SSO assignment. This prevents fresh certificates. Deleting a temporary user record alone does not prevent its recreation on the next SSO login. Existing certificates remain usable at GitHub until expiry. For earlier cutoff, remove access in GitHub or remove the trusted CA.
  • Replace the trusted CA at GitHub. Cluster-side CA rotation does not remove GitHub's trust in the old key. Register the new CA and remove the old one from GitHub when ending that trust. Removing it affects every certificate it signed, including unexpired ones. See the CA rotation guide for the full procedure and its implications on the rest of your cluster (the user CA also signs SSH certificates for every non-GitHub SSH login).

Which cluster signs for which GitHub org​

A GitHub organization can register more than one CA, and a AlmaForge cluster signs for exactly one CA per type. Two reasonable shapes:

  • One cluster, many GitHub orgs. Register the CA at the GitHub enterprise level to cover its organizations. GitHub does not allow uploading the same CA to multiple organization or enterprise accounts. See GitHub CA management.
  • One GitHub org, many clusters. Register one CA per cluster. Engineers pick a cluster first, log in there, and the certificate that cluster hands out authenticates them to the shared GitHub organization under whatever username that cluster's trait mapping provides. This is unusual outside of multi-tenant setups.

The login@github.com extension is not a secret​

The extension only tells GitHub which organization member to bind the session to. It does not grant any new capability. An attacker who forged an SSH certificate with an arbitrary login@github.com value still could not use it, because the GitHub side verifies the certificate's signature against the registered CA before it ever reads the extension. The signing key (the private half of the CA) is what matters, and it stays inside almad. Protect the AlmaForge auth server the same way you already protect every other piece of cluster state that depends on it.

Interaction with GitHub SSO on the same organization​

You can run this integration at the same time as the GitHub SSO integration on the same organization. They solve different problems: github-ssh-ca authenticates engineers to git over SSH, github authenticates engineers to AlmaForge itself through an OAuth app. Nothing stops you from using one, the other, or both, and they share no state.

References​