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.
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
LET’S BUILD
Register only the isolated lab labels and SELinux port
Use a dedicated VM with active SELinux and standard OpenSSH policy. Before adding anything, inspect local path customizations and the full port list: the chosen port must have no existing service-specific assignment, including a range, or prior local customization, and the lab prefix must have no existing equivalence. On conflict choose a different lab value; never modify/delete another mapping. Missing tools do not justify disabling SELinux. Generic unreserved_port_t ranges are different from a service-specific assignment; do not modify them. The lab adds one new ssh_port_t entry for its otherwise unused port and never uses modify.
Only after confirming no conflict, add the two lab-owned mappings below. Equivalence to the SSH configuration tree lets the standard host-key filename receive its policy label; restorecon is limited to the new lab tree. Retain the output and verify sshd_key_t for the host key. Stop on a different installed-policy result or unresolved AVC; do not generate a blanket allow rule. This registers an SELinux port, not a public firewall opening.
Authorized terminal on the host identified in this section
getenforce
sudo semanage fcontext -l -C
sudo semanage port -l
read -r -p 'Type ADD-LAB-MAPPINGS only after checking the conditions above: ' academy_policy_confirm
test "$academy_policy_confirm" = ADD-LAB-MAPPINGS || exit 1
sudo semanage fcontext -a -e /etc/ssh '/var/lib/academy-ssh-cert-lab' || exit 1
sudo semanage port -a -t ssh_port_t -p tcp '22222' || exit 1
sudo restorecon -Rv '/var/lib/academy-ssh-cert-lab'
ls -Zd '/var/lib/academy-ssh-cert-lab' '/var/lib/academy-ssh-cert-lab/ssh_host_rsa_key'
matchpathcon '/var/lib/academy-ssh-cert-lab/ssh_host_rsa_key'
sudo ausearch -m AVC,USER_AVC -ts recentThe host key has sshd_key_t; both newly added mappings are recorded and no inbound firewall port was opened.
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.
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}'The explicit config parses and only127.0.0.1 on the selected port is configured.
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.
Authorized Linux terminal
sudo /usr/sbin/sshd -D -e -f '/var/lib/academy-ssh-cert-lab/sshd_config'The dedicated process listens only on loopback; use another terminal for the client checks.
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.
Authorized Linux terminal
sudo ss -lntp 'sport = :22222'
sudo ssh-keygen -lf '/var/lib/academy-ssh-cert-lab/ssh_host_rsa_key.pub'
sudo awk '{print "[127.0.0.1]:22222 " $1 " " $2}' '/var/lib/academy-ssh-cert-lab/ssh_host_rsa_key.pub' | sudo tee '/var/lib/academy-ssh-cert-lab/known_hosts' >/dev/null
sudo chmod 0644 '/var/lib/academy-ssh-cert-lab/known_hosts'Only loopback is listening and the separate known_hosts entry matches the independently recorded host fingerprint.
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.
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'Inspection shows a USER certificate, expected CA/principal, serial1001, short validity and no default extensions.
LET’S BUILD
Prove the constrained certificate login
The client reads only the system crypto-policy algorithm backend, not user or normal system SSH connection configuration. The isolated identity/certificate, pinned known_hosts and absence of an agent are explicit. Do not weaken the active policy if it rejects the algorithm/certificate. Enter the user-key passphrase interactively; the server must return only the fixed id command as labuser and close.
Authorized Linux terminal
sudo ssh -F /etc/crypto-policies/back-ends/openssh.config -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'The server prints the existing non-root account identity and closes. A rejected login must be correlated with the dedicated daemon log.