SSH Certificates
EnterpriseHow the NetStacks Controller signs short-lived Ed25519 SSH user certificates at login, auto-renews them, and lets devices trust a single CA public key.
Overview
The SSH Certificate Authority lives in the NetStacks Controller. It is part of the Enterprise deployment. In standalone Terminal mode the agent generates its own Ed25519 keypair and stores any certificate locally, but there is no organization-wide CA without the Controller.
SSH certificates replace per-user key distribution with a single trust anchor. Instead of copying every engineer's public key into authorized_keys on every device, you deploy one CA public key per device. The Controller then mints a short-lived, identity-bound certificate for each user, and the device accepts any certificate that its trusted CA signed.
- One Ed25519 CA per organization, generated and held by the Controller
- Certificates are signed automatically at login — no manual issuance step
- Default validity is 8 hours; the terminal renews before expiry
- Each certificate carries the user's username as its principal and a unique serial
- The CA private key is stored encrypted in the Controller vault and never leaves it
- Every issued certificate is recorded (serial, principal, validity, user) for audit
With raw SSH keys you must push every user's public key to every device and remove it on offboarding. With certificates you deploy the CA public key once per device and control access centrally. Certificates also expire on their own, so a leaked credential is only useful for hours, not forever.
How It Works
The certificate is issued at login
The flow is driven by the login exchange, not by a separate "request a certificate" action. When the terminal logs in, it sends its locally generated SSH public key. If the organization has a default CA, the Controller signs a certificate and returns it inside the login response.
- Terminal generates a keypair — an Ed25519 keypair is created and the private key is stored encrypted; the public key is sent during login.
- Controller looks up the default CA — the org's active, default CA is loaded and its private key is decrypted in memory from the vault.
- CA signs the certificate — the user's public key is signed with the username as the principal, an 8-hour validity window, and a unique serial.
- Certificate is returned and stored — the login response carries an
ssh_certificateobject containing the certificate, the CA public key, and the validity timestamps. - Renewal before expiry — the terminal requests a fresh certificate from the auto-sign endpoint before
valid_beforepasses; the old one simply expires.
What the Controller returns: SignedCertInfo
The signed-certificate payload is the same shape whether it comes from login or auto-renewal:
{
"certificate": "[email protected] AAAAIHNza...",
"ca_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...",
"valid_after": "2026-06-16T08:00:00Z",
"valid_before": "2026-06-16T16:00:00Z",
"serial": 43
}certificate— the signed OpenSSH user certificate the terminal presents when connecting.ca_public_key— the CA public key, so the terminal can cache and verify the trust anchor locally.valid_after/valid_before— the RFC 3339 validity window. Renewal is driven byvalid_before.serial— a per-CA monotonic serial number that uniquely identifies the certificate in the audit record.
Certificate contents
Certificates are signed as OpenSSH user certificates (CertType::User) with:
| Field | Value |
|---|---|
| Principal | The authenticated user's username |
| Validity | 8 hours from signing time |
| Key ID | username@terminal-auto at login, username@terminal-renew on renewal |
| Extensions | permit-pty, permit-agent-forwarding |
| Serial | Unique per CA, incremented on each signing |
The auto-renewal endpoint enforces a short per-user cooldown (a few tens of seconds) between signings. A normal terminal renews roughly a few times per day, well under the limit; rapid-fire requests are rejected as a safeguard against a stolen session token being used to harvest certificates.
Managing the CA
CA administration is exposed through admin API endpoints (under /api/admin/ssh-ca) and requires an administrator. A typical lifecycle:
- Create a CA — the Controller generates a fresh Ed25519 keypair, encrypts the private key into the vault, and returns the public key.
- Set it as default — the default CA is the one used for login auto-signing and renewal. Only one CA is the default per organization.
- Distribute the public key — install it on every device that should accept certificate logins (see the next section).
- Rotate if needed — create a new CA, set it as default, deploy its public key, then remove the old trust anchor from devices.
Each CA stores a name, an optional description, its public key, an active flag, a default flag, and a next_serial counter. The default CA cannot be deleted while it is the default — promote another CA first.
Audit and revocation
Every certificate the Controller signs is recorded with its serial, type, key ID, principals, validity window, and the issuing user. The data model tracks a revocation flag per certificate, so the audit trail can answer "who minted what, for whom, and when." In practice the primary security control is the short 8-hour lifetime plus auto-renewal: stop a user from logging in and their certificates expire on their own within hours.
Trusting the CA on Devices
For a device to accept certificate-based logins, its SSH server must trust the CA public key. On OpenSSH (Linux, BSD, and many network appliances that run OpenSSH-derived daemons) this is the standard TrustedUserCAKeys directive. NetStacks does not push this configuration for you — it is a one-time setup per device using native OpenSSH features.
# 1. Copy the CA public key onto the device
scp netstacks-ca.pub admin@core-rtr-01:/etc/ssh/netstacks-ca.pub
# 2. Trust the CA for user authentication
echo "TrustedUserCAKeys /etc/ssh/netstacks-ca.pub" | sudo tee -a /etc/ssh/sshd_config
# 3. Reload the SSH daemon
sudo systemctl reload sshdThe CA public key is not sensitive — distribute it as widely as you like. Only the CA private key (held encrypted in the Controller vault) can sign certificates.
The certificate's principal is the user's NetStacks username. The login on the device must match a principal in the certificate. If the device account name differs from the NetStacks username, use OpenSSH's AuthorizedPrincipalsFile to map allowed principals to that account:
# /etc/ssh/sshd_config
# Trust the NetStacks CA for user certificate authentication
TrustedUserCAKeys /etc/ssh/netstacks-ca.pub
# Optional: map which certificate principals may log in as the local account.
# %u expands to the target login name on the device.
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%uSSH user-certificate support depends on the platform. Hosts and appliances running modern OpenSSH support TrustedUserCAKeys; some network OSes support SSH user certificates and some do not. For devices that cannot validate certificates, fall back to SSH keys or passwords stored in the credential vault. Check your vendor's documentation for the exact configuration commands.
API & Inspection Examples
Create a CA (admin)
curl -X POST https://controller.example.net/api/admin/ssh-ca \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Production CA",
"description": "Primary SSH CA for production network devices"
}'
# 201 Created
# {
# "id": "ca-uuid-here",
# "name": "Production CA",
# "publicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...",
# "isActive": true,
# "isDefault": false
# }Promote a CA to default (admin)
# The default CA is what login and auto-renewal sign with.
curl -X POST https://controller.example.net/api/admin/ssh-ca/${CA_ID}/default \
-H "Authorization: Bearer ${ADMIN_TOKEN}"Fetch the CA public key for device deployment
# Public endpoint: returns the default CA public key in OpenSSH format.
curl https://controller.example.net/api/ssh-ca/public-key \
-o netstacks-ca.pub
cat netstacks-ca.pub
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...Inspect a signed certificate
ssh-keygen -Lf netstacks-cert.pub
# Type: [email protected] user certificate
# Public key: ED25519-CERT SHA256:xR3gN7...
# Signing CA: ED25519 SHA256:kM4pQ2... (using ssh-ed25519)
# Key ID: "jsmith@terminal-auto"
# Serial: 43
# Valid: from 2026-06-16T08:00:00 to 2026-06-16T16:00:00
# Principals:
# jsmith
# Critical Options: (none)
# Extensions:
# permit-agent-forwarding
# permit-ptyWhen the local agent sidecar is running (standalone mode, not Enterprise), it exposes GET /cert/status for the current certificate's validity and fingerprint, GET /cert/public-key for the agent's SSH public key, and POST /cert/store to save a signed certificate. In Enterprise mode the sidecar is not used — the certificate arrives in the login response instead.
Questions & Answers
- Q: What is an SSH certificate?
- A: A signed statement from a Certificate Authority granting a specific public key the right to authenticate as certain usernames (principals) on devices that trust the CA. Unlike a raw SSH key, a certificate has an expiration time, an identity, and a serial number.
- Q: How does NetStacks issue certificates?
- A: Automatically, at login. The terminal sends its public key during login; if the organization has a default CA, the Controller signs an 8-hour certificate and returns it in the login response as an
ssh_certificateobject containing the certificate, the CA public key, and the validity window. The terminal renews before the certificate expires. - Q: What is the default validity period?
- A: 8 hours. Login auto-signing and auto-renewal both use an 8-hour window. The admin-driven sign endpoint also defaults to 8 hours and accepts a
validityHoursoverride. There is no separate short-lived "automation certificate" tier — the same 8-hour model and auto-renewal apply. - Q: What principal does the certificate carry?
- A: The authenticated user's NetStacks username. If your device accounts use a different login name, map the principal with OpenSSH's
AuthorizedPrincipalsFile. - Q: How do I deploy CA trust to a device?
- A: Fetch the CA public key from
/api/ssh-ca/public-key, copy it to the device, addTrustedUserCAKeys /etc/ssh/netstacks-ca.pubto/etc/ssh/sshd_config, and reload sshd. NetStacks does not push this configuration automatically. - Q: How does revocation work?
- A: Every issued certificate is recorded with its serial, principal, validity window, and issuing user, and the data model tracks a revocation flag per certificate. Because certificates are short-lived (8 hours) and auto-renewed, the practical control is to disable the user so their certificates expire on their own within hours.
- Q: What if the CA private key is compromised?
- A: Create a new CA, set it as default, deploy its public key to all devices, and remove the old CA public key from each device's
TrustedUserCAKeys. Because certificates are short-lived, the exposure window is inherently bounded.
Troubleshooting
No certificate is issued at login
- Confirm the organization has a CA marked both active and default — signing only uses the default CA.
- Verify the terminal is sending a public key during login; without one, the Controller returns no
ssh_certificate. - Check the Controller logs for an auto-sign warning (for example an invalid public key or a missing default CA).
"Certificate has expired"
- The 8-hour validity window has passed — the terminal should renew automatically; re-authenticate if it has not.
- Verify the Controller and device clocks agree; certificates validate against UTC timestamps.
CA not trusted on the device
- Confirm the CA public key is installed:
cat /etc/ssh/netstacks-ca.pub. - Confirm
TrustedUserCAKeyspoints to that file insshd_configand that sshd was reloaded. - Make sure you deployed the public key of the default CA — if you have several CAs, signing uses the default one.
# Confirm sshd loaded the CA trust setting
sshd -T | grep trustedusercakeys
# trustedusercakeys /etc/ssh/netstacks-ca.pub
# Test certificate auth manually (cert next to its private key)
ssh -o CertificateFile=./netstacks-cert.pub \
-i ./netstacks-key \
[email protected]Principal mismatch
- Inspect the certificate's principals:
ssh-keygen -Lf cert.pub. The principal is the NetStacks username. - If the local login name differs, add it to the device's
AuthorizedPrincipalsFilemapping for that account.
Clock skew
Certificates carry UTC valid_after/valid_before bounds. If the Controller and device clocks differ significantly, a certificate can be rejected as "not yet valid" or "expired" even within its intended window.
- Run NTP on the Controller host and on managed devices.
- Verify synchronization:
timedatectl statuson Linux,show clockon network devices.
Related Features
Explore related credential and access features:
- Credential Vault — how the vault encrypts the CA private key and all other credentials
- SSH Passwords & Keys — fallback authentication for devices that cannot validate certificates
- Adding Devices — onboard devices and choose how they authenticate
- Authentication (LDAP/OIDC) — the login flow that triggers certificate signing
- Audit Logs — review every certificate issued and by whom