☰
SSH密钥认证配置指南:从原理到实战,彻底告别密码登录
2026/10/17 3:45:34 网站建设 项目流程

1. 从密码到密钥:为什么我建议你尽快切换SSH登录方式

SSH协议(Secure Shell)是几乎所有开发者连接Linux服务器、网络设备、云主机时绕不开的一道门。无论是部署应用、排查日志、修改配置,还是传输文件,绝大多数远程操作都建立在SSH这条安全通道之上。默认情况下,很多服务器仍然使用“用户名+密码”的方式认证登录,但这种方式在实际生产环境里越来越让人不放心:密码容易被暴力破解,撞库风险高,团队内共享密码时又无法追究具体是谁操作过,一旦密码泄露,整个服务器等于直接暴露。

密钥认证登录则完全不同。它基于一对公私钥实现身份验证,私钥保存在你本机,公钥部署到服务器上。登录时服务器用公钥验证持有私钥的客户端,整个过程不需要在网络中传输密码。安全性上,密钥长度通常远大于普通密码,暴力破解几乎不可行;管理上,你可以为每台电脑、每个成员单独生成密钥对,回收权限时只需从服务器上删除对应公钥,不用改密码,也不用影响其他人。对于需要频繁操作多台服务器的开发者来说,密钥认证还支持免密登录、脚本自动化、批量分发配置,体验比密码登录高一个档次。

这篇内容适合谁看?如果你是刚接触服务器运维的开发者,或者长期用密码登录但一直没搞清楚密钥认证怎么配置、怎么避坑的从业者,这篇文章会把从生成密钥对、部署公钥、修改服务端配置到最终验证登录的完整流程拆开讲透,顺带把我踩过的坑和排查思路一并整理给你。读完之后你可以直接照着操作,把常用的几台服务器全部切换到密钥认证。

有些人觉得SSH密钥配置有点门槛,不想折腾。但其实整套流程的核心就是三条命令加一个配置文件,半小时内可以完成。真正难的不是动手,而是理解每一步为什么这么做。理解了“为什么”,以后遇到Permission denied这类报错你才不会慌,也知道该往哪个方向排查。

2. 密钥认证的核心原理与准备工作

2.1 非对称加密在SSH登录中是怎么工作的

密钥认证依赖的是非对称加密体系,简单说就是一对密钥,一把公开的公钥和一把私有的私钥。公钥可以随处分发,私钥必须严密保管。它工作的逻辑可以用一个日常场景类比:公钥是一把锁,私钥是唯一能打开这把锁的钥匙。你把这把锁安装在服务器上,之后任何人想进门,必须出示匹配的钥匙才能开锁。

具体到SSH登录过程,流程是这样的:客户端发起连接请求时,服务器会发送一个用公钥加密的随机挑战数据,客户端拿到后用私钥解密并返回结果;服务器验证返回结果正确后,确认对方确实持有匹配的私钥,于是允许登录。整个过程里密码没有出现在网络上,私钥也没有离开过客户端,所以即使网络流量被截获,攻击者也无法从中还原出私钥信息。

这里有个细节容易被新手忽略:服务器上的公钥并不需要保密,哪怕泄露出去也无所谓,因为公钥只能用来验证私钥签名,不能反推出私钥。你真正需要保护的是本机上的私钥文件。很多人喜欢把私钥文件随便复制到U盘或者网盘同步,这是相当危险的操作。私钥一旦泄露,攻击者拿着它可以直接登录所有部署了对应公钥的服务器。

2.2 OpenSSH、密钥类型与工具链选型

绝大多数Linux发行版和macOS系统都自带OpenSSH套件,这也是目前最主流的SSH实现。Windows系统的话,从Win10 1809版本开始也内置了OpenSSH客户端,直接可以在PowerShell或CMD里使用ssh命令,不需要额外安装第三方工具。如果你还在用老的PuTTY,其实可以换掉了,原生的OpenSSH配合PowerShell能更自然地融入日常工作流,密钥管理和Agent转发也更顺手。

生成密钥对时,需要考虑密钥算法类型。目前推荐优先使用Ed25519,它的密钥短、速度快、安全性高,OpenSSH 6.5以上版本都支持。RSA也可以,但2048位以下的RSA密钥现在不再建议使用,如果服务器要求RSA,至少要生成4096位。还有ECDSA,虽然速度快,但在某些场景下对随机数质量敏感,实际用得相对少一些。日常如果没特殊要求,直接选Ed25519即可。

工具链方面,你大概率还需要用到ssh-copy-id这个命令来快速把公钥部署到服务器。这个命令在大多数Linux发行版里都包含在openssh-client配套工具中,macOS自带,Windows的OpenSSH里也有。它最大的作用是自动把本地公钥追加到服务器authorized_keys文件中,省去手动操作权限设置的麻烦。至于远程服务器上的配置,则要改/etc/ssh/sshd_config这个文件,修改后需要重启ssh服务或执行reload操作使配置生效。

