Ubuntu服务器默认允许账号密码登录,但密码登录在公网环境下面临暴力破解、撞库和弱口令风险。将SSH登录方式从密码切换为密钥对,可以直接关闭密码认证通道,让没有私钥的客户端无法完成登录握手。这个操作不依赖额外安全组件,也不需要改变现有用户体系,只需要在服务器端和客户端各做一次配置,就能显著降低自动化攻击的成功率。

先明确一个基本事实:SSH密钥登录不是“更长的密码”,而是完全不同的认证机制。密码登录时,客户端把口令发送给服务器进行比对;密钥登录时,客户端用私钥对一段随机数据进行签名,服务器用公钥验签。私钥不会在网络上传输,即使中间人截获了登录流量,也无法还原出可用于登录的私钥。因此,只要私钥文件不被窃取,攻击者就无法冒充合法用户。

生成密钥对:选择合适的算法和长度

在客户端电脑上执行以下命令生成密钥对。客户端可以是Linux、macOS或Windows 10以上系统自带的OpenSSH。不要使用过时的RSA 1024位,推荐使用Ed25519算法,它安全性高、密钥短、签名速度快;如果某些旧系统不支持Ed25519,再考虑RSA 3072位或4096位。

ssh-keygen -t ed25519 -C "ubuntu-server-admin-2025"

命令会提示选择保存路径,默认是~/.ssh/id_ed25519,直接回车即可。接着会提示输入passphrase,这是对私钥文件本身的加密口令。即使私钥文件泄露,没有passphrase也无法直接使用。生产环境建议设置一个强度足够的passphrase,个人测试环境可以留空。

生成完成后,~/.ssh/目录下会出现两个文件:id_ed25519是私钥,必须妥善保管,权限应为600;id_ed25519.pub是公钥,内容可以公开,稍后要上传到服务器。

上传公钥到Ubuntu服务器

最直接的方式是使用ssh-copy-id命令。如果客户端是Linux或macOS,执行:

ssh-copy-id -i ~/.ssh/id_ed25519.pub 用户名@服务器IP

如果客户端是Windows,或者ssh-copy-id不可用,可以手动复制公钥内容。先查看公钥:

cat ~/.ssh/id_ed25519.pub

输出内容类似“ssh-ed25519 AAAAC3... comment”。登录服务器后,将这段内容追加到目标用户的~/.ssh/authorized_keys文件中:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAAC3... comment" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

权限非常关键。如果.ssh目录权限不是700、authorized_keys文件权限不是600,sshd会拒绝使用该公钥进行认证。很多密钥登录失败的问题都出在权限上,而不是密钥本身。

验证密钥登录是否生效

不要急着关闭密码登录。先新开一个终端窗口,尝试用私钥登录:

ssh -i ~/.ssh/id_ed25519 用户名@服务器IP

如果配置了passphrase,会提示输入私钥口令。登录成功后,说明密钥认证链路已经打通。此时可以检查服务器端日志确认认证方式:

sudo grep "Accepted publickey" /var/log/auth.log | tail -5

看到“Accepted publickey for 用户名”的记录,说明本次登录确实使用了密钥认证,而不是密码。

关闭密码登录:修改sshd配置

确认密钥登录稳定可用后,再修改SSH服务端配置。编辑/etc/ssh/sshd_config文件:

sudo nano /etc/ssh/sshd_config

找到以下配置项并修改。如果配置项前面有#号,需要去掉注释并修改值:

PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
PubkeyAuthentication yes

PasswordAuthentication no直接关闭密码认证,这是核心配置。ChallengeResponseAuthentication no关闭键盘交互认证,防止某些PAM模块绕过密码认证。UsePAM no可以进一步减少PAM带来的认证面,但要注意,如果系统依赖PAM做其他账户策略,需要评估后再决定。PubkeyAuthentication yes确保公钥认证开启,虽然默认就是yes,但显式写出可以避免被其他配置覆盖。

修改完成后,检查配置语法并重启sshd服务:

sudo sshd -t
sudo systemctl restart sshd

sshd -t没有任何输出代表配置语法正确。重启服务不会中断已经建立的SSH连接,所以当前会话不会断开。保持当前窗口不要关闭,再开一个新终端测试登录。如果新终端能正常用密钥登录,并且尝试用密码登录会被直接拒绝,说明配置成功。

测试密码登录是否被拒绝,可以使用以下命令强制使用密码认证:

ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no 用户名@服务器IP

如果返回“Permission denied”而不是提示输入密码,说明密码登录通道已经关闭。

加固authorized_keys文件与私钥管理

关闭密码登录后,私钥文件成为唯一的登录凭证,它的管理直接决定服务器安全。私钥文件应保存在可信设备上,不要上传到网盘、Git仓库或即时通讯工具。如果需要在多台设备上登录,可以为每台设备生成独立的密钥对,并把各自的公钥追加到服务器authorized_keys文件中。这样某台设备丢失后,可以单独删除对应的公钥行,不影响其他设备登录。

