☰
SSH密钥认证配置指南:公钥私钥登录服务器与安全加固实践
2026/10/10 10:22:52 网站建设 项目流程

1. 先弄清楚:服务器上为什么要用公钥和私钥这套东西

1.1 密码登录的毛病

你手里有一台服务器,IP地址是固定的,平时要么用密码登录,要么靠密码配合各种安全软件勉强撑着。密码登录最大的问题不在于“密码本身不够长”,而在于它每次登录都要走一遍交互认证流程,只要这台服务器暴露在公网上,就一定会被扫描、爆破、试探。我见过太多人把root密码设成复杂字符串,结果三个月后翻日志,里面全是来自各个IP的失败尝试记录。密码爆破本质上是“概率游戏”,只要你的密码不是无限长度,理论上就有被撞库撞出来的可能,只是时间成本问题。

公钥和私钥这套机制,英文里叫做SSH密钥认证,它解决的正是“密码容易被爆破”的场景。你不是用一串字符证明自己,而是用一把“私人钥匙”去对上服务器上预留的“锁头”。只要私钥不泄露,攻击者就算把你的服务器翻个底朝天,也拿不到登录凭证。这不是什么新概念,所有的Linux服务器、Git代码仓库、云上运维平台,底层走的都是这套东西。我们今天说的“公钥与私钥配置服务器”,本质就是三件事:生成本机密钥对,把公钥部署到服务器,再调整服务器配置让系统只认公钥。

1.2 密钥对的工作机理

先打个比方。公钥和私钥的关系,就像你家的门锁和钥匙。门锁可以复制出很多把,随便发给别人都没关系,别人拿着门锁也开不了门;但钥匙只有你手里这一把,钥匙丢了,门就谁也进不去。在加密世界里,公钥就是那把“锁”,可以公开,可以放到服务器上、放到代码仓库里、写到各种配置文件里;私钥就是“钥匙”,只能存在你自己的本机,不能给任何人看一眼。

具体到登录流程里,情况是这样的:当你的客户端发起SSH连接,服务器会生成一串随机数,用你预留的公钥加密后发回来,你的客户端收到后用私钥解密,再把解密结果返回给服务器验证。整个过程里,私钥从头到尾不出你的电脑,网络上传送的只有加密后的随机数据。服务器端验证通过,连接建立,之后的一切操作都走加密通道。这套验证过程写起来复杂,但用户实际感受到的只有一个字:快。密钥认证免去了输密码的环节,配合ssh-agent或者密钥链还能进一步简化,后面我会专门说。

2. 环境准备:在动手配置服务器之前,先把本机这一侧弄明白

2.1 确认本机SSH客户端

配置服务器不是服务器单方面的事,它要求“本机手上有钥匙,服务器门上有锁”。所以第一步不是登录服务器改配置,而是先看看你本机到底是什么环境。

Linux和macOS系统默认自带OpenSSH客户端,你在终端里敲一行ssh -V就能看到版本信息。Windows这边的情况要稍微留意一下。Windows 10 1809之后的版本,系统设置里的“可选功能”中自带OpenSSH客户端,装上以后可以在PowerShell或CMD里直接使用ssh命令。Windows 11就更省事了,默认组件里基本都带了,你打开PowerShell输入ssh -V试一下,如果有版本输出,说明环境已经就绪。

如果你的Windows上还没有OpenSSH客户端,去“设置 — 系统 — 可选功能 — 添加可选功能”里找OpenSSH客户端装上就行。别去第三方网站下载所谓的SSH工具,系统自带的基本够用,而且和Linux上的行为完全一致。

顺便提一句,如果你平时用VSCode做远程开发,VSCode的Remote-SSH插件本质上也是调本机OpenSSH客户端,配置密钥的环境路径跟你命令行里用的完全一样。所以把命令行里的密钥体系配好,VSCode那边自动就通了,这也是我推荐大家先在命令行里把流程走通的原因。

