有一类服务器,我建议每个接手的人第一件事就是去改它的 SSH 登录方式。我说的不是换端口、改密码这种表面功夫,而是直接把 SSH 服务配置成“只认密钥、不认密码”。键登录这四个字,运维同行基本都听过,但真敢在线上环境动手关掉密码登录的人,比想象中少很多。原因不外乎两条:怕自己把自己锁在外面,以及觉得密码登录这么多年也没出什么事。这两条顾虑我都经历过,甚至有一次正在生产环境上敲配置,重载服务之后另一台终端立刻断开连接,那一瞬间心跳直接拉满。这篇内容就是我从那次以及之后多次操作中整理出来的完整记录,包括为什么非改不可、每一步的参数该怎么理解、出问题时怎么在十分钟内把自己救回来。适合刚接手服务器的新手,也适合那些想关密码登录但一直没下定决心的人。
1. 为什么非要把 SSH 改成只认密钥
1.1 密码登录的隐患到底藏在哪
密码登录最大的问题不是密码本身不够复杂,而是它天然具备“可猜测”和“可暴力遍历”两个属性。只要你开了密码认证,任何能访问到 22 端口的人都可以不限制次数地尝试登录。现在公网上扫描 SSH 弱口令的脚本基本是全天候运行的,我自己的服务器在没关密码登录之前,几乎每隔几天就能在日志里看到一批来自陌生地址的认证失败记录。那些脚本用字典、常用密码组合反复尝试,一旦撞上一个弱密码,服务器就相当于裸奔。
另一个容易被忽略的弱点是,密码会通过“输入行为”泄露。假设你平时用一台个人电脑去管理服务器,而电脑上不小心中了键盘记录类的恶意程序,那么不管密码设得多复杂,只要你输过一次,对方就拿到了。密钥认证在这两点上都有明显优势:私钥以文件形式存在,不需要在每次登录时重复输入,而且登录过程中私钥本身永远不会经过网络传输。换句话说,密钥模式从根本上就不存在“被撞库”和“被记录后重放”这两种攻击面。
1.2 密钥认证的原理其实很简单
很多人觉得密钥认证很玄,其实核心原理就是一把锁和一把钥匙的关系。可以把服务端存放的“公钥”理解成一把特殊的锁,这把锁装在你的账户目录里,任何持有对应“私钥”的人才能打开它。你手头那份私钥是唯一的钥匙,登录时服务器会发起一个随机数挑战,客户端用私钥对它进行签名,服务器再用公钥验证签名。验证通过,连接才会建立。
这里面最关键的一点在于:网络上传送的不是私钥本身,而是用私钥生成的一段签名结果。哪怕中间有人抓包拿到了签名,也无法通过它反向推出私钥。公钥放在服务器上完全没有关系,它本来就是公开的,只有私钥必须绝对保密。理解了这一层,你就能明白为什么密钥认证被普遍认为比密码更可靠:密码是“你记得什么”,而密钥是“你拥有什么”,后者被远程猜中的可能性趋近于零。
1.3 只保留密钥登录的收益和代价
关掉密码登录之后最直接的收益,是服务器的认证失败日志会突然安静下来。暴力破解脚本连试探的机会都没有,因为服务端直接拒绝了密码方式。这比装任何入侵检测工具都更省心。其次,团队管理也会变清晰:谁负责哪台机器,就把对应公钥装到哪台机器上,离职时从 authorized_keys 里删掉一行即可,不用像改密码那样反复通知所有人。
当然,代价也很明确。第一次部署要额外花点时间生成密钥、拷贝公钥,私钥一旦丢失,处理起来比“忘记密码”麻烦得多,需要管理员通过控制台等方式介入恢复。但老实说,这个代价是一次性的,而密码登录的风险是每天都在累计的。大多数人觉得麻烦,只是还没把“私钥丢了怎么办”这件事想清楚,后面我会专门讲应急恢复的办法。
2. 动手前需要想清楚的两件事
2.1 密钥算法选什么:RSA 还是 Ed25519
生成密钥之前得先选算法。早期大家习惯用 RSA,它的兼容性最好,几乎所有 SSH 客户端和服务端都认识它,哪怕是很老的操作系统也没问题。通常建议用 4096 位,2048 位也不是不能用,只是安全余量上略逊一筹。但 RSA 的缺点是密钥文件长,验证时计算量相对大,生成的公钥是一大串字符。
现在的 OpenSSH 版本基本都支持一种叫 Ed25519 的算法,密钥长度短、生成速度快、安全性也不输 RSA,而且它使用的是现代椭圆曲线签名,被业界普遍推荐为新项目的首选。我在实际部署中,只要确认服务器上的 OpenSSH 版本不低于某个常见版本,就直接用 Ed25519。唯一要注意的老旧系统场景,比如某台服务器还跑着多年前的旧发行版,客户端也可能是很老的版本,这时候才退回去选 RSA。判断方法很简单:在服务器上执行 ssh -V 看版本,只要不是很古老的版本,Ed25519 基本都能用。
2.2 服务器端的状态检查清单
开始动手之前,我建议先把服务器端的状态摸清楚。先确认你当前登录服务器用的账户是什么,这个账户需要有 sudo 权限,因为修改 sshd_config 和重载 sshd 服务都需要 root 权限。同时检查一下 SSH 服务本身是否正常,执行 systemctl status sshd 或者 service ssh status 即可,不同发行版略有差异。
还要确认你准备配置密钥登录的目标用户目录。通常就是 /home/用户名/.ssh,如果你用 root 账户登录管理,则是 /root/.ssh。不同发行版的默认权限要求略有不同,但核心规则是一致的:目录本身要是 700,authorized_keys 文件要是 600,否则服务端会认为有安全风险而拒绝读取。这个权限问题也是新手最容易踩的坑,后面排查部分我会细说。
2.3 关键配置参数速查
修改 /etc/ssh/sshd_config 之前,先把几个核心参数搞明白,后面操作时心里会更踏实。我把最常用的一组整理成了表格,方便照做:
| 参数 | 建议值 | 作用说明 |
|---|---|---|
| PubkeyAuthentication | yes | 允许使用公钥认证方式登录 |
| PasswordAuthentication | no | 禁止密码认证,核心开关 |
| KbdInteractiveAuthentication | no | 禁止键盘交互式认证,防止绕过 |
| ChallengeResponseAuthentication | no | 老版本参数,作用和上面类似 |
| PermitRootLogin | prohibit-password | 根用户只能用密钥登录,不能用密码 |
| UsePAM | yes | 保持开启,但要配合上面参数理解 |
| StrictModes | yes | 严格检查密钥文件权限,建议保留 |
表格里的 UsePAM 值得单独提醒一句:在某些发行版里,SSH 最终是否允许密码登录是由 PAM 和 sshd_config 共同决定的。即使你写了 PasswordAuthentication no,如果 PAM 配置还在允许密码认证,某些情况下仍然可能放行。所以最好把 KbdInteractiveAuthentication 也关掉,并在修改之后做一次实测,确认密码方式已经彻底失效。
3. 完整实操:从生成密钥到强制密钥登录
3.1 第一步:在客户端生成密钥对
生成密钥的操作是在你自己的电脑上完成的,不是在服务器上。打开终端,执行下面的命令:
ssh-keygen -t ed25519 -C "personal-workstation" -f ~/.ssh/id_ed25519这里 -t 指定算法,-C 是备注信息,通常写这台机器用途,方便以后管理时辨认,-f 指定生成的文件路径。如果不写 -f,默认就是 ~/.ssh/id_ed25519,回车一路确认也能用。
执行过程中会提示你设置 passphrase,也就是私钥保护口令。这里强烈建议设置一个,哪怕只是短一点的口令。它的作用是:即使私钥文件被别人拷走,没有这个口令也无法使用。我见过不少人为了省事不设口令,结果笔记本丢失后私钥直接裸奔,这是很危险的。之后每次使用私钥时可以借助 ssh-agent 缓存口令,不需要反复输入,体验上没有太大损失。
3.2 第二步:把公钥部署到服务器
密钥对生成之后,会得到两个文件:id_ed25519 是私钥,留在本地;id_ed25519.pub 是公钥,要放到服务器上。最简单的方式是使用 ssh-copy-id 命令:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip这条命令会提示你输入服务器密码,验证通过后自动把公钥追加到服务器上对应用户的 authorized_keys 文件里,还会顺手把目录权限设置好,非常省事。如果服务器禁用了密码登录,这条命令没法用,也还有手动的办法:在服务器上编辑 ~/.ssh/authorized_keys,把公钥内容粘贴进去,然后注意设置权限。
更稳妥的手动方式是这样的,用一条命令完成目录创建和权限设置:
mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys然后把公钥内容追加进去:
echo "ssh-ed25519 AAAA... your-comment" >> ~/.ssh/authorized_keys这里要注意是追加而不是覆盖,用两个大于号,防止把已有公钥冲掉。多个人管理的机器尤其要养成这个习惯。
3.3 第三步:修改 sshd_config,但先别急着关密码
公钥部署完成之后,先不要立刻禁用密码登录。我建议分两步走:第一步,先确认密钥登录已经可以正常工作;第二步,再关掉密码认证。顺序搞反了,极有可能把自己锁在外面。
先保持服务器当前配置不变,从你的客户端重新开一个终端,执行:
ssh -i ~/.ssh/id_ed25519 user@server-ip如果不需要输入密码就成功登录,说明密钥认证链路已经通了。我习惯在确认这一步之后再做下一步修改。如果这一步失败,先停下来排查,不要继续关密码登录,不然后面会很难受。
确认密钥可用后,再编辑配置文件:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak sudo vim /etc/ssh/sshd_config先把这几行的值改成下面的样子:
PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no ChallengeResponseAuthentication no改完后先不要重载,再检查一遍有没有语法错误。可以执行:
sudo sshd -t这个命令用于测试配置文件是否正确,如果有参数写错,它会直接报错,而不会影响当前正在运行的 sshd 进程。这一步很关键,相当于给配置加了一道安全阀。
3.4 第四步:重载服务并做完整验证
确认配置无误后,重载服务。不同发行版命令略有差异,常见的是:
sudo systemctl reload sshd或者是 sysvinit 风格:
sudo service sshd reload重载后,重新开一个终端窗口测试密钥登录是否仍然正常。如果正常,再测试密码登录是否已经被拒绝。可以在同一台客户端上执行:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@server-ip正常情况会提示权限拒绝,或者直接提示没有可用认证方法。这一步确认之后再回到原来的终端继续使用,说明切换已经安全完成。我有一次就是因为少测了第二步,后来才发现某个老客户端因为私钥格式问题根本没有走密钥通道,一直在用别的方式登录,等于白改。
3.5 万一连不上了,怎么快速回滚
就算前面每一步都做了验证,仍然可能遇到意外情况,比如配置保存时不小心改坏了别的参数,或者重载后新连接一直握手失败。此时如果你手上还有一个保持连接状态的终端窗口,什么都别想,马上用它把配置恢复回去:
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config sudo systemctl reload sshd这样大概率能救回来。如果所有连接都断了,只能通过服务器控制台登录。大多数云厂商的网页控制台都提供 VNC 或者类似功能,物理机也有带外管理口。用控制台登录后,按照同样的方式恢复配置文件就行。每次配置 SSH 之前我都会单独开一个终端窗口保持连接,并且确认窗口不会因为超时自动断,这算是我多年养成的保命习惯。
4. 常见问题与排查技巧实录
4.1 权限错误导致的“诡异”登录失败
密钥登录最常遇到的报错是 “Permission denied (publickey)”。如果公钥明明已经放进 authorized_keys,密钥算法也没问题,那十有八九是权限设置不对。OpenSSH 在 StrictModes 开启时,会检查密钥文件和目录权限,如果 .ssh 目录对组或其他用户可写,或者 authorized_keys 权限过宽,它会直接拒绝使用这些文件,日志里通常会出现 “Authentication refused: bad ownership or modes” 之类的记录。
解决办法也很直接,在服务器上执行:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys还要确认这两个文件和目录的属主是你当前登录的用户,而不是 root。我之前帮一个同学排查过,他直接把公钥文件用 sudo 写入,导致文件属主变成 root,普通用户根本读不了。执行 chown 用户名:用户名 ~/.ssh/authorized_keys 之后立刻恢复正常。
4.2 SSH 日志都不会看怎么查
出问题的时候,第一反应应该是看日志。主流 Linux 发行版上,SSH 的日志大多记录在 /var/log/auth.log 或 /var/log/secure 里,取决于发行版家族。用 tail 实时跟踪:
sudo tail -f /var/log/auth.log在另一个终端尝试连接服务器,观察日志里出现的内容。如果能看到 “Accepted publickey for xxx”,说明认证已经成功,问题大概率出在会话建立之后;如果看到 “Failed publickey for xxx”,说明服务器不认可你提供的密钥。常见原因包括:仓库了错误的公钥、密钥算法不匹配、权限错误。配合日志排查,比自己瞎猜快得多。
4.3 SELinux 和防火墙可能拦住你
如果你用的系统启用了 SELinux,并且处于强制模式,那么即使文件权限看起来完全正确,SSH 仍然可能拒绝读取 authorized_keys。这时候需要恢复正确的 SELinux 上下文:
sudo restorecon -R -v ~/.ssh这条命令会把 ~/.ssh 目录下的文件属性恢复到默认策略要求的类型。我记得第一次在 SELinux 系统上配密钥时,折腾了半天权限,最后就是一条 restorecon 解决的问题。另外,如果自定义了 SSH 监听端口,还要检查 SELinux 是否允许 sshd 使用该端口:
sudo semanage port -l | grep ssh没有对应规则时,需要手动添加放行策略。防火墙方面,如果你改了非默认端口,记得通过 firewall-cmd 或系统自带防火墙策略放行新端口,这个看似简单,但有很多人在改端口后忘了这一层,导致连接超时。
4.4 多台服务器、多把钥匙时的管理心得
当管理的服务器变多之后,不建议把所有服务器都配上同一把私钥对应的公钥。我的习惯是给不同安全级别的环境分配不同的密钥:办公电脑生成一把,自己个人项目用;生产环境单独用一把,放在加密容器里;偶尔帮朋友维护的机器,再单独生成一把。这样万一某一把私钥泄露了,影响范围是可控的,不需要满世界去改。
日常使用可以使用 SSH 客户端的 config 文件来简化连接。在 ~/.ssh/config 里写清楚每一台主机的别名、地址、用户、对应私钥路径,例如:
Host my-server HostName 203.0.113.10 User ops IdentityFile ~/.ssh/id_ed25519以后直接执行 ssh my-server 就能连上,不用记 IP 和账号。建议把私钥加入 ssh-agent 管理,并设置合适的私钥有效期:
ssh-add -t 8h ~/.ssh/id_ed25519这样私钥只在本地进程内存里存在 8 小时,过期后重新添加,兼顾便利和安全。
5. 密钥登录落地之后的进一步加固
5.1 顺手把 root 直接登录也关掉
既然已经只保留密钥认证,建议顺便把 root 的密码登录彻底禁掉。在 sshd_config 里确认这一项改成:
PermitRootLogin no这样 root 无法通过 SSH 直接登录,只能先用普通用户登录,再通过 sudo su 切换到 root。这样做的好处是,任何审计记录里都能看到“谁先用普通账号登录,然后才提权”,而不是一堆人共用 root 身份登录却分不清是谁。如果确实有特殊场景需要 root 远程密钥登录,可以设成 prohibit-password,严格限制只能密钥认证,不建议使用默认的 yes。
5.2 再加一层访问限制更安心
关闭密码登录只能挡住远程爆破,如果账号密码不是唯一入口,还可以通过限制来源 IP 进一步收敛暴露面。常用的做法是在 sshd_config 里配置 AllowUsers 或 Match Address:
AllowUsers ops@192.0.2.0/24 admin@203.0.113.10这段配置的含义是只允许特定用户从特定网段登录,其他来源一律拒绝。适合办公网 IP 固定或通过跳板机中转的团队场景。如果你不想在 sshd_config 里写死,也可以配合防火墙策略,只对已知办公 IP 开放 SSH 端口,效果类似。
另外一个推荐组合是 fail2ban。它是通过监控认证失败日志,在短时间内出现多次失败时自动封禁来源 IP。即使你已经关闭了密码登录,它依旧能帮你抵御一些针对 SSH 服务的非认证类探测,比如端口扫描和畸形包攻击。
5.3 私钥不是随便放的文件
最后这一点必须反复强调:私钥文件一旦泄露,和你把服务器密码贴在桌面上没什么两样。不要把它上传到网盘、笔记软件或者聊天工具里。我见过有人为了方便,把私钥放到在线笔记里,后来笔记账号被盗,名下所有服务器都面临风险。也没必要把同一把私钥复制到手机、平板、家里台式机、公司笔记本等所有设备上。设备越多,泄露面越大。
如果有多台设备需要登录同一批服务器,更合理的做法是让每台设备各自生成一对密钥,把各自的公钥分别加入服务器,而不是共享一份私钥。这样日后关停某台设备时,只需在服务器 authorized_keys 里删除对应公钥即可,不影响其他设备。
私钥的备份也应该放入加密容器,而不是明文存放。可以考虑用加密压缩包放一份私钥在安全位置,万一本地文件损坏还有恢复机会。至于是否需要硬件密钥,如果团队安全要求高,可以考虑支持 SSH 的硬件令牌,把私钥写入令牌芯片,登录时需要插卡或触碰,安全性又高一档。对个人服务器和一般团队来说,软件密钥加上强口令保护已经基本够用。
配置 SSH 服务仅支持密钥登录这件事,第一次做确实有点紧张,但只要按“先部署公钥、再验证密钥、最后关密码”的顺序来,每一步都有回退余地,风险就能控制住。我现在每新接手一台服务器,第一件事就是把这套流程走一遍。习惯了之后再回头看,所谓的安全焦虑大多来自“不知道配置错了会怎样”,而当你知道怎么回滚、怎么通过控制台自救之后,关掉密码登录就只是一个正经运维的常规操作而已。