跳到主要内容

SSH 密钥登录与安全加固

两类密钥不要混淆

  • 用户密钥:私钥留在 WSL2;公钥追加到 VPS 用户的 ~/.ssh/authorized_keys。它回答“谁在登录”。
  • 主机密钥:私钥留在 VPS 的 /etc/ssh/;客户端在首次连接时保存公钥指纹。它回答“连到的是不是原来那台服务器”。

私钥没有 .pub 后缀,绝不能复制到 VPS、网站或聊天中。需要确认身份时输出公钥指纹:

[LOCAL: WSL2]
test -f "$LOCAL_SSH_PUBLIC_KEY"
ssh-keygen -lf "$LOCAL_SSH_PUBLIC_KEY"

安装用户公钥

[LOCAL: WSL2]
ssh-copy-id -i "$LOCAL_SSH_PUBLIC_KEY" "$VPS_USER@$VPS_PUBLIC_IPV4"

ssh-copy-id会追加公钥,不应覆盖已有的 authorized_keys。远端权限通常应为:

[REMOTE: VPS]
stat -c '%a %U:%G %n' ~/.ssh ~/.ssh/authorized_keys
# 期望:目录 700,文件 600

明确测试“只用公钥”

普通 ssh 成功可能仍是密码认证。用客户端选项排除密码:

[LOCAL: WSL2]
ssh \
-i "$HOME/.ssh/id_ed25519" \
-o IdentitiesOnly=yes \
-o PreferredAuthentications=publickey \
-o PasswordAuthentication=no \
"$VPS_USER@$VPS_PUBLIC_IPV4"

必须在第二个新会话中通过,同时保留原始已知可用会话。

用配置片段加固 sshd

Ubuntu 支持 /etc/ssh/sshd_config.d/*.conf。OpenSSH 对多数指令采用“获得的第一个值”,而通配符文件名按词法顺序读取。因此较晚的 99-...conf 不一定覆盖较早的 cloud-init 设置;必须检查实际 include 顺序和有效值。

[REMOTE: VPS]
grep -nE '^[[:space:]]*Include|^[[:space:]]*(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication)' \
/etc/ssh/sshd_config
sudo find /etc/ssh/sshd_config.d -maxdepth 1 -type f -print

在确认顺序后创建一个足够早、职责单一的片段,例如:

/etc/ssh/sshd_config.d/00-personal-infra.conf
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

这里关闭的是 SSH 密码认证和 root 直接登录,不是删除用户,也不是删除已有公钥。

验证、重载、再验证

[REMOTE: VPS]
sudo sshd -t
sudo sshd -T | grep -E \
'^(permitrootlogin|passwordauthentication|pubkeyauthentication) '
sudo systemctl reload ssh

预期有效值:

permitrootlogin no
passwordauthentication no
pubkeyauthentication yes

sshd -t无输出且退出码为 0 才算语法通过。使用 reload让服务重新读取配置,不需要为了这项修改重启整台 VPS。重载后,再从 WSL2 打开第三个全新公钥会话

首次连接时的主机指纹

看到 “authenticity can't be established” 时,不要机械输入 yes。可以从已知可信的旧路径读取 VPS 公钥指纹:

[REMOTE: known-good session]
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

与客户端提示逐字一致后再接受。系统重装会合理地改变主机密钥;未计划的变化则可能是地址复用、中间人攻击或连错机器。

故障排查

[REMOTE: keep known-good session open]
sudo sshd -t
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication) '
sudo journalctl -fu ssh.service

客户端可增加 -vvv查看协商过程,但日志可能包含用户名、地址和路径,公开前要脱敏。任何新连接失败都应停止防火墙收口,并保留现有会话。