2.2 生成密钥对:ssh-keygen实战

确认SSH客户端就绪后,就可以生成密钥对了。在终端里执行:

ssh-keygen -t ed25519 -C "我的服务器登录密钥"

这里几个参数说清楚。-t指定算法类型,ed25519是目前综合安全性和性能都比较好的选择。它的密钥短、速度快、安全性高,几乎不需要纠结。有些老系统只支持RSA,那你可以用:

ssh-keygen -t rsa -b 4096 -C "我的服务器登录密钥"

RSA 4096是兼容性最保守的方案,只要不是特别老的系统基本都没问题。两种算法选一个就行,我个人的习惯是能上ed25519就上ed25519,少数老服务器不接受再做RSA降级。

执行命令后,系统会问你要把密钥保存在哪里。默认路径是~/.ssh/id_ed25519,一般直接回车用默认路径就好。然后它会提示你设置一个passphrase,也就是私钥的使用口令。这一步很多人图省事直接留空,我建议不要留空。

passphrase不是登录服务器用的密码,而是一道保护私钥的本地屏障。如果有人拿到了你的私钥文件,没有passphrase他还是用不了。输入一个自己记得住的短语,成本很低,安全性提高一个量级。

生成完成后,~/.ssh/目录下会出现两个文件。不带.pub后缀的是私钥,带.pub后缀的是公钥。私钥文件默认权限是600,公钥是644,等下我还会单独说权限问题。

2.3 生成时最容易被忽略的两个细节

第一,看清楚公钥内容。公钥是一行文本,开头是算法名,中间是Base64编码的密钥本体,结尾是刚才-C参数写的注释。把这行内容完整复制下来,别漏字符。很多人在部署时手打公钥,结果少了一个字符,服务器端验证就是过不去。

第二,检查一下~/.ssh/目录属性。正常情况下这个目录权限应该是700,也就是只有你自己的账户能读写执行。如果权限过大,有些严格模式的SSH客户端会直接拒绝读取私钥,报错还很模糊。可以用下面命令修正:

chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pub

这三行命令养成习惯,每次生成密钥后都顺手跑一遍,能避免后面一大半权限相关的坑。

3. 把公钥部署到服务器:这一步其实就三招

3.1 最省事的方式:ssh-copy-id

生成了密钥对,接下来就是把公钥放到服务器的“锁孔”里。对于Linux和macOS用户来说,最省事的方式是使用ssh-copy-id命令:

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

执行过程中会要求你输入一次密码,这是唯一一次需要密码的场景。命令的作用是自动登录服务器,把指定公钥追加到用户主目录下的~/.ssh/authorized_keys文件末尾,并顺手修好相关文件的权限。整个过程一气呵成,基本上不会出错。

有些精简版系统可能不带ssh-copy-id命令,这时候不用慌,用下面手动部署的方法就行。

3.2 手动追加authorized_keys

手动部署的原理也很简单,就是在服务器上创建一个authorized_keys文件,然后把公钥内容写进去。具体步骤是:

  1. 用现有密码登录服务器:ssh 用户名@服务器IP
  2. 检查用户主目录下有没有.ssh目录,没有就创建:
    mkdir -p ~/.ssh chmod 700 ~/.ssh
  3. 使用编辑器打开或创建认证文件:
    vi ~/.ssh/authorized_keys
  4. 把本机公钥内容粘贴到文件里,每行一个公钥。
  5. 修改文件权限:
    chmod 600 ~/.ssh/authorized_keys

这里要注意一个细节:authorized_keys文件里的公钥每个占一行。如果以后要加第二台电脑的钥匙,直接在末尾追加一行,不需要删除旧的行。这个文件可以积累很多公钥,每行对应一个可以登录这台服务器的身份。

3.3 权限不对等于白配

很多新人在这步反复踩坑:公钥明明部署上去了,从客户端连接时却一直提示输入密码,或者直接报Permission denied (publickey)。绝大多数情况就是服务器端权限问题。

