☰
Linux SSH密钥免密登录配置详解:从原理到安全加固
2026/10/14 4:46:48 网站建设 项目流程

在Linux服务器上干活的人,应该都有这种经历:一台新机器,ssh root@192.168.1.101,回车,然后开始输密码。如果只是几台机器还好,一旦上了规模,运维手里十几台、几十台服务器,每次登录都输一遍密码,一是慢,二是烦,三是总有记错密码的尴尬时刻。后来我开始全面切换到SSH密钥免密登录,这个问题才真正解决。这篇内容就是要把我这几年的配置经验完整梳理一遍——从密钥认证的原理,到生成密钥、部署公钥、验证登录,再到常见的坑位排查和安全加固,一条线走下来。适合刚接触Linux的开发者,也适合想把自己服务器登录体系整理清楚的老手。

密钥免密登录这件事,名字里带"免密",但本质上并不是把密码去掉了,而是把"密码验证"换成了"密钥验证"。密码变成了你手里那把私钥,你带着私钥,服务器认的是你。理解这个底层逻辑之后,后面所有配置就都顺了。

1. 为什么选择密钥认证 —— 免密登录背后的设计逻辑

1.1 密码登录的两个死穴

先说个小前提:SSH默认的密码登录,在真实生产环境里其实很脆弱。第一是爆破风险。只要你的IP暴露在公网上,sshd的日志里几乎天天能看到来自各种扫描器的暴力尝试。那些脚本会不断尝试root/123456、admin/admin之类的弱口令组合,如果你的密码稍微简单一点,被撞开只是时间问题。

第二是密码本身的传输问题。SSH协议在传输密码时是加密的,这一点倒不用担心明文被抓包,但问题是:密码这个东西,只要是"人脑记得住"的,就大概率不够复杂;只要是人会输入的,就可能被肩窥、被记录、被复用。公司里某台服务器密码在东一个文档、西一个表格里传来传去的情况,太常见了。

密钥认证恰好绕开了这两个死穴:私钥不出本机,不存在"在网络上传输密码"这回事;密钥的长度和随机性也不是人类能随口输入的密码能比的。所以,用密钥免密登录不只是为了省事,本质上是在做安全升级。

1.2 密钥对的工作原理快速扫盲

SSH密钥认证用的是非对称加密。你手上会生成两个文件:一个是私钥(默认叫id_ed25519),留在自己机器上,权限600,绝不能泄露;另一个是公钥(默认叫id_ed25519.pub),可以公开,放到你要登录的服务器上。

登录过程大致是这样:客户端发起连接,服务器给出一个用公钥加密的挑战,客户端用私钥回应,服务器验证通过后放行。整个过程私钥不会离开客户端,公钥本身也不怕被人看到。换句话说,公钥相当于一把锁,私钥是钥匙,你把锁装到了服务器上,钥匙自己揣好。

生活一点理解就是,密码登录像你每次进门都喊一句暗号,时间长了暗号总会被听走;密钥登录是进门掏门禁卡,卡里的信息是动态的、不可复制的,而且卡不离手。

2. 动手前需要准备的环境与工具

2.1 检查客户端和服务端的OpenSSH版本

配置密钥登录之前,先看看两边环境。客户端无论是Windows的OpenSSH客户端、macOS自带的Terminal,还是Linux发行版里的终端,基本都是OpenSSH这套东西。服务端就是目标服务器的sshd。

用下面两条命令确定版本:

# 客户端版本 ssh -V # 服务端版本 sshd -V

只要版本不是特别古董(比如10年前的OpenSSH 5.x),配置密钥登录都没问题。我现在主力用的是Ed25519算法,这是OpenSSH 6.5以后才支持的,如果你手上还有比较老的系统,可能需要退一步用RSA。

2.2 需要准备的工具清单

其实核心工具就三样:生成密钥的ssh-keygen、拷贝公钥的ssh-copy-id,以及测试连接用的ssh。这些都是OpenSSH自带的,不需要额外安装。