3. 一步步配置密钥认证:从生成到登录

3.1 本地生成密钥对:参数选择与注意事项

首先在本地电脑上打开终端(macOS/Linux用户直接开Terminal,Windows用户打开PowerShell),执行生成密钥对命令:

ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519

参数说明:-t指定密钥类型为ed25519;-C是注释,通常写自己的邮箱或标识符,方便在服务器上区分这把公钥是谁的;-f指定保存路径,默认就是~/.ssh/id_ed25519。如果不带-f,命令会交互式询问路径,直接回车用默认路径即可。

执行后系统会提示输入passphrase,也就是给私钥加的一道口令。这一步很多人直接跳过,我建议你设置一个。passphrase的作用是:即使私钥文件被人拷走,没有这道口令也无法使用它。不设置的话,拿到私钥文件就等于拿到了所有对应服务器的登录权限。关于passphrase和登录效率的平衡,下面单独展开讲。

生成的成果是两个文件:id_ed25519是私钥,权限应为600;id_ed25519.pub是公钥,权限应为644。如果你是在Windows上生成的,完成记得检查下文件权限,避免私钥对其他用户可见。

3.2 公钥部署到服务器:ssh-copy-id与手动两种方式

密钥生成好之后,接下来把公钥部署到目标服务器上。最简单的方式是使用ssh-copy-id:

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

执行这条命令时会提示输入一次现有密码。它会自动连接服务器,在服务器的~/.ssh目录下创建authorized_keys文件(如果不存在),并把公钥追加进去,同时设置好权限。整个过程不用手动干预,非常方便。

如果你的环境暂时没有ssh-copy-id命令,也可以手动完成。手动方式分三步:先远程登录服务器,然后执行:

mkdir -p ~/.ssh chmod 700 ~/.ssh echo "这里粘贴你的公钥内容" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys

这里每个权限设置都不是可有可无的。~/.ssh目录必须是700,authorized_keys文件必须是600,这是OpenSSH的安全校验要求。权限过宽(比如authorized_keys是644或目录是755)时,服务器为了安全会直接拒绝使用该公钥认证,这个坑在初学者里极其常见。

部署完成后,在服务器上用cat命令检查一下公钥内容确实追加进去了,确认无误。

3.3 用密钥首次登录并验证配置生效

公钥部署完成后,先不要急着关掉当前登录窗口,而是新开一个终端测试密钥登录:

ssh user@server_ip

如果一切正常,你会直接登录进服务器,不需要输入密码(如果你设置了passphrase,只需要输入一次passphrase,这不是服务器密码)。看到登录成功那一刻,说明整个链路已经打通。

这里我强烈建议:先保持当前已登录的会话不要关闭,因为一旦新会话登录失败,你还有旧会话可以排查修复。很多人配置完就立刻退出,结果新登录不上了,服务器也进不去了,只能跑到机房或控制台去救,特别被动。

登录成功之后,再验证一下密钥认证是否真的在生效,可以执行:

ssh -v user@server_ip

输出信息里会看到类似Using username "user"、Authenticated with partial success以及publickey的字样,说明这次登录确实走的是公钥认证方式。如果看到Next authentication method: password,说明密钥认证没被接受,后面会讲排查方法。

3.4 可选优化:配置~/.ssh/config简化连接

当你需要管理的服务器不止一台时,每次输入完整用户名和地址会有点烦。可以在本机的~/.ssh/config文件里为每台服务器定义一个简短别名:

Host myserver HostName 192.168.1.10 User root IdentityFile ~/.ssh/id_ed25519 Port 22

配置之后,直接输入ssh myserver就能登录,不用再记忆IP和用户名。如果有相同配置的多台服务器,还可以用通配符统一配置,比如某个网段下的多台机器都用同一把密钥连接,能少写很多重复项。

4. 服务端安全加固:切换密钥认证后必须做的几件事

4.1 关停密码登录的正确姿势

密钥认证验证通过后,下一步就是把服务端配置改成只允许密钥登录。编辑服务器上的/etc/ssh/sshd_config文件,找到以下几个关键项并修改:

PasswordAuthentication no PubkeyAuthentication yes ChallengeResponseAuthentication no UsePAM no PermitRootLogin prohibit-password

PasswordAuthentication no表示禁用密码登录;PubkeyAuthentication yes明确启用公钥认证;ChallengeResponseAuthentication no是关闭额外的挑战响应认证方式,否则部分系统仍可能走密码路径;UsePAM涉及PAM模块,在Debian系系统中如果PAM还在,密码认证可能绕回来,建议一并关掉;PermitRootLogin prohibit-password表示root用户只允许密钥登录,禁止直接用密码登录root。