SSH对服务器端的文件权限要求非常严格,它的逻辑是:如果认证相关文件对“其他用户”可写,系统会认为文件不安全,从而拒绝使用其中的密钥。具体要求可以概括成几条:

  • ~(用户主目录)权限不能大于755
  • ~/.ssh目录权限只能是700
  • ~/.ssh/authorized_keys文件权限只能是600

用下面一组命令统一修正:

chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chown $(whoami) ~/.ssh ~/.ssh/authorized_keys

第三行命令是把文件归属改成当前用户,防止因为root部署导致普通用户无法读取。服务器上如果你是用root登录的,那么authorized_keys默认归root,以后换普通用户登录时就会出问题。正确做法是:第一次用root部署好普通用户的钥匙之后,chown到那个普通用户,这个细节尤其容易漏。

4. 服务器端sshd_config加固:让密钥成为唯一入口

4.1 关键配置项逐条解释

公钥部署成功之后,默认情况下你依然可以用密码登录。很多人的目标是把服务器配置成“只认密钥、不认密码”,这一步就需要修改服务器上的SSH守护进程配置文件/etc/ssh/sshd_config。

打开配置文件,找到或追加以下几项:

PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password ChallengeResponseAuthentication no UsePAM no
  • PubkeyAuthentication yes:开启公钥认证,这是基础前提。
  • PasswordAuthentication no:关闭密码认证,这是核心加固项。设置之后,服务器彻底拒绝密码登录,没有私钥的人连试探的机会都没有。
  • PermitRootLogin prohibit-password:允许root用密钥登录,但禁止密码登录。如果你习惯直接以root工作,这个配置能兼顾安全和便利。
  • ChallengeResponseAuthentication no:关闭挑战响应认证,避免一些额外认证方式绕过密码限制。
  • UsePAM no:这是一个偏保守的选项,关闭PAM模块介入。注意,有些系统上关闭PAM会影响其他认证流程,配置前最好确认一下你的服务器有没有依赖。

修改配置后重启服务:

systemctl restart sshd

有些发行版的SSH服务名是ssh,不是sshd,不确定可以两个都试一下,哪个返回成功用哪个。

4.2 重启前自检:别把自己锁在门外

这是整篇里最重要的一句话:在彻底关闭密码认证之前,务必开一个备用终端窗口,保持当前的SSH会话不断开。

因为一旦配置有误,重启服务后你可能就再也连不上了。如果你在云厂商控制台有VNC或者网页终端,那还有后路;但很多自建机房服务器,一旦SSH连接失败,就只能跑现场接显示器敲键盘了,非常折腾。

我自己的检查流程是这样的:

  1. 新开一个终端窗口,用密钥测试连接:ssh 用户名@服务器IP,确认能登进去再继续。
  2. 在已连接的会话里执行sudo sshd -t,这是SSH配置文件的语法检查命令。如果有错误,它会明确告诉你哪一行有问题。
  3. 语法检查通过后再重启sshd服务。

sudo sshd -t这个命令很多人不知道,但它确实能在重启之前帮你把配置错误拦截下来。凡是改sshd_config,我必定先跑这一条,跑完心里有底了才重启。

4.3 密钥的应急通道与定期轮换

关闭密码登录之后,私钥就成了唯一的入场券。如果私钥丢了、忘了passphrase、或者电脑被盗,服务器就等于彻底失联了。所以我在生产环境里通常会提前埋两条备用通道。

第一条是为root账户单独生成一对密钥,公钥放到服务器的root用户authorized_keys里,私钥加密压缩后放到一个离线安全的位置。平时不用它,真出事的时候这就是保命绳。

第二条是保留一个防火墙白名单外的临时密码入口。具体做法是:配置一个非常用的SSH端口,并且只在内网段或白名单IP范围内开放密码认证。这条通道平时是关着的,只在极端情况下临时打开。安全人员看到这个可能觉得不够严谨,但运维的底线思维永远是“先保证能进去,再谈安全感”。