authorized_keys文件支持一些限制选项,可以在公钥行前面添加。例如限制某个公钥只能从特定IP登录:

from="192.168.1.100" ssh-ed25519 AAAAC3... comment

或者禁止该密钥用于端口转发:

no-port-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3... comment

这些选项可以在不改变全局配置的情况下,对不同密钥做细粒度控制。对于跳板机或共享账户场景尤其有用。

处理常见问题:权限、SELinux和旧客户端

密钥登录失败时,先检查服务器端日志,而不是反复重试。日志文件通常是/var/log/auth.log,Ubuntu 22.04及更新版本中,sshd日志可能整合在journald中,可以用journalctl -u sshd查看。常见错误包括“Authentication refused: bad ownership or modes”,这几乎都是.ssh或authorized_keys权限不对导致的。

如果服务器启用了SELinux或AppArmor,还需要检查文件上下文。Ubuntu默认使用AppArmor,对SSH密钥文件通常没有额外限制,但如果手动移动过home目录或使用非标准路径,可能需要更新策略。SELinux在Ubuntu上不是默认启用,但如果是自行安装的,需要确认.ssh目录的上下文为ssh_home_t。

旧版SSH客户端可能不支持Ed25519算法,如果必须兼容这些客户端,可以额外生成一个RSA密钥对,把RSA公钥也加入authorized_keys。但要注意,RSA密钥长度至少3072位,低于2048位的RSA密钥在现代OpenSSH中默认被拒绝。

多用户与自动化场景的密钥策略

如果服务器上有多个用户,每个用户都应该使用自己的密钥对,不要共享同一个私钥。管理员可以为每个用户生成密钥对,把公钥分发到对应用户的authorized_keys中,私钥交给用户本人。这样每个登录行为都可以追溯到具体用户,审计日志中会记录使用哪个公钥登录。

自动化脚本或CI/CD流水线需要登录服务器时,也应该使用独立的密钥对,而不是复用管理员的私钥。可以为自动化任务生成一个专用密钥,在authorized_keys中添加限制选项,比如只允许执行特定命令:

command="/usr/local/bin/backup.sh",no-pty,no-port-forwarding ssh-ed25519 AAAAC3... ci-backup

这样即使自动化环境的私钥泄露,攻击者也只能执行备份脚本,无法获得交互式shell。

密钥轮换与吊销

密钥和密码一样需要定期轮换。轮换流程是:生成新密钥对,把新公钥追加到服务器authorized_keys,用新私钥测试登录成功后,删除旧公钥行。轮换周期可以根据安全要求设定,通常建议6到12个月。如果怀疑私钥泄露,应立即吊销对应公钥,并检查服务器登录日志,确认是否有异常登录行为。

吊销操作很简单,编辑authorized_keys文件,删除对应公钥行即可。删除后,使用该私钥的客户端会立即无法登录,不需要重启sshd服务。

对于服务器数量较多的环境,可以考虑使用集中化的密钥管理方案,比如在服务器上部署一个配置管理工具,统一分发和吊销公钥。手动管理几台服务器还可以接受,但超过十台后,手动编辑authorized_keys容易出错,也难以及时吊销泄露的密钥。

禁用root直接登录

在关闭密码登录的同时,建议一并禁用root用户通过SSH直接登录。即使使用密钥认证,root账户也不应该暴露在SSH登录入口。在sshd_config中设置:

PermitRootLogin no

管理员先使用普通用户登录,再通过sudo或su切换到root。这样即使普通用户的私钥泄露,攻击者还需要突破sudo权限才能获得root控制权,增加了一层隔离。

备份与恢复注意事项

关闭密码登录后,如果私钥丢失,将无法通过SSH登录服务器。因此必须提前规划恢复通道。对于云服务器,可以通过云服务商提供的控制台VNC或串口登录,重新配置SSH或重置密钥。对于物理服务器,需要准备带外管理接口,如IPMI或iDRAC。如果这些通道都没有,建议在关闭密码登录前,至少保留一个备用登录方式,或者将密码登录限制在特定内网IP段,而不是完全关闭。

可以在sshd_config中使用Match块实现按来源限制:

PasswordAuthentication no
Match Address 192.168.1.0/24
    PasswordAuthentication yes

这样内网网段仍然可以使用密码登录,公网只能使用密钥。这种折中方案适合没有可靠带外管理的环境。

切换到SSH密钥登录是Ubuntu服务器安全加固的基础步骤,但它不是终点。密钥管理、权限控制、日志审计和恢复通道同样重要。把密码登录关闭后,攻击面缩小了,但私钥保护成为新的重点。配合禁用root登录、限制来源IP、定期审查authorized_keys文件,才能让SSH入口保持在一个可控的安全水平。