修改完成后,注意要重启服务而不是只重载配置:

sudo systemctl restart sshd

有些教程会告诉你用systemctl reload sshd,但reload只是读取配置文件,对某些参数(比如UsePAM)并不会真正生效。稳妥起见,直接restart。重启后旧的已建立SSH连接不会断开,但新的连接会按新配置走。

重启完成后,再用之前的密钥登录验证一次,确认还能正常登录。然后你可以在当前会话中尝试一下密码登录是否真的被拒绝了。如果被拒,说明配置生效了。

我遇到过一种情况:在Ubuntu系统上,明明改了PasswordAuthentication no,但密码登录还是能用。查了一圈发现是/etc/ssh/sshd_config.d/目录下有其他配置文件覆盖了主配置,比如50-cloud-init.conf。OpenSSH读取配置时,最后加载的配置会覆盖之前的同名配置项。这类子配置目录的优先级问题很容易让人抓狂,排查时记得留意。

4.2 配置了passphrase还要担心什么

前面提到建议给私钥设置passphrase,但设置了passphrase后,每次登录都要输入一次,有点影响效率。解决方案是使用ssh-agent。ssh-agent是一个常驻内存的密钥管理工具,启动后把私钥添加进去,后续登录会自动用agent里的密钥完成认证,不用反复输入passphrase。

使用方法:

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

执行后会提示输入一次passphrase,之后当前会话内所有SSH连接都免密了。如果是macOS用户,还可以在~/.ssh/config里添加UseKeychain yes,让系统钥匙串帮你记住passphrase,连第一次输入都省了,顺手体验很好。

不过也要记住:ssh-agent是内存中的服务,机器重启后需要重新添加密钥,这是它的安全设计,不是麻烦。另外在共享电脑上使用ssh-agent,最后离开前可以执行ssh-add -D清空所有已加载的密钥,避免后一个用户直接复用你的身份登录服务器。

5. 踩坑实录:Permission denied与连接超时排查清单

5.1 Permission denied (publickey) 的四个高频原因

密钥认证登录时报Permission denied (publickey),是最常见也最让人头疼的错误。按我的经验,99%的情况逃不出下面这四个原因。

第一是公钥根本没有追加正确。很多人手动复制公钥时,把公钥内容换行拆开了,或者多了一个看不见的换行符,导致服务器无法正确解析那行公钥。用ssh-copy-id自动部署能避免绝大多数这类问题。如果必须手动操作,复制粘贴时留意不要在内容和“ssh-ed25519 AAAA...”之间插入多余的空格或字符。

第二是服务器上的authorized_keys文件权限不对。这个前面讲过,文件需要是600、目录需要是700。权限过宽时OpenSSH会直接拒绝使用密钥认证,安全性和严格性相绑定,它不是故意找茬,是防其他用户篡改公钥文件。

第三是指定的IdentityFile不对。如果你在命令里用了ssh -i指定密钥文件,但指定的路径不是服务器上公钥对应的那把私钥,认证当然失败。默认情况下ssh会用~/.ssh/id_ed25519、id_rsa等文件,如果私钥路径不在默认列表里,就需要用-i参数或config里的IdentityFile明确指定。

第四是SELinux或AppArmor拦截。在CentOS、Rocky这类系统上,如果开启了SELinux,公钥相关文件被标记了错误的上下文,sshd可能被禁止读取用户home目录下的authorized_keys。常见的解决办法是检查SELinux状态,用restorecon恢复上下文,或者对特定用户设置正确的布尔值。Ubuntu上AppArmor一般对sshd没有这么强的限制,但也值得作为备选排查方向。

这四种原因中,权限类和公钥内容类占了大多数。我自己的排查习惯是:先看/var/log/auth.log(Debian系)或/var/log/secure(RHEL系)里sshd的报错信息,它会直接告诉你原因,比盲目试各种命令高效得多。

5.2 连接超时与Connection refused排查

除了认证失败,还有一个高频问题是连接超时。这类问题通常跟认证本身没有关系,而是网络路径或服务状态出了问题。

Connection refused通常是sshd服务没有启动,或监听端口不对。在服务器上执行systemctl status sshd查看服务状态,再用ss -tlnp | grep 22确认端口监听情况。如果服务没启动,start起来即可。

连接超时(Operation timed out)则多半是防火墙拦截,或者IP地址本身不通。先ping一下服务器地址,能通说明网络层没问题;再用telnet或nc测试22端口是否可达。安全组、iptables规则、云平台防火墙策略都在排查范围内。多数云服务器都需要在安全组里显式放行22端口,这个配置很多人会忘。