密钥轮换方面,建议半年左右换一次服务器公钥。轮换的步骤是:本地生成新密钥对,把新公钥追加到服务器的authorized_keys中,测试新密钥能够登录后,把旧公钥从authorized_keys中删除,最后更新本机~/.ssh/config里对应的IdentityFile路径。整个过程可以在一个SSH会话里分步完成,关键是“先加后删”,别一上来就把旧钥匙拔了,结果新钥匙还没放好,自己进不去了。

5. 多台服务器、多个密钥的日常管理方案

5.1 用~/.ssh/config管理所有主机

手里服务器多了以后,有一个很现实的问题:不同服务器可能需要不同的私钥,IP记不住,用户名也各不相同。很多人每天都靠记忆敲完整的ssh命令,这完全可以理解,但更好的做法是用~/.ssh/config文件管起来。

在本地~/.ssh/config文件里写:

Host aliyun-prod HostName 192.168.1.10 User ubuntu IdentityFile ~/.ssh/id_ed25519 Port 22 Host office-git HostName git.example.com User git IdentityFile ~/.ssh/id_ed25519_work Port 2222

配置完成之后,你只需要在终端输入:

ssh aliyun-prod

剩下的HostName、用户名、密钥、端口全都会自动带上。这个文件相当于你的“连接通讯录”,一次配置,长期省心。

这里有个小经验:IdentityFile优先显式指定。不要以为系统会自动选择正确的私钥,当~/.ssh目录下有多把钥匙时,SSH客户端不一定能猜到你想用哪一把。显式配置后,连接速度和成功率都会明显提升。

5.2 ssh-agent怎么用比较稳

如果你的私钥设置了passphrase,那么每次SSH连接时都要输入一次。次数多了,很多人就会动“把passphrase去掉”的念头,但我强烈不建议这么做。更好的解决办法是使用ssh-agent。

ssh-agent是OpenSSH自带的一个“钥匙保管员”,它把私钥加载进内存,之后所有的SSH连接都经由它完成身份验证,你不需要重复输入passphrase:

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

第一行启动agent进程,第二行把私钥加入agent,然后会提示你输入一次passphrase。输完之后,当前会话里的所有SSH连接都不再需要重复输入。

macOS用户还可以用系统自带的钥匙串功能,让passphrase在系统层面记住,省事程度更高。Windows 11上的OpenSSH客户端默认集成了agent服务,服务名是OpenSSH Authentication Agent,在服务管理里把它设为自动启动,然后就能在PowerShell里用ssh-add了。

5.3 密钥的备份、吊销与轮换

先给个忠告:私钥不要随便备份。很多人图省事,把私钥文件上传到云盘、微信传输助手或者公司文档里,这个习惯非常危险。私钥一旦泄露,和你服务器密码泄露没有本质区别,而且因为关闭了密码登录,泄露私钥等于直接交出服务器控制权。

如果你确实需要备份,建议这样处理:私钥压缩后用加密压缩包的形式存放,密码用强密码短语,且和私钥本身的passphrase不是同一个。离线U盘也比网盘安全得多。

吊销的逻辑和备份相反:一旦怀疑私钥泄露,第一时间在服务器上从authorized_keys里删除对应的公钥行,然后马上生成新密钥对并部署。删除公钥后,旧私钥瞬间失效,比改密码操作快得多。这也是密钥认证相比密码认证的一个天然优势:吊销钥匙只需要删一行文本,即刻生效。

轮换频率上,个人服务器一年一次就行,企业合规环境建议每半年一次。轮换时务必牢记我前面说的顺序:新密钥先部署,测试通过,旧密钥再删除。

6. 真实踩坑记录与问题排查速查表

6.1 权限问题是最普遍的故障源

我在帮别人排查SSH密钥连接问题时,十个里面有六个是权限问题。典型表现是:服务器端authorized_keys里明明有公钥,客户端连接时却一直要求输入密码,或者直接报Permission denied。

