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.comextension 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
- Export the
AlmaForgeuser CA (Step 1). - Register the CA in your GitHub organization's settings (Step 2).
- Add a
certExtensionsentry with thelogin@github.comextension to a role and assign the role to the engineers who need GitHub access (Step 3). - Issue certificates through the normal
alma loginflow and reach GitHub with the engineer's organization user (Step 4).
Prerequisites
Before you start, make sure you have:
- A running
AlmaForgecluster with an Auth Service that you can reach over the network and a workingalmaCLI 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 securitypermission 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
AlmaForgecertificate 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
AlmaForgeside to create and editroleresources.
Step 1. Export the AlmaForge user CA
Run the following on any host that has alma
configured against the target AlmaForge cluster:
alma auth export --type=usercert-authority ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1... clustername=almaforge.example.com&type=userA few details about what this command does:
--type=userselects the SSH user CA, which is the CA that signs end-user certificates. Do not use--type=hosthere: 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_keysformat, prefixed withcert-authorityand 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
almadholds the private half.
GitHub wants the public key on its own, without the
cert-authority prefix. Strip it inline:
alma auth export --type=user | sed 's/^cert-authority //'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1... clustername=almaforge.example.com&type=userCopy the whole output line, prefix and comment and all
except the stripped cert-authority keyword. GitHub's
"New CA" form accepts it as-is.
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
- Open your organization on github.com.
- Go to Settings > Authentication security > SSH Certificate Authorities.
- Click New CA.
- Paste the output from Step 1 into the Key field.
- 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.
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:
# 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.comsuffix 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 usesexternal.github_username, which is a convention for a claim populated by your SSO connector. Pick whatever trait your SSO already carries.
Apply the role:
alma apply -f github-ssh-ca-role.yamlAssign 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.
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:
alma login --proxy=almaforge.example.comalma 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:
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-ptyIf 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.
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:
git clone org-12345@github.com:acme/backend.gitgit remote set-url origin org-12345@github.com:acme/backend.gitOr add a one-time entry to ~/.ssh/config so the right user
is used transparently for every repository under that host:
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:
-
The CA is still registered. Revisit Settings > Authentication security > SSH Certificate Authorities and confirm the CA from Step 1 is still listed. Rotating the
AlmaForgeuser CA on theAlmaForgeside without re-exporting to GitHub breaks every certificate that was signed by the new key. -
The certificate carries the extension. Run
alma statusand look forlogin@github.comunder Extensions. If missing, the role in Step 3 is not applied to this user. -
The extension value is a real GitHub user. Inspect the public certificate file in your local profile:
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.
-
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.
AlmaForgecertificates are short-lived by default. The engineer's access to GitHub stops authorizing new connections at theValid untiltime shown byalma 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
AlmaForgeuser 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
- GitHub documentation: About SSH certificate authorities:
the canonical reference for the GitHub side of this
feature: what editions support it, how the
org-<ID>SSH user works, and the exact format of thelogin@github.comextension. - GitHub documentation: Managing your organization's SSH certificate authorities: the UI walkthrough GitHub maintains for registering, rotating, and removing CAs on their side.
- Role reference:
certExtensions: theAlmaForgeresource field that carries the extension template used in Step 3. - CA rotation: cluster-side procedure for rotating the user CA, required for emergency revocation of already-issued certificates.
- GitHub SSO for AlmaForge: the
complementary
AlmaForge→ GitHub authentication integration.