提醒一个实际场景:如果你改了sshd_config里的监听端口,比如把22改成22022,那么除了改配置文件,还要同步放行防火墙端口和安全组,否则服务虽然在监听,但外部流量根本进不来。

5.3 多人协作时公钥管理的细节经验

团队协作场景下,每个人的公钥都追加到同一台服务器的authorized_keys文件里,文件会越来越长,管理和回收权限都变得不方便。我的做法是:在authorized_keys中使用带注释的规范格式,每条公钥前加一行注释,标明使用者、用途、加入日期。这样后续排查时能快速定位是哪把公钥登录了服务器,也能在成员离开时精准删除对应的公钥行。

更规范的是使用配置管理工具(如Ansible)统一推送公钥,把公钥列表放在一份配置里,服务器变更时自动同步。这样不需要登录每台服务器手动改文件,也不容易出现某些机器公钥遗漏或过期的问题。

6. 进阶玩法:密钥代理、跳板机与会话复用的实践心得

6.1 跳板机场景下的密钥转发配置

实际工作中,很多服务器的网络拓扑是:你本机只能访问跳板机,跳板机才能访问内网的其他服务器。如果要用密钥认证直接登录内网目标机,需要在本地启动Agent转发。

在本地~/.ssh/config里这样配置:

Host bastion HostName 10.0.0.1 User jumpuser ForwardAgent yes

登录跳板机后,你本机的ssh-agent会通过跳板机转发,使得跳板机上能够使用你本机的私钥去继续认证内网机器。整个过程中私钥不落地到跳板机,安全性比把私钥拷贝到跳板机上强得多。

有个坑要特别提醒:ForwardAgent yes开启后,如果跳板机被攻破,攻击者可以在你连接期间利用你的agent去登录其他机器。所以对待跳板机要像对待自己电脑一样谨慎,不要在有敏感数据的内网环境里省略验证就直接转发Agent。

6.2 会话复用:让多次SSH连接快得多

每次新开SSH连接都需要经过TCP握手和密钥协商,重复登录多台机器时体感会比较慢。OpenSSH支持连接复用,通过ControlMaster参数实现多条连接共用一条底层TCP链路。

本地配置:

Host * ControlMaster auto ControlPath ~/.ssh/controlmasters/%r@%h:%p ControlPersist 600

ControlMaster auto表示连接同一主机时,如果已有master连接,就复用;ControlPersist 600表示master连接在最后一个会话关闭后保留600秒,期间再次连接无需重新握手。实际体验是第二次及以后的连接速度接近瞬时,对频繁在多个服务器之间切换操作的场景帮助很大。

使用这个配置时,目标机上的~/.ssh/controlmasters目录需要预先创建。如果遇到连接卡住或无法建立的情况,可以清理controlmasters目录下的socket文件后重试,老手也常被这个小问题绊一下。

6.3 多服务器场景下的密钥批量分发

当你需要给十几台机器部署公钥时,逐台登录执行ssh-copy-id就显得太慢了。可以写一个简单循环:

for host in server1 server2 server3; do ssh-copy-id -f -i ~/.ssh/id_ed25519.pub user@$host done

ssh-copy-id的-f参数是强制复制,即使公钥已经存在也会重新写入。执行完一轮后,再写一个循环批量验证:

for host in server1 server2 server3; do ssh -o BatchMode=yes -o ConnectTimeout=5 user@$host "echo OK $host" done

BatchMode=yes是关键参数,它禁止交互式密码输入,认证失败时直接报错而不是卡在密码提示上。这个命令适合批量快速检查哪些机器能免密登录、哪台机器还有问题,非常值得投入使用。

7. 我的一点实际体会

这套密钥认证流程我从第一次接触SSH到现在,前前后后配置过几十台服务器,踩过的坑基本都写在上面了。回过头来看,真正让我觉得值得坚持切换密钥认证的原因,不是配置一次之后免密登录那点便利,而是整个安全模型的变化:密码不再出现在任何网络传输中,私钥可以随身携带又不暴露给服务器,离职成员或泄露设备可以精确回收权限,一切操作都有对应的审计线索。

如果你的服务器还开着密码登录,我建议你抽出半小时按这篇文章的步骤把密钥认证配置好,关掉密码登录后再观察一段时间。刚开始可能会遇到一些连接上的小问题,但把Permission denied和连接超时这两类报错的排查思路过一遍之后,你会发现SSH其实没有太多神秘的地方。后面再遇到新的问题,多看日志、多理解认证流程,基本都能自己解决。

延伸一步想,密钥认证的思路其实也可以迁移到其他场景:比如Git仓库的SSH方式拉取代码、SFTP传输文件、内网自动化巡检脚本,都共用同一套密钥基础设施。把基础打牢之后,这些上层应用都会省心不少。

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

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

立即咨询