排查命令很简单,在服务器上切换到目标用户,执行:

namei -l ~/.ssh/authorized_keys

这条命令会把路径上每一级目录的属主和权限列出来。你要检查的是:从根目录到authorized_keys,每一级都不能存在“其他用户可写”的状态。任何一级出问题,比如用户主目录所属错了、.ssh目录变成777了,都会导致认证失败。

还有一种隐蔽情况:authorized_keys文件是用root创建的,后来虽然chmod 600了,但属主不是当前用户。这时用chown把属主改回来就行。

6.2 指纹变化与known_hosts冲突

服务器重装系统、重建虚拟机之后,SSH服务的主机密钥会变。这时候本地连接会收到一个警告:REMOTE HOST IDENTIFICATION HAS CHANGED,然后拒绝连接。

这不是密钥认证出了问题,而是客户端的known_hosts文件还留着旧的主机指纹。解决办法是删除对应主机的旧记录:

ssh-keygen -R 服务器IP

执行完这条命令后再重新连接,会提示你确认新的主机指纹,输入yes即可。如果你管理的主机比较多,换掉的是整个known_hosts文件,那也不建议直接删光,一个IP一个IP地-R处理更稳妥。

6.3 端到端排查的推荐顺序

如果你的公钥配好了、配置改了、服务也重启了,但连接依然失败,我建议你别东猜西猜,按下面顺序逐级排查:

  1. 在本地用ssh -v 用户名@服务器IP开启调试输出。这个参数会在连接过程中打印详细的握手日志,最基本的错误原因一眼就能看到。
  2. 重点看服务端日志日志。Ubuntu系的日志位置在/var/log/auth.log,CentOS系在/var/log/secure。tail -f盯住日志文件,再另开一个终端尝试连接,日志里会精确告诉你认证在哪一步被拒绝的。
  3. 检查服务器端口是否通。telnet 服务器IP 22或者nc -vz 服务器IP 22,确认能连上再说配置的事。
  4. 检查本地私钥文件是否存在且路径正确。ls -l ~/.ssh/看一眼就知道。
  5. 最后再确认一次sshd_config里的配置项有没有被后续行覆盖。SSH配置的规则是“后出现的配置覆盖前面的”,有些人配置写在文件前半段,后面又被Include进来的其他配置覆盖回去了。

这套排查流程走下来,百分之九十的问题都能定位。剩下百分之十,多半是防火墙策略或SELinux造成的,那就要去查云安全组入方向规则和getenforce状态了。

6.4 我自己养成的几个运维习惯

写到最后,说几个我这些年攒下的习惯。第一个是每次生成新密钥之后,我会顺手把公钥的最后一段注释改成“用途+主机名+日期”,比如web-01-2026-01。这样以后在服务器的authorized_keys里看到一堆公钥时,还能分清哪把钥匙是谁的、什么时候加的。不然时间一长,钥匙多了根本对不上号。

第二个习惯是坚持使用普通用户登录、特权操作用sudo。就算root密钥配置得再安全,日常操作也尽量少用root身份。密钥认证能防外部攻击,但防不了手滑误操作,权限边界多一层都是好的。

第三个习惯是每次修改服务器SSH配置之前,先备份原文件,备份名带上日期。比如sshd_config.bak.20260101。改完有问题,30秒就能回滚;没备份,就只能凭记忆还原了。

密钥认证这套方案配置好之后,你几乎感觉不到它的存在。每天连接服务器就是敲一行命令、直接进入工作状态,不再被密码询问打断。但它又在每一秒的连接中替你挡住了那些密码爆破、扫描试探的流量。运维工具的价值不在于使用时的存在感,而在于没有存在感的那份稳妥感。下一次再有人问我“密码已经很复杂了,有必要配密钥吗”,我还是会用行动回答:配,而且越早配越省心。

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

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

立即咨询