← Start of tutorial

WIUNIX ACADEMY

OpenSSH user certificates: short-lived access and revocation in a loopback lab

Page 2 of 3. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Debian 12Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment4 editable values
05

LET’S BUILD

Check host policy before starting the listener

This listener is loopback-only; do not open a public port. If host policy denies this process or its new files, stop and resolve the evidenced denial within the VM. The administration daemon and its authorization remain unchanged.

bash

Authorized terminal on the host identified in this section

sudo journalctl -k --since '-10 min' --no-pager -n 80
How do I know it worked?

Host-policy logs are correlated with the failure window; this Debian/Ubuntu path adds no port mapping or firewall rule.

06

LET’S BUILD

Validate and inspect the separate listener configuration

Run the parser and effective-config view against the explicit lab file. Inspect the emitted port/listenaddress, trust and revocation paths, forced command and authentication settings. A failure about a missing privilege-separation directory means the packaged service prerequisites must be repaired through its native package procedure on the disposable VM; do not modify the production daemon or disable privilege separation.

bash

Authorized Linux terminal

sudo /usr/sbin/sshd -t -f '/var/lib/academy-ssh-cert-lab/sshd_config' || exit 1
sudo /usr/sbin/sshd -T -f '/var/lib/academy-ssh-cert-lab/sshd_config' | awk '$1 ~ /^(port|listenaddress|trustedusercakeys|authorizedprincipalsfile|revokedkeys|forcecommand|authenticationmethods|authorizedkeysfile)$/ {print}'
How do I know it worked?

The explicit config parses and only127.0.0.1 on the selected port is configured.

07

LET’S BUILD

Run the lab daemon in a dedicated foreground terminal

Open a separate authorized terminal on the lab VM and keep it attached to this foreground process. This command does not start/restart/enable the system ssh service. Leave its output visible for certificate rejection reasons; logs can contain usernames and must remain private. Stop this lab daemon with Ctrl+C in its own terminal when the exercise is finished.

bash

Authorized Linux terminal

sudo /usr/sbin/sshd -D -e -f '/var/lib/academy-ssh-cert-lab/sshd_config'
How do I know it worked?

The dedicated process listens only on loopback; use another terminal for the client checks.

08

LET’S BUILD

Pin the lab host key through the local trusted channel

In the other terminal, check the listener and derive a separate known_hosts file from the host public key you just generated locally. This authenticated local file is the trust source; a network keyscan by itself would not authenticate a remote host. No existing known_hosts entries are deleted or bypassed.

bash

Authorized Linux terminal

sudo ss -lntp 'sport = :22222'
sudo ssh-keygen -lf '/var/lib/academy-ssh-cert-lab/host.pub'
sudo awk '{print "[127.0.0.1]:22222 " $1 " " $2}' '/var/lib/academy-ssh-cert-lab/host.pub' | sudo tee '/var/lib/academy-ssh-cert-lab/known_hosts' >/dev/null
sudo chmod 0644 '/var/lib/academy-ssh-cert-lab/known_hosts'
How do I know it worked?

Only loopback is listening and the separate known_hosts entry matches the independently recorded host fingerprint.

09

LET’S BUILD

Issue one constrained fifteen-minute user certificate

Sign only the public user key. Serial1001 and key ID identify this issuance; the principal must match the account mapping created next. A one-minute start allowance handles a small lab clock difference, not a substitute for correct time. Clearing extensions removes forwarding/PTY defaults; the isolated daemon also enforces a harmless fixed command. The CA passphrase is entered interactively.

bash

Authorized Linux terminal

sudo ssh-keygen -s '/var/lib/academy-ssh-cert-lab/user-ca' -I academy-user-1001 -z 1001 -n 'academy-operator' -V -1m:+15m -O clear '/var/lib/academy-ssh-cert-lab/client.pub'
sudo ssh-keygen -L -f '/var/lib/academy-ssh-cert-lab/client-cert.pub'
How do I know it worked?

Inspection shows a USER certificate, expected CA/principal, serial1001, short validity and no default extensions.

10

LET’S BUILD

Prove the constrained certificate login

Use the isolated client key and certificate, no agent and no user/system SSH config. Enter the user-key passphrase interactively. This runs the server’s fixed id command as labuser, regardless of a requested remote command. A successful result proves this one principal/CA/validity/account path; it does not test production CA custody or fleet rollout.

bash

Authorized Linux terminal

sudo ssh -F /dev/null -o StrictHostKeyChecking=yes -o 'UserKnownHostsFile=/var/lib/academy-ssh-cert-lab/known_hosts' -o IdentitiesOnly=yes -o IdentityAgent=none -i '/var/lib/academy-ssh-cert-lab/client' -o 'CertificateFile=/var/lib/academy-ssh-cert-lab/client-cert.pub' -p '22222' 'labuser@127.0.0.1'
How do I know it worked?

The server prints the existing non-root account identity and closes. A rejected login must be correlated with the dedicated daemon log.

Explore all tutorials ↗