个别情况下可能没有ssh-copy-id这条命令(比如在极简安装的容器里),那就用scp或者手动追加公钥的方式,后面会详细说。还有一个要提前确认的:目标服务器的sshd_config文件里,PubkeyAuthentication没有被设置成no,否则后面一切白搭。

# 查看服务端是否开启公钥认证 grep -E "^PubkeyAuthentication|^PasswordAuthentication" /etc/ssh/sshd_config

我遇到过一次,某公司的一批云服务器在装机镜像里干脆把PubkeyAuthentication no写死在配置里,导致所有客户端生成了密钥也登不上去。排查了半天,最后发现是镜像模板的问题。

3. 免密登录全流程实操:生成、部署与验证

3.1 在客户端生成密钥对

这一步在任何一台客户端机器上做一次就够了。打开终端,输入:

ssh-keygen -t ed25519 -C "your-email-or-comment"

拆解一下参数:

  • -t ed25519指定算法。现在主流推荐Ed25519,安全性足够,密钥短,性能好,生成出来是一把80多字节的公钥。如果你的服务器OpenSSH版本太老,可以改用-t rsa -b 4096。
  • -C "备注"是给密钥加一行注释。一般写你自己的邮箱或者用户名,方便以后管理多个密钥时知道哪把是哪把,不影响认证逻辑。
  • 然后会问你要保存到哪个文件,默认是~/.ssh/id_ed25519,直接回车。
  • 还会问你是否设置passphrase(私钥口令),这个后面细讲,第一次配置建议先留空。

生成完之后,检查一下:

ls -l ~/.ssh/

正常情况下应该有id_ed25519和id_ed25519.pub两个文件。私钥权限必须是600,如果你发现私钥权限是644,后面连接时会直接被拒绝。

顺便说一句,有些教程会让你加-b 4096表示长度。这个参数只对RSA有意义,对Ed25519没用,因为Ed25519的长度是固定的。这也是为什么不推荐继续用RSA——明明参数多,还容易整出些概念混淆。

3.2 把公钥部署到目标服务器

公钥要放到服务器上用户的~/.ssh/authorized_keys文件里。最简单的命令:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ip

这条命令会要求你输入一次密码,然后把公钥追加到服务器的authorized_keys里。如果你一共要管50台机器,那就对每台机器执行一次。

有些环境没有ssh-copy-id,比如服务器是精简版系统。没关系,手工补:

cat ~/.ssh/id_ed25519.pub | ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

这一条命令干了四件事:在服务器上建.ssh目录、把目录权限改成700、把公钥追加进authorized_keys、把文件权限改成600。权限这步非常关键,后面排查部分会重点讲。

3.3 验证免密登录是否生效

公钥部署完,退出服务器,重新连接:

ssh user@server_ip

如果配置成功,直接进入,不会再要密码。如果还提示输入密码,先别慌,唯一要确认的是:当前客户端是否真的在用你生成的那把私钥。加一个-v参数看调试信息:

ssh -v user@server_ip

输出里会打印出连接过程中尝试的密钥路径。看有没有这样一行:Offering public key: /home/you/.ssh/id_ed25519。如果有,说明客户端在正常"出示"钥匙,问题出在服务端没能验证通过;如果没有,可能客户端默认没启用这把密钥。

有时候你会遇到一个情况:登录时依然要密码,但是输入密码后进去了。这种多半是公钥认证失败后SSH自动回退到密码认证。真正的免密登录,应该是从头到尾不出现password:提示的。

3.4 多密钥并存时怎么指定私钥

如果客户端机器上有多把密钥(比如公司一台、个人一台、某项目一台),SSH默认会按顺序尝试~/.ssh/下的所有私钥,顺序不确定,可能试错好几把才轮到你想要的那把。推荐的做法是给每台服务器在~/.ssh/config里写一段明细:

