Add support for instance-level SSH certificates via configuration
Summary
Add support for instance-level SSH certificates in gitlab-sshd by introducing a trusted_user_ca_keys configuration option. This allows self-managed GitLab administrators to configure trusted Certificate Authorities (CAs) directly in config.yml, enabling SSH certificate authentication without requiring Rails/database changes.
This is the gitlab-sshd equivalent of OpenSSH's TrustedUserCAKeys directive, providing a native alternative to the existing OpenSSH-based SSH certificate setup.
Background
GitLab currently supports two SSH certificate flows:
-
OpenSSH-based (instance-level): Configured via
sshd_configwithTrustedUserCAKeysandAuthorizedPrincipalsCommand. Requires manual OpenSSH configuration. - Group-level SSH certificates: Managed via API/database, supported by gitlab-sshd. CAs are registered per top-level group and scoped to that namespace.
For self-managed instances using gitlab-sshd, there's currently no way to configure instance-level SSH certificates without switching to OpenSSH. This feature bridges that gap.
Proposal
Add a new configuration option trusted_user_ca_keys to the sshd section of config.yml:
sshd:
# Trusted user CA public keys for instance-level SSH certificate authentication.
# Certificates signed by these CAs will be trusted for authentication.
# The certificate's KeyId must contain the GitLab username.
trusted_user_ca_keys:
- /etc/gitlab/ssh_user_ca.pub
How it works:
- Admin generates a CA key pair and configures the public key path in
config.yml - Admin signs user certificates with the CA, setting
KeyIdto the GitLab username:ssh-keygen -s /path/to/ca -I <gitlab-username> -V +1d user-key.pub - When a user connects with a certificate signed by the configured CA:
- gitlab-sshd validates the certificate signature and expiry
- The
KeyIdis used as the GitLab username - Standard GitLab access checks proceed (user existence, project permissions via
/allowedendpoint)
Key characteristics:
- No namespace restriction (instance-wide access, same as OpenSSH flow)
- No Rails/database changes required
- No feature flag required - enabled when
trusted_user_ca_keysis configured - Supports multiple CA files for rotation and multi-CA setups
- Falls back to group-level certificate check (via Rails API) if CA is not locally trusted
Implementation Details
Affected files:
-
internal/config/config.go- AddTrustedUserCAKeys []stringtoServerConfig -
internal/sshd/server_config.go- Add CA loading, local trust verification, and instance-level auth path -
internal/sshd/server_config_test.go- Add tests -
config.yml.example- Document new option
Changes to handleUserCertificate:
- Check if certificate's signing CA matches any locally configured trusted CA
- If matched: return permissions with
usernamefromcert.KeyId(no namespace) - If not matched: fall back to existing group-level flow via
/authorized_certsAPI
Acceptance Criteria
-
trusted_user_ca_keysconfiguration option accepts a list of file paths - CA public keys are loaded and parsed at startup (invalid files logged and skipped)
- Certificates signed by configured CAs are trusted for authentication
-
Certificate
KeyIdis used as the GitLab username - No namespace restriction applied (instance-wide access)
- Expired certificates are rejected
- Wrong certificate type (host cert) is rejected
- Certificates signed by unknown CAs fall back to group-level flow
- Multiple CA files supported
-
Configuration documented in
config.yml.example - Unit tests cover all scenarios
Security Considerations
- Trust model: Instance admins already have full control, so instance-level CA trust introduces no new privilege escalation (unlike group-level on GitLab.com)
- No Enterprise User requirement: Unlike group-level certificates, this doesn't need the Enterprise User check since all users are already under admin authority on self-managed
-
User validation: Invalid usernames in
KeyIdwill fail at the/allowedendpoint with appropriate errors
Limitations
- CA management requires filesystem access and service restart (no API/UI)
- No audit events for CA configuration changes
- No certificate revocation beyond CA removal
-
config.ymlmust be synchronized across all gitlab-sshd nodes in clustered deployments
Related Issues/Documentation
- SSH certificates for groups - existing group-level feature
- User lookup via OpenSSH AuthorizedPrincipalsCommand - existing OpenSSH-based approach
- Epic &10662 (closed) - Original group SSH certificates implementation