Configuring target servers
A target server is any host that GitHub Actions (or another OIDC caller) logs into with a certificate issued by the CA. Configuring it means two things, both on the SSH server side only:
Trust the CA — tell sshd that user certificates signed by the CA public key are valid (
TrustedUserCAKeys).Authorize principals per user — decide, for each login account, which certificate principals may use it (
AuthorizedPrincipalsFile).
Nothing about oidc-ssh-ca itself runs here — the server only needs its
own sshd and a copy of the CA public key. This page does it by hand on
a single host; to apply the same configuration to a fleet, the bundled
Ansible role automates every step below — see
Configuring target servers with Ansible.
How certificate login works
A normal key-based login checks the client’s public key against
authorized_keys. A certificate login is different: sshd checks that the
certificate is signed by a trusted CA and that the certificate carries
a principal that the target account allows. There is no per-user public
key to distribute, rotate, or clean up — only the CA public key, the same
on every host.
So a login as user deploy succeeds when both hold:
the certificate is signed by a CA listed in
TrustedUserCAKeys, andone of the certificate’s principals appears in
deploy’sAuthorizedPrincipalsFile.
The principals come from certificate.principals in the CA’s
policy; the per-user file on the server is the other half of
the decision. The login account itself must already exist — none of this
creates Unix users.
1. Install the CA public key
Export the CA public key (never the private key) and copy it to each server:
oidc-ssh-ca print-ca-pub --ca-key-file ./ca_key > oidc-ssh-ca.pub
Install it at /etc/ssh/oidc-ssh-ca.pub. It is not secret, but it must not
be writable by anyone but root — sshd trusts whatever it contains.
sudo install -o root -g root -m 0644 oidc-ssh-ca.pub /etc/ssh/oidc-ssh-ca.pub
3. Configure sshd
Drop a fragment into /etc/ssh/sshd_config.d/ (a full example is in
examples/sshd/):
# /etc/ssh/sshd_config.d/oidc-ssh-ca.conf
TrustedUserCAKeys /etc/ssh/oidc-ssh-ca.pub
Match User deploy
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
PasswordAuthentication no
KbdInteractiveAuthentication no
TrustedUserCAKeys is global — it makes the CA trusted for the whole host.
The per-user Match User block points at the principals file from step 2
(%u expands to the login user) and, for those accounts, turns off
password and keyboard-interactive logins so the certificate is the only way
in. That hardening affects only the listed users; other accounts are left
as they were — disable password auth globally yourself if that is your
policy.
This fragment only takes effect if sshd_config includes the drop-in
directory, which is the default on current Debian, Ubuntu, RHEL, and
Fedora:
Include /etc/ssh/sshd_config.d/*.conf
If your sshd_config lacks that line, either add it or place the
configuration directly in sshd_config.
4. Validate and reload
Always check the configuration before reloading — a syntax error that reaches a restart can lock you out:
sudo sshd -t # exits non-zero and prints the offending line on error
sudo systemctl reload ssh # `sshd` on RHEL/Fedora; reload, not restart
reload re-reads the configuration without dropping existing connections,
so an open session stays available as a fallback while you confirm a fresh
login works.
5. Test the login
From a machine that has a freshly issued certificate (or reproduce one with the quickstart flow), confirm the certificate logs in and that the key ID shows up in the server’s sshd log — it embeds the repository and run ID, tying the login back to the exact CI run:
ssh -i gha_key -o CertificateFile=gha_key-cert.pub -o IdentitiesOnly=yes \
deploy@example.com 'whoami'
sudo journalctl -u ssh | grep 'Accepted publickey'
# ... Accepted publickey for deploy ...: ED25519-CERT ID gha:your-org/your-repo:9000000000:1 (serial 0) CA ...
Always pin host keys on the client side (known_hosts, or a host
certificate) — short-lived user certificates do not help if the connection
itself is not verified.
CA key rotation
TrustedUserCAKeys may list more than one key (or the file may hold
several lines), so rotation is zero-downtime: trust both the old and new CA
public keys during the transition, then drop the old one once its
certificates have expired. The full procedure is in
Operations → CA key rotation.
Doing this across many servers
Repeating these steps by hand does not scale and is easy to get subtly
wrong (a missing sshd -t, an inconsistent principals file). The
oidc_ssh_ca_trust Ansible role performs exactly steps 1–4
above — idempotently, validating with sshd -t both before and after, and
reloading only when something changed.