SSH Key Access and Hardening
Keep user keys and host keys distinct
- A user key identifies who is logging in. The private key stays on WSL2; append the public key to the VPS user’s
~/.ssh/authorized_keys. - A host key lets you verify that you have reached the expected server. Its private key stays under
/etc/ssh/on the VPS; the client normally stores the public host key in~/.ssh/known_hostsand displays its fingerprint for verification.
The private key has no .pub suffix. Never copy your private key to the VPS, a website, or a chat. To check identity, display the public-key fingerprint:
test -f "$LOCAL_SSH_PUBLIC_KEY"
ssh-keygen -lf "$LOCAL_SSH_PUBLIC_KEY"
Install the user's public key
ssh-copy-id -i "$LOCAL_SSH_PUBLIC_KEY" "$VPS_USER@$VPS_PUBLIC_IPV4"
ssh-copy-id appends the public key; it should not overwrite the existing authorized_keys. The remote permissions should normally be:
stat -c '%a %U:%G %n' ~/.ssh ~/.ssh/authorized_keys
# Expected: directory 700, file 600
Explicitly test public-key-only access
A normal ssh connection may still succeed through password authentication. Exclude password authentication with explicit client options:
ssh \
-i "$HOME/.ssh/id_ed25519" \
-o IdentitiesOnly=yes \
-o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
-o KbdInteractiveAuthentication=no \
"$VPS_USER@$VPS_PUBLIC_IPV4"
This must succeed in a second, fresh session, while the original known-good session stays open.
Harden sshd with a configuration snippet
Ubuntu supports /etc/ssh/sshd_config.d/*.conf. OpenSSH uses the first obtained value for most directives and reads wildcard filenames in lexical order. A late 99-...conf therefore may not override an earlier cloud-init setting. Inspect the actual include order and effective values:
grep -nE '^[[:space:]]*Include|^[[:space:]]*(PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|PubkeyAuthentication|AuthenticationMethods)' \
/etc/ssh/sshd_config
sudo find /etc/ssh/sshd_config.d -maxdepth 1 -type f -print
After confirming the order, create a focused snippet that loads early enough, for example:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
PasswordAuthentication and KbdInteractiveAuthentication control separate authentication methods. On Ubuntu, PAM keyboard-interactive authentication may still ask for an account password. AuthenticationMethods publickey explicitly requires public-key authentication. This configuration is for a key-only server; it cannot be applied unchanged if the existing policy requires an additional keyboard-interactive MFA step. Do not delete users or existing public keys.
Validate, reload, and validate again
sudo sshd -t
sudo sshd -T | grep -E \
'^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|authenticationmethods) '
The expected effective values are:
permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
pubkeyauthentication yes
authenticationmethods publickey
The syntax check passes only if sshd -t produces no output and exits with code 0. If the configuration contains Match blocks, also use sudo sshd -T -C user=<login>,addr=<client-ip>,host=<client-hostname> to inspect the effective policy under the actual login conditions, replacing the placeholders. A global configuration dump does not replace this conditional check.
Only after these checks pass, run sudo systemctl reload ssh to reload the configuration; the whole VPS does not need a reboot. Then open a third, fresh public-key-only session from WSL2.
Verify the host fingerprint on first connection
When you see “authenticity can't be established,” do not automatically type yes. Read the VPS public host-key fingerprint through an existing trusted path:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Accept only if it matches the client prompt exactly. Reinstalling the system can legitimately change the host key; an unplanned change may instead indicate address reuse, a man-in-the-middle attack, or a connection to the wrong machine.
Troubleshooting
sudo sshd -t
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|authenticationmethods) '
sudo journalctl -fu ssh.service
On the client, add -vvv to ssh (ssh -vvv) to see the negotiation process. These logs may contain usernames, addresses, and paths; redact them before sharing publicly. If any new connection fails, stop tightening the firewall and keep the existing session open.