Fullmoon System

SSH Key Authentication: Ed25519, ssh-agent, and Server Security

EdwardMoon

SSH key authentication is a chain of trust: verify server identity, protect the private key, distribute the public key, test a new session, and strengthen login policy. It is more than removing a password prompt. Keep the private key on the client and protect it with a passphrase and ssh-agent.

This guide uses Ed25519 and covers an RSA alternative for FIPS mode, known_hosts verification, authorized_keys permissions, sshd validation, key revocation, and recovery in operational order.

SSH key authentication: client private key and agent, known_hosts, server authorized_keys, and new-session validation
Keep the private key on the client, verify the server and public-key login, then strengthen authentication policy

Two Different Checks in SSH Authentication

What is verified Where it is stored Risk if verification fails
Is the server authentic? Client known_hosts May connect to a man-in-the-middle server
Is the user authorized? Server authorized_keys Stolen or unauthorized keys may gain access

Working public-key user authentication alone does not establish a safe connection. Verify the server host-key fingerprint through a trusted console, asset management system, or separate channel to detect server impersonation.

Generate a Passphrase-Protected Client Key

Ed25519 is a compact, secure default for ordinary environments. FIPS mode may not permit it; under such policies, use an RSA key of sufficient length. Choose purpose-specific filenames to avoid overwriting existing keys.

install -d -m 0700 ~/.ssh
ssh-keygen -t ed25519 -a 100   -f ~/.ssh/id_ed25519_ops   -C 'ops-admin@client'
chmod 0600 ~/.ssh/id_ed25519_ops
chmod 0644 ~/.ssh/id_ed25519_ops.pub

RSA Alternative for FIPS Policy

ssh-keygen -t rsa -b 3072 -o -a 100   -f ~/.ssh/id_rsa_ops   -C 'ops-admin@client'
Never upload private keys to servers, tickets, messaging systems, or Git repositories. Only the single-line public key from the .pub file belongs on the server.

Manage Unlocked Keys with ssh-agent

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519_ops
ssh-add -l

Verify the Server Fingerprint Through a Separate Channel

Display the public host-key fingerprint from the server console and compare it with the client's first-connection prompt. ssh-keyscan collects keys but does not prove they belong to the real server; never trust its output without verification.

sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
sudo ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
ssh-keyscan -t ed25519 server.example.com > /tmp/server.hostkey
ssh-keygen -lf /tmp/server.hostkey
install -m 0600 /tmp/server.hostkey ~/.ssh/known_hosts.ops
rm -f /tmp/server.hostkey
Use the known_hosts file only when the SHA256 fingerprints in both outputs exactly match the value verified through a separate channel. If they differ, stop connecting and investigate DNS, IP addresses, and rebuild records.

Deploy the Public Key to authorized_keys

For initial setup, connect once through an already authorized password login or console and add the public key. When available, ssh-copy-id simplifies duplicate handling and permissions.

ssh-copy-id -i ~/.ssh/id_ed25519_ops.pub ops@server.example.com

Manual Deployment Without ssh-copy-id

cat ~/.ssh/id_ed25519_ops.pub   | ssh ops@server.example.com     'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys'
# From here, run commands in the server's ops user session.
chmod 0700 ~/.ssh
chmod 0600 ~/.ssh/authorized_keys
restorecon -RFv ~/.ssh

On SELinux-enabled RHEL-family systems, file contexts must be correct as well as permissions. Also verify that other users cannot write to the home directory.

Use a Client Alias and Explicit Key

Host prod-app-01
    HostName server.example.com
    User ops
    IdentityFile ~/.ssh/id_ed25519_ops
    IdentitiesOnly yes
    UserKnownHostsFile ~/.ssh/known_hosts.ops
chmod 0600 ~/.ssh/config
ssh -v prod-app-01

Verify Before Disabling Password Login