Host web-prod HostName 192.168.1.10 User root IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes

关键在最后两行。IdentityFile指定用哪把密钥,IdentitiesOnly yes告诉SSH别乱试其他密钥,只认你指定的这把。加了这两个配置之后,登录就是ssh web-prod,主机名都省得记住了。

4. 参数细节与常见坑位排查实录

4.1 权限不对导致的Permission denied

配置密钥登录时遇到最多的报错就是:

Permission denied (publickey,password)

十有八九是服务端.ssh目录或者authorized_keys文件的权限太宽松。SSH为了保证安全,如果发现这两个位置的权限是"其他人也可写"的,就会直接忽略这个公钥文件。这是OpenSSH的默认安全策略,不是玄学。

正确权限如下:

  • 用户家目录不能是777,一般755或700都行;
  • ~/.ssh目录权限700;
  • ~/.ssh/authorized_keys权限600。

如果不放心,直接在服务器执行:

chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys

顺便说个我踩过的具体坑:某次我把authorized_keys权限设成了644,因为想着"公钥本来就是公开的,权限放宽点没影响",结果连着三次被Permission denied。后来才意识到,公钥内容公开没问题,但authorized_keys这个文件本身决定了谁能登录,OpenSSH对它的权限检查完全不看你放的内容是什么。这就跟你家门口挂了块"本户主在此"的牌子,门锁密码还是写在牌子背面一样离谱。

4.2 SELinux导致公钥写入无效

另一类挺隐蔽的问题是SELinux。在一些使用SELinux的发行版上(比如CentOS、RHEL系列),即使权限完全正确、authorized_keys文件里也有公钥,但SSH服务端依然会拒绝。

这个问题的根源是SELinux的安全上下文。处理方式一般是看看SELinux是否开启:

getenforce

如果输出是Enforcing,而且日志里能看到sshd相关的AVC拒绝记录,那就需要给SSH放行相应规则。比较省事的方式是:

restorecon -R -v /root/.ssh

如果执行完restorecon还不行,再用ausearch -m avc查看具体拒绝的项目,按提示处理。SELinux这东西平时容易被人忽略,但它确实曾经耽误过我半个下午。排查免密登录问题时,权限检查完没结果,就顺手查一下SELinux,能省很多时间。

4.3 换主机或换用户名之后密钥失效

可能遇到这样的问题:同一把密钥在一台服务器上能免密登录,到了另一台服务器上就要密码。这太正常了,因为密钥认证认的是"服务器用户"与"公钥"的对应关系——你并没有把公钥放到那台新服务器的authorized_keys文件里。对每一台要登录的服务器,都需要单独执行一次ssh-copy-id。

还有种情况是换用户名。同一台服务器,你用root登录可以,换成普通用户ops登录就要求密码。原因是公钥分别存在不同用户的home目录下,root的authorized_keys有这把公钥,不代表ops用户也有。

处理方式依然很简单:切到目标用户,重新部署公钥:

su - ops ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@server_ip

4.4 Windows客户端的一些细节

用Windows自带的OpenSSH或者Windows Terminal连Linux服务器,整体流程和Linux/macOS客户端是差不多的。密钥生成后用同一个方法部署公钥即可。需要注意的坑是:ssh-copy-id默认在Windows下还没有(较新的Windows OpenSSH版本可能已经有PowerShell方案),如果你没有装Git Bash或WSL,可以直接用type命令读公钥,再手动粘贴到服务器的authorized_keys文件里。

Windows端还有个常见的毛病是私钥路径写错。有些人把密钥下载到了C:\Users\admin\Downloads\id_ed25519就开始连,SSH默认只在C:\Users\admin\.ssh\下找私钥,找不到自然会要求输密码。把密钥挪到.ssh目录下,问题就没了。

4.5 常见问题速查表

