sshs3 Docs
v0.9 sshs3.com ↗ GitHub ↗

SSH Certificate Authority (SSH CA)

Security & Teams Target Platforms: Linux, Windows, Step-CA, Vault / OpenBao, OIDC Last Updated: 2026-10-25 10:40
Short-lived SSH user certificates through OIDC with Step-CA or HashiCorp Vault / OpenBao: setup, ephemeral or smartcard keys, automatic renewal and troubleshooting.

Sign in once with your company identity provider and sshs3 hands you a short-lived OpenSSH user certificate. There are no long-lived keys to distribute, rotate or revoke on your servers: they trust the CA, and the certificate expires on its own. sshs3 works with Smallstep Step-CA and HashiCorp Vault / OpenBao, and the private key is either generated in memory for each login or stays on your smartcard.

SSH Certificate Authority settings
SSH Certificate Authority settings

1. How It Works

You authenticate with an OpenID Connect (OIDC) identity provider such as Keycloak, Okta, Google Workspace or Microsoft Entra ID. The browser sign-in uses the authorization code flow with PKCE and returns to a loopback address that only sshs3 listens on.

📊 Architecture Diagram
sequenceDiagram
    participant U as You
    participant S as sshs3
    participant I as Identity provider
    participant C as CA (Step-CA or Vault / OpenBao)
    participant H as SSH server
    U->>S: Log In with SSH CA
    S->>I: Open browser, authorization code + PKCE
    I-->>S: ID token (via loopback redirect)
    S->>C: Public key + ID token
    C-->>S: Signed SSH user certificate
    S->>S: Load certificate into the app agent
    U->>H: Connect (certificate presented by ssh)
    H-->>U: Trusted CA, principal allowed: logged in
StepWhat happensWhere it runs
1Sign-in in your default browserIdentity provider
2The ID token is exchanged for a certificateCA
3The certificate is used by terminals, SFTP and tunnels that opt insshs3 and the system ssh
4Renewed before it expires, or you sign in againsshs3
Note