Keep the current administrator session open while testing public-key login from another terminal. Do not disable PasswordAuthentication before verifying recovery-console access and sudo privileges.
ssh -o PreferredAuthentications=publickey   -o PasswordAuthentication=no   prod-app-01
sudo -v
whoami

Check sshd Syntax and Effective Settings

sudo sshd -t
sudo sshd -T   | grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin) '

After key authentication works, separate policy into a drop-in file. Include order and Match blocks can change effective settings, so verify sshd -T output rather than relying only on the file contents.

sudo install -d -m 0755 /etc/ssh/sshd_config.d
sudo tee /etc/ssh/sshd_config.d/20-key-auth.conf >/dev/null <<'CONF'
PubkeyAuthentication yes
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
CONF

sudo sshd -t
sudo sshd -T | grep -E "^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin) "
sudo systemctl reload sshd
ssh -o PreferredAuthentications=publickey prod-app-01
sudo journalctl -u sshd --since '-10 minutes' --no-pager

Restrict and Revoke Keys

Separate automation keys from general-purpose human administrator keys. Where appropriate, use authorized_keys options to restrict source addresses or features. These options can block required tunneling or PTY behavior, so validate them in a test environment first.

from="198.51.100.0/24",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... deploy-key

Remove the corresponding public-key line when a key is compromised or a user leaves. For centralized revocation, create an OpenSSH KRL and reference it through sshd's RevokedKeys. Update an existing KRL with -u to preserve previous revocations, build it in a temporary file on the same filesystem, then replace it atomically. If sshd cannot read the KRL, it may reject all public-key authentication, so retain an existing administrator session and recovery console. On servers using Match blocks, inspect the real connection conditions with a command such as sshd -T -C user=ops,host=client.example.com,addr=198.51.100.10.

KRL_STAGE_DIR=$(sudo mktemp -d /etc/ssh/krl-stage.XXXXXX)
if sudo test -f /etc/ssh/revoked.krl; then
  sudo cp -p /etc/ssh/revoked.krl "$KRL_STAGE_DIR/revoked.krl"
  sudo ssh-keygen -k -u -f "$KRL_STAGE_DIR/revoked.krl" compromised_key.pub
else
  sudo ssh-keygen -k -f "$KRL_STAGE_DIR/revoked.krl" compromised_key.pub
fi
sudo chown root:root "$KRL_STAGE_DIR/revoked.krl"
sudo chmod 0644 "$KRL_STAGE_DIR/revoked.krl"
sudo mv "$KRL_STAGE_DIR/revoked.krl" /etc/ssh/revoked.krl
sudo rmdir "$KRL_STAGE_DIR"
sudo restorecon /etc/ssh/revoked.krl
sudoedit /etc/ssh/sshd_config.d/20-key-auth.conf
# Add the following line to the file's global settings.
# RevokedKeys /etc/ssh/revoked.krl
sudo sshd -t
sudo sshd -T | grep "^revokedkeys "
sudo systemctl reload sshd

Troubleshooting SSH Key Authentication

ssh -vvv prod-app-01
ssh-add -l
ssh-keygen -lf ~/.ssh/id_ed25519_ops.pub
sudo journalctl -u sshd -b --no-pager
sudo namei -l /home/ops/.ssh/authorized_keys
sudo ls -ldZ /home/ops /home/ops/.ssh /home/ops/.ssh/authorized_keys
sudo sshd -T
  • If authentication is rejected after Offering public key, check the user, public-key line, permissions, and SELinux contexts.
  • For Too many authentication failures, explicitly set IdentitiesOnly and IdentityFile.
  • For REMOTE HOST IDENTIFICATION HAS CHANGED, verify server rebuilds or DNS changes instead of blindly deleting the old key.
  • If access fails after a configuration change, restore the drop-in through an existing session or console and run sshd -t.

Official Documentation and Related Guides

SSH key lifecycle management matters more than key generation alone. Keep private keys on clients, verify server fingerprints, test new sessions before tightening policy, and revoke compromised keys promptly. These practices make passwordless access a real security improvement.