现象最常见原因快速处理
连接要求输密码,且密码能登进去公钥没生效或服务端未开启公钥认证检查sshd_config的PubkeyAuthentication,重新部署公钥
提示Permission denied (publickey,password)权限问题或公钥不对chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
明明公钥写入成功却仍要密码SELinux或密钥不在客户端默认路径执行restorecon -R -v ~/.ssh,检查客户端私钥位置
多密钥机器上老用错密钥SSH默认尝试顺序不确定在~/.ssh/config配IdentityFile和IdentitiesOnly
更换服务器后免密失效公钥未部署到新服务器的目标用户对新服务器执行ssh-copy-id
Windows下找不到密钥私钥不在.ssh目录把私钥移动到C:\Users\用户名\.ssh\

5. 安全加固:让免密登录真正"稳"起来

5.1 确认无误后禁用密码登录

免密登录配置好之后,下一步就该考虑是否彻底关闭密码登录了。这一步是安全性的分水岭:只要密码还能用,攻击者就还有机会爆破;把密码登录关掉,只剩密钥一条路,爆破直接变得没有意义。

在服务端编辑/etc/ssh/sshd_config,找到:

PasswordAuthentication yes

改成:

PasswordAuthentication no

改完后重启SSH服务:

systemctl restart sshd # 或者 systemctl restart ssh

重启之前一定要仔细确认:你的免密登录已经测通了,且root用户也能通过密钥登录(如果你平时用root登录的话)。万一没测好就关掉密码认证,你可能会把自己锁在服务器外面。强烈建议在关闭密码登录之前,多开一个SSH会话挂在那里,出了问题还能从那个已有会话里救回来。

5.2 root直登与普通用户切换的生产建议

很多云服务器默认就拿root登录。如果为了管理方便,也可以保留root的密钥登录,但更好的生产实践是把高风险操作收敛到普通用户加sudo。具体做法是:创建普通用户,把公钥部署到该用户,然后用这个用户日常操作,需要提权的时候再用sudo。

你的公钥文件会同时存在于本地的~/.ssh/和服务器多个用户的~/.ssh/authorized_keys中,这一点不影响使用。只是提醒一句:公钥只是"钥匙",能开哪扇门取决于你把钥匙配给了哪个用户的门。生产环境里最小化授权,给每个用户配对应权限的钥匙,是更稳妥的做法。

5.3 私钥保护:给密钥加passphrase

前面生成密钥时留空的passphrase,其实不建议长期留空。私钥文件虽然权限是600,但如果你本机被种了木马,或者笔记本被拿走,私钥就直接丢了。给私钥加一个passphrase后,每次使用还是要输一次口令。这样私钥泄露不等于立即风险,泄露的只是一个加了锁的文件。

有人会觉得加passphrase和加密码没区别,反正都要输密码。其实不然:passphrase只在本地解锁私钥,不会在网络上传;密码登录是把密码发给服务器验证,两者安全模型完全不同。而且日常用ssh-agent可以把解锁后的私钥加载到内存里,只要agent不退出,后续连接都不用再输口令,便利性也没有太大损失。

加载私钥到agent的方法:

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

如果你给私钥设置过passphrase,这一步会让你输入一次。之后在当前会话内再连其他服务器,SSH会自动通过agent提供身份,不会反复问口令。macOS还可以在~/.ssh/config里加一行AddKeysToAgent yes,让系统每次解锁过的私钥自动写入agent,减少重复输入的时间。

5.4 限制来源IP和fail2ban组合

如果bash脚本扫描太多,还有一个比较硬气的办法:在服务器的防火墙里限制SSH来源IP,只允许你自己的办公网段访问22端口。比如用iptables/nftables/firewalld放行特定IP,拒绝其他所有来源。这招比fail2ban更直接——不在黑名单里匹配,而是直接白名单。

如果你需要从多个不固定位置登录,那fail2ban依旧值得上。它监控SSH日志,发现多次认证失败就临时封禁来源IP,规则由你定。和密钥认证搭配使用,服务器被爆破的窗口期会进一步缩小。