The CA decides the lifetime and the principals. The certificate lifetime comes from the CA (the Vault / OpenBao role ttl, or the Step-CA provisioner's SSH user duration). Default Principals is a request; the CA grants what its policy allows.


2. Quick Start

  1. Prepare your identity provider and the CA (section 3 and 4). This is done once by an administrator.
  2. Open Settings → Security & Smartcard, scroll to SSH Certificate Authorities (SSH CA) and click Add CA.
  3. Fill in the CA and the identity provider (section 5), click Save Configuration, and save the settings dialog.
  4. Click Authenticate with CA on the new entry. Your browser opens; sign in. The entry shows Active (your name).
  5. In a connection profile, set Authentication to SSH Agent and tick Use active SSH Certificate Authority session.
  6. Open the connection. The Shield icon in the top toolbar is green while the certificate is valid.

3. Prepare the Identity Provider

Create an OIDC client for sshs3 with these properties:

SettingValue
Client typePublic client, authorization code flow with PKCE (S256). sshs3 never sends a client secret.
Redirect URIhttp://127.0.0.1:<port>/callback. The port is chosen at random for every login, so the provider has to accept any port on the loopback address (RFC 8252). The IPv6 form http://[::1]:<port>/callback is used only if IPv4 loopback is unavailable.
Scopesopenid profile email
Client IDThe value you enter as OIDC Client ID in sshs3. It must match what the CA expects.
IssuerThe base URL that serves /.well-known/openid-configuration. This is the OIDC Issuer URL.
Important

A confidential client does not work. If your provider requires a client secret for the token exchange, the sign-in fails with OIDC token exchange failed. Create a separate public client for sshs3.


4. Prepare the CA and the Servers

4.1 Step-CA

Add an OIDC provisioner whose client ID is the one you gave sshs3, and make sure SSH certificates are enabled on the CA:

SHELL
step ca provisioner add keycloak --type oidc \
  --client-id sshs3 \
  --configuration-endpoint https://keycloak.example.com/realms/sshca/.well-known/openid-configuration \
  --domain example.com

Your step version may also ask for --client-secret; sshs3 never sends one. sshs3 sends the ID token to POST /1.0/ssh/sign as the one-time token, with the certificate type user and any Default Principals you configured. The provisioner's own listenAddress and client secret are only used by Smallstep's CLI; sshs3 runs its own sign-in. Without requested principals, Step-CA derives them from the email claim of the token (for carl@example.com: carl and carl@example.com). Templates can add more, for example from a groups claim.

4.2 Vault / OpenBao

sshs3 logs in to the JWT auth method with the ID token, signs the public key with the SSH secrets engine, and then revokes its own short-lived token. The commands below use bao; with HashiCorp Vault use vault.

SHELL
# 1. JWT auth method: trust the identity provider, one role for sshs3 users
bao auth enable jwt
bao write auth/jwt/config oidc_discovery_url="https://keycloak.example.com/realms/sshca"
bao write auth/jwt/role/sshs3 role_type=jwt user_claim=preferred_username \
    bound_audiences=sshs3 token_policies=sshs3-sign

# 2. SSH secrets engine as a user CA
bao secrets enable -path=ssh-client-signer ssh
bao write ssh-client-signer/config/ca generate_signing_key=true
bao write ssh-client-signer/roles/client -<<'EOH'
{
  "key_type": "ca",
  "allow_user_certificates": true,
  "allowed_users": "*",
  "default_extensions": { "permit-pty": "" },
  "ttl": "30m"
}
EOH

# 3. Policy that lets the sshs3 role sign with that SSH role
bao policy write sshs3-sign - <<'EOH'
path "ssh-client-signer/sign/client" { capabilities = ["create", "update"] }
EOH

In sshs3, select OpenBao / HashiCorp Vault and enter the mount names and roles (section 5). A requested principal must be allowed by the SSH role's allowed_users. The requested Default Principals are sent as valid_principals, and the SSH role's ttl sets how long the certificate is valid.

4.3 Your servers

Servers only need to trust the CA's public key. Print it from the CA (Step-CA: step ssh config --roots; OpenBao: bao read -field=public_key ssh-client-signer/config/ca), save it on the server, for example as /etc/ssh/ca.pub, and add it to sshd_config:

SSH-CONFIG
TrustedUserCAKeys /etc/ssh/ca.pub

By default a certificate is accepted for any principal that equals the login name. To map principals to accounts yourself, use AuthorizedPrincipalsFile (see sshd_config(5)). No per-user authorized_keys entries are needed.


5. Configure sshs3

Open Settings → Security & Smartcard and click Add CA. You can define several CAs; Set as Default chooses the one that Log In with SSH CA in the toolbar uses.

Editing an OpenBao / Vault CA with a smartcard key source
Editing an OpenBao / Vault CA with a smartcard key source
FieldMeaning
NameLabel shown in the list.
Backend ProviderSmallstep (step-ca) or OpenBao / HashiCorp Vault.
CA URLBase URL of the CA, for example https://ca.example.com:9000. A missing scheme is treated as https://.
OIDC Issuer URLThe identity provider's issuer, for example https://keycloak.example.com/realms/sshca.
OIDC Client IDThe public client from section 3.
Key SourceEphemeral in-memory key (Ed25519) or Smartcard / PIV (PKCS#11) (section 7).
Vault Auth Mount (Vault only)JWT auth mount. Default jwt.
Vault Auth Role (Vault only)JWT auth role. Default default.
Vault SSH Secrets Mount (Vault only)SSH engine mount. Default ssh.
Vault SSH Role (Vault only)SSH signing role. Defaults to the auth role if empty, then client.
Vault Namespace (Vault only)Enterprise namespace, optional.
Default PrincipalsComma-separated principals to request, optional.
Custom Root CA CertificatePEM of a private root CA that signs the CA and identity provider, optional. Blank means the system trust store.

Authenticate with CA on an entry runs the full sign-in for that CA and reports Successfully authenticated as <name>! Certificate active. or the error. It is a real login, not a dry run.

Note

Only https, except on loopback. sshs3 refuses http:// for the identity provider and CA unless the issuer or CA address is a loopback address (localhost, 127.x.x.x, [::1]). That makes local test setups possible without opening the door to plain-text token traffic.


6. Daily Use

The status badge

The SSH CA status badge with an active certificate
The SSH CA status badge with an active certificate

Click the Shield icon in the top toolbar, or focus it and press Enter. Press Escape to close the popup.

ElementWhat it shows
Active / InactiveWhether a valid certificate is loaded.
User, PrincipalsThe identity and the principals the CA granted.
Valid UntilExpiry as yyyy-mm-dd HH:mm.
Time LeftCountdown; amber below two minutes, red when expired.
SerialThe certificate serial number.
Log In with SSH CAStarts a sign-in with the default CA (inactive state).
Renew CertificateRenews now (active state).
Log OutRemoves the certificate from the agent and ends the session.

Connection profiles

A connection profile that uses the active SSH CA session
A connection profile that uses the active SSH CA session

Set Authentication to SSH Agent and tick Use active SSH Certificate Authority session. Terminals, SFTP and file-manager connections, SSH tunnels and Test connection then use the active certificate. When no certificate is active, sign in first; the profile does not trigger a sign-in by itself.

Renewal

  • Automatic: if the identity provider issues a refresh token, sshs3 renews the certificate about 60 seconds before it expires. It does this silently and never opens a browser by itself. With the smartcard key source the card has to be unlocked: if you locked it, sshs3 asks for the PIN again. If the silent renewal fails, the certificate runs out and the badge shows Inactive.
  • Manual: Renew Certificate tries a silent refresh first and falls back to a browser sign-in if that fails.
  • Log Out also cancels a sign-in that is still waiting in the browser.
  • Restarting sshs3 ends the session; sign in again.

7. Key Sources

Key SourcePrivate keyHow the certificate is used
Ephemeral in-memory key (Ed25519)Generated in memory for every login and never written to disk.Private key and certificate are added to the app agent with a lifetime that ends with the certificate.
Smartcard / PIV (PKCS#11)Stays on the card or token (the PIV authentication key, slot 9A, on PIV cards such as a YubiKey).The card's public key is signed by the CA. The certificate is written to a 0600 file in the app's runtime directory and passed to ssh with -o CertificateFile; the connection is limited to that card's key.

The smartcard source works with any PKCS#11 smartcard that exposes an authentication key, not only YubiKeys. sshs3 asks for the card's PIN when you sign in.

Warning / Limitation

The smartcard key source unlocks the card for your other profiles under the Global policy: The key source loads the card into AppAgent, the same agent the Global (App Lifetime) PIN policy uses. The PIN you entered for SSH CA then also covers every profile that uses this card, until you click Lock All Now or quit sshs3. Logging out of SSH CA does not lock the card. Under Always Prompt and Once Per Terminal Connection your PKCS#11 profiles keep their own prompts. See Security, Hardware Keys & Smartcards for the PIN policies.


8. Security Model

sshs3 checks every certificate before it uses it, and keeps tokens out of storage:

ProtectionDetail
Sign-inAuthorization code flow with PKCE (S256), a random state and nonce. The ID token must carry the same nonce, and a different or missing one aborts the login before anything is sent to the CA.
Loopback receiverListens on 127.0.0.1 (or ::1) only, accepts one callback, checks the Host header, and ignores answers with a wrong state.
Transporthttps required, certificates verified against the system store or your Custom Root CA Certificate. Redirects are not followed, so tokens are not forwarded elsewhere.
Certificate checksIt must be a user certificate, carry at least one principal, be inside its validity window, and be issued for the exact key sshs3 asked the CA to sign.
Nothing sensitive on diskIdentity tokens, refresh tokens and the ephemeral private key live in memory only. The PIV certificate file contains the public certificate.
Cancel and logoutLog Out aborts running CA requests and removes the certificate from the agent and the certificate file.

Technical Architecture: Where Things Live

SshCaService owns the session, installs the certificate and schedules renewal. oidcFlow runs the browser sign-in and token refresh, and an operation queue serialises login, renewal and logout. The Step-CA and Vault providers only perform the signing call. Terminals and SFTP receive the agent socket and, for smartcards, the certificate file through the same preparation step as other agent-based profiles.


9. Troubleshooting & Diagnostics

Symptom / ErrorProbable Root CauseCorrective Action
No SSH CA configuration provided or configured in settingsNo CA is saved.Add one under Settings → Security & Smartcard → SSH CA and save the settings.
Failed to fetch OIDC discovery configuration … (HTTP …)Wrong OIDC Issuer URL, or the provider is unreachable.Open <issuer>/.well-known/openid-configuration in a browser; use the exact issuer including the realm path.
Only https URLs are allowedThe issuer or CA is http:// and not a loopback address.Use https, or run a local test CA on 127.0.0.1.
Timed out waiting for the browser sign-in …The browser sign-in was not finished within two minutes, or the redirect was blocked.Try again; check that the provider allows http://127.0.0.1:<port>/callback for the client.
OIDC token exchange failed: …The client is confidential, or the redirect URI is not allowed.Use a public client with PKCE; allow loopback redirects with any port.
OIDC id_token nonce does not match the sign-in requestThe provider does not return the nonce it was sent.Use a provider or client configuration that returns the nonce claim in the ID token.
Step-CA certificate signing failed: …The provisioner rejects the token (client ID, domain or SSH disabled).Match the provisioner's client ID with OIDC Client ID; check domains and that SSH is enabled.
Vault OIDC login failed: …The JWT role rejects the token.Check bound_audiences, user_claim and oidc_discovery_url; match Vault Auth Mount and Vault Auth Role.
Vault SSH key signing failed: …The token's policy or the SSH role does not allow the request.Allow create, update on <mount>/sign/<role>; check allowed_users for the principals.
SSH CA issued invalid certificate: …The CA returned a host certificate, no principals, an expired certificate, or one for another key.Fix the CA role or provisioner; the message names the reason.
No smartcard detected …The smartcard is not inserted, or no PKCS#11 module is available.Insert the card and install a module such as opensc or libykcs11.
Login succeeds but the server answers Permission deniedThe server does not trust the CA, or no granted principal matches the login name.Check TrustedUserCAKeys, then compare the badge's Principals with the account (AuthorizedPrincipalsFile). Use ssh -v to see the certificate being offered.
Silent SSH CA renewal failed; sign in again before the certificate expires (log)The refresh token was rejected or expired.Click Renew Certificate or Log In with SSH CA.
Smartcard profiles no longer ask for the PIN after the loginThe card was loaded into the shared agent under the Global policy.Click Lock All Now, or use Once Per Terminal Connection or Always Prompt.