禁用密码登录和限制来源IP,这两件事做完之后,SSH的安全基线基本上就立住了。

6. 多服务器批量部署的高级操作

6.1 批量分发公钥的脚本思路

如果你接手了一批服务器(比如上线一个微服务集群,一次性给你8台节点),一台台ssh-copy-id的体验会很酸爽。可以写一个简单的循环脚本:

#!/bin/bash PUBKEY="$HOME/.ssh/id_ed25519.pub" USER="deploy" PASS="初始统一密码" for HOST in 192.168.1.11 192.168.1.12 192.168.1.13; do sshpass -p "$PASS" ssh-copy-id -i "$PUBKEY" -o StrictHostKeyChecking=no "$USER@$HOST" done

这里涉及两个工具和参数:sshpass用来在脚本里自动输入密码;StrictHostKeyChecking=no跳过首次连接时的host key确认。必须说明:sshpass只建议在一次性初始化脚本里用,密码会暴露在命令行中,不推荐长期保留在常用脚本里。批量部署完成后,马上把这些服务器的密码登录关掉,同时把初始密码改掉,不给它留下长期风险。

如果不想安装sshpass,也可以用expect或者Ansible的authorized_key模块。我个人在几十台机器规模以下更喜欢直接循环跑ssh-copy-id,简单直接;再往上走就上Ansible了。

6.2 跳板机和堡垒机场景的密钥穿透

带跳板机的场景稍微复杂点:你需要先从本机登录到跳板机,再从跳板机登录到目标服务器。如果每台目标服务器都配置密钥,那就得把公钥同时放到跳板机和目标机。

一个更顺滑的做法是使用SSH代理转发。在本地~/.ssh/config里写:

Host bastion HostName 10.0.0.1 User ops IdentityFile ~/.ssh/id_ed25519 Host target-1 HostName 10.0.0.21 User ops ProxyJump bastion IdentityFile ~/.ssh/id_ed25519

ProxyJump会先连跳板机,再用跳板机作为中转去连接目标机。这个方式的妙处是:中间经过的机器完全不接触你的私钥,私钥始终待在本地。跟那种把私钥复制到跳板机的做法相比,这个显然安全得多。唯一的点是:目标机必须信任你的公钥,跳板机也要信任你的公钥,两边都配好之后,从本地一条命令直达目标,体验非常顺滑。

我在实际项目里常这样用:本地配好config,平时操作内网服务器就跟操作本机一样,且私钥从不落到任何一台中间机器里。这一点对安全审计来说也是很加分的。

7. 一些个人的配置习惯与建议

现在我手上的每台Linux服务器,SSH层面基本都是同一套基线:密钥登录已经是常态,密码登录默认关闭,普通用户日常操作,提权走sudo,公钥常年存在~/.ssh/下并做好命名区分。新服务器到手,第一步就是把公钥部署上去、确认免密登录、关掉密码认证。这套流程走熟了之后,配一台新机器总共要不了两分钟。

另外有个习惯想分享:私钥文件一定要做好备份。以前总觉得私钥不导出、不带出,丢就丢了重新生成一把。真到了几十台服务器都配了同一把公钥的时候,私钥丢了就真的傻眼了。我现在的做法是生成密钥当天就把私钥做一次加密备份,放在一个离线加密U盘里,以后新增服务器也都用这一把主钥分发。

还有个小技巧:authorized_keys文件里每条公钥前面可以加上一段注释,标识这把钥匙属于谁。比如:

ssh-ed25519 AAAAC3NzaC1l... your-name@work-laptop

以后想收回某个人的访问权限,直接定位到对应行删掉即可,不用满文件猜测哪行是谁的。这个习惯在多人协作的场景里特别有用。

给passphrase、做备份、加注释,这几件事做完,密钥免密登录才算真正形成了闭环。你有了"免密"的高效,也有了密钥丢失时挽回局面的一条后路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询