第一次管理 Linux 服务器的人,几乎都是从"用密码登录"开始的。方便归方便,但只要 22 端口暴露在公网,密码登录就会持续遭遇自动化暴力破解:每天成千上万次的用户名密码尝试,一旦某个密码太弱或者某个服务账号忘了改默认口令,服务器就可能被攻破。相比之下,SSH 密钥登录用一对非对称密钥代替密码,客户端持有的私钥几乎不可能被在线猜到,能把这类风险降低几个数量级。
这篇文章从运维视角,完整讲一遍密钥登录该怎么配:怎么生成密钥、怎么把公钥分发到服务器、sshd_config 里要改哪几项、目录和文件的权限为什么必须卡死、多台机器和多个密钥怎么管理、ssh-agent 怎么用,以及最关键的——怎么在"关掉密码登录"这种高风险操作中,保证自己不把自己挡在门外。命令都区分了 RHEL/CentOS/Rocky 和 Debian/Ubuntu 两套。
先给一个明确的前提:本文讲的所有操作,都是围绕"规范化自己的服务器访问控制"展开,属于标准的运维加固动作。密钥的公开部分(公钥)本来就是要放出去的,私钥必须严格保管,绝不外传。
一、密钥登录的原理:为什么它比密码安全
密码登录的本质是"客户端把秘密本身发给服务器,服务器比对是否一致"。哪怕走的是加密通道,秘密在网络两侧都出现过,服务器后端也可能把它存成可被撞库的形式。密钥登录换了一套思路:客户端手里拿着私钥,服务器上放着对应的公钥,登录时双方做一个数学上的挑战应答,客户端用私钥证明"我确实持有这把私钥",而私钥本身从头到尾没有离开过客户端机器。服务器即使被拖库,也拿不到任何可以直接登录的东西。
这带来几个实际好处。第一,不存在"密码字典撞库"这回事,因为验证的不是一段可猜的字符串,而是一次加密运算的结果。第二,公钥可以随便复制到多台服务器,私钥只有一份,分发公钥不影响私钥的安全。第三,私钥可以加一道本地密码(passphrase)保护,即使私钥文件被偷,没有密码也用不了。
当然,密钥不是没有风险。私钥文件一旦泄露,拿到它的人就等于拿到了你的登录凭据;所以私钥的存放、权限和备份方式,反而是密钥登录里最需要认真对待的部分。另外,密钥登录适合人和机器,但非常适合自动化:CI/CD 流水线、定时备份脚本用密钥免交互登录,是行业惯例。
二、生成密钥:优先选 ed25519
现代 OpenSSH 默认推荐 ed25519 算法,它密钥短、生成快、安全性好,比传统的 RSA 更干净。只有面对非常老的服务器(比如十年前的设备或某些只认 RSA 的嵌入式系统)时,才需要回退到 RSA。
# 在你的个人电脑上执行(Linux/macOS/Windows 的 Git Bash 均可)
# -t 指定算法,-C 加一段注释方便日后识别,-f 指定存放路径
ssh-keygen -t ed25519 -C "work-laptop-2026" -f ~/.ssh/id_ed25519_server
# 如果必须用 RSA(兼容老服务端),建议至少 4096 位
ssh-keygen -t rsa -b 4096 -C "legacy-rsa" -f ~/.ssh/id_rsa_legacy
执行过程中会问你两件事。第一是保存路径,直接回车用默认也行,但更推荐按用途区分文件名,比如上面用了 id_ed25519_server,这样一台电脑管多台服务器时不会互相覆盖。第二是设置 passphrase,也就是给私钥再加一道本地口令。这一步强烈建议设置:它意味着即使私钥文件被拷走,攻击者没有口令也用不了。如果嫌每次输口令麻烦,后面用 ssh-agent 可以只输一次。
生成完会得到两个文件:id_ed25519_server 是私钥,权限必须是 600,谁也不能多读;id_ed25519_server.pub 是公钥,就是那一行以 ssh-ed25519 开头的内容,可以随便分发。
# 查看私钥文件权限(应该是 -rw-------)
ls -l ~/.ssh/id_ed25519_server
# 如果权限太松,收紧它
chmod 600 ~/.ssh/id_ed25519_server
# 查看公钥内容(这一整行就是要发给服务器的)
cat ~/.ssh/id_ed25519_server.pub
Windows 用户注意:如果用的是系统自带的 OpenSSH,~/.ssh 默认在 C:\Users\你的用户名\.ssh,路径用正斜杠或反斜杠都可以,但权限模型和 Linux 不同,通常不会因为权限问题导致登录失败;而 Linux 客户端对权限极其敏感,这是后面要专门讲的一节。
三、把公钥传到服务器:ssh-copy-id
把公钥放到服务器的过程叫"分发"。最规范的做法是用 ssh-copy-id,它会自动在服务器上创建 ~/.ssh 目录、把公钥追加到 authorized_keys、并把权限设对,省去手工操作的麻烦。
# 语法:ssh-copy-id -i 公钥文件 用户名@服务器地址
ssh-copy-id -i ~/.ssh/id_ed25519_server.pub root@198.51.100.20
# 如果服务器 SSH 端口不是 22
ssh-copy-id -i ~/.ssh/id_ed25519_server.pub -p 2222 root@198.51.100.20
这一步需要用密码登录一次,所以必须在关闭密码登录之前做。完成后,用密钥试登一次:
# 指定使用哪把私钥登录(-i 指向私钥,不是公钥)
ssh -i ~/.ssh/id_ed25519_server root@198.51.100.20
# 如果私钥设了口令,这一步会提示输入口令
如果这台机器经常登,建议在 ~/.ssh/config 里配置别名,之后直接 ssh 我的服务器 就能连。
# ~/.ssh/config (客户端侧,权限建议 600)
Host myserver
HostName 198.51.100.20
User admin
Port 22
IdentityFile ~/.ssh/id_ed25519_server
ServerAliveInterval 60
配置好后 ssh myserver 即可登录。ServerAliveInterval 60 是个实用选项,它每隔 60 秒发一个保活包,能减少因网络空闲导致的连接被断开。
有的环境无法用 ssh-copy-id(比如跳板机限制),也可以手工追加,但一定要核对权限:
# 手工方式:把公钥内容追加到服务器上的 authorized_keys
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA...你的公钥内容... work-laptop-2026" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
四、权限管理:这一步做错,密钥登录必然失败
SSH 对权限的检查近乎苛刻,这是它的一种安全设计。只要相关文件或目录的权限"过松",sshd 就会拒绝使用密钥,理由往往是"目录可被其他人写"。很多人配了半天登不上去,最后发现是权限问题。记住下面这张表,基本就不会踩坑。
| 路径 | 正确权限 | 含义 |
|---|---|---|
~/.ssh |
700(drwx------) | 只有本人可读可写可进入 |
~/.ssh/authorized_keys |
600(-rw-------) | 只有本人可读可写 |
| 私钥文件(客户端) | 600 | 只有本人可读可写 |
| 公钥文件 | 644 即可 | 公开内容,无特殊要求 |
家目录 ~ |
不能是全局可写 | 例如 750 或 755,一定不能是 777 |
# 一次性把服务器侧权限设对
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~ # 确保家目录不对组和其他用户开放写
还有一个隐蔽的坑:SELinux 开启的系统(如 RHEL 系默认开启)对 ~/.ssh 目录有安全上下文要求。如果你从别处拷过来的目录上下文不对,OpenSSH 会因为 SELinux 拒绝而看不到 authorized_keys。排查时可以临时看审计日志,或者用恢复上下文的命令处理。
# 查看是否被 SELinux 拦住(RHEL/CentOS/Rocky)
sudo ausearch -m avc -ts recent | grep ssh
# 恢复 ~/.ssh 目录的默认安全上下文
restorecon -Rv ~/.ssh
如果 authorized_keys 里有多把公钥,每把占一行,按行独立生效。这样一来,你可以给不同设备分配不同公钥,某台设备丢了,只需删掉对应那一行,不影响其他设备。
五、加固 sshd_config:关掉密码登录的正确姿势
服务器上的配置文件是 /etc/ssh/sshd_config(有些系统会有 /etc/ssh/sshd_config.d/ 目录存放片段)。加固的目标通常是:启用密钥认证、关闭密码认证、禁止 root 直接登录、限定允许登录的用户。
先备份,再修改,这是铁律。
# 备份原配置
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
# 编辑配置
sudo vi /etc/ssh/sshd_config
需要确认或修改的关键项:
# 启用公钥认证(通常默认就是 yes,确认一下)
PubkeyAuthentication yes
# 关闭密码认证——这一步是风险最高的操作,务必先确保密钥登录可用
PasswordAuthentication no
# 禁止 root 用密码或密钥直接登录(视管理习惯决定)
# 更安全的做法是 root 完全禁止登录,用普通用户登录后再 sudo
PermitRootLogin no
# 只允许指定用户登录,多个用户用空格分隔(可选但很有用)
AllowUsers admin deploy
# 关闭空密码登录(现代版本默认就是 no,确认一下)
PermitEmptyPasswords no
# 限制认证尝试次数,降低被爆破的空间
MaxAuthTries 3
# 保持连接探测,及时清理死连接
ClientAliveInterval 300
ClientAliveCountMax 2
修改完成后,不要直接 systemctl restart sshd,正确做法是先校验语法、再重新加载:
# 校验配置语法是否正确(有的版本用 -t,有的用 -T 输出解析结果)
sudo sshd -t
# 输出为空或没有报错,说明语法通过
# 重新加载配置(reload 不会中断现有连接)
sudo systemctl reload sshd
# Debian/Ubuntu 上服务名是 ssh
sudo systemctl reload ssh
关键纪律:在执行 reload 之前,先保持当前这个 SSH 会话不要关。reload 之后,另开一个新终端窗口,用密钥登录一次,确认能进;能进,再放心关掉旧会话。如果不能进,你还有一个仍然活着的旧会话可以用来把 PasswordAuthentication 改回 yes 并 reload 恢复。这个"留一条活连接"的习惯,能帮你躲过绝大多数把自己锁死的意外。
# 新开一个终端,验证新配置下密钥登录是否正常
ssh -i ~/.ssh/id_ed25519_server admin@198.51.100.20
# 能正常进入,说明加固生效且没锁死自己
万一真锁死了,还有云厂商的网页控制台可以进系统修复,或者把云盘挂到另一台机器上改配置。但与其依赖这些兜底手段,不如养成上面那条纪律。
六、多密钥、多主机与 ssh-agent
当你管理的服务器越来越多,公钥散布在很多 authorized_keys 里,私钥也攒了好几把,这时候一个清晰的管理习惯就很重要了。
建议的做法是:按用途区分私钥文件名,并在 ~/.ssh/config 里为每台主机指明用哪把私钥。这样你不用每次记 -i 参数,也不会因为默认私钥不对而反复失败。
# 客户端 ~/.ssh/config 示例:不同主机用不同密钥
Host prod-web
HostName 198.51.100.20
User admin
IdentityFile ~/.ssh/id_ed25519_prod
Host staging
HostName 198.51.100.21
User deploy
IdentityFile ~/.ssh/id_ed25519_staging
ssh-agent 解决的是"私钥设了口令,每次登录都要输"的痛点。它把解密后的私钥放在内存里,在本次会话内帮你完成签名,你只需要在一次添加时输入口令。
# 启动 agent(大多数桌面环境会自动启动)
eval "$(ssh-agent -s)"
# 把私钥交给 agent 管理,会提示输入一次 passphrase
ssh-add ~/.ssh/id_ed25519_server
# 查看 agent 里已加载了哪些密钥
ssh-add -l
# 如果还要跳到内网另一台机器,开启密钥转发(谨慎使用)
ssh -A admin@198.51.100.20
这里要特别提醒:ssh -A 开启的是"agent 转发",它让你登录跳板机后,可以继续用你本机的私钥去连下一台机器。方便,但风险也真实存在:如果跳板机本身被入侵,攻击者可能借用你转发的 agent 身份去访问其他能访问的机器。所以 agent 转发只在可信的跳板机上使用,不要在共享或不可信的主机上开启。
私钥的备份也需要规划。私钥只应该保存在你信任的设备上,最好有加密备份(比如放进加密磁盘、密码管理器),并给每台服务器留至少两把有效公钥——一把常用、一把备用。这样当常用设备损坏或私钥丢失时,你有备用路径进去把失效的公钥删掉。
七、常见误区与务实建议
第一个误区:认为"改了 SSH 端口就等于安全了"。改端口能过滤掉一大部分扫描噪声,日志会清爽很多,但它只是"隐蔽手段",不是"安全措施"。一个针对目标专门扫描的人几分钟就能发现你换到了哪个端口。所以改端口可以配合做,但必须同时关闭密码登录、限制来源,不能把它当主力。
第二个误区:用一把私钥打通所有服务器。这在初期很省事,但它违反了"最小权限"原则:一台机器泄露,等于全部沦陷。更规范的做法是按环境或按用途分密钥,生产、测试分开,人的密钥和自动化的密钥分开。
第三个误区:关闭密码登录前没有充分验证。这是导致"把自己锁在外面"的头号原因。纪律就一条:任何会影响登录的改动,都先留一条已验证的备用通道,改完立即用新通道验证,通过之后才清理旧通道。
第四个误区:忽视私钥的本地安全。私钥设了口令是好的,但也要确保你的个人电脑本身是安全的,别把私钥明文丢在共享盘、聊天记录或公开仓库里。私钥一旦外泄,服务器侧再怎么配置都补救不了。
常见问题
Q:没有 ssh-copy-id 命令怎么办?
ssh-copy-id 是 openssh-client 包的一部分,通常自带。如果确实没有,可以手工把公钥内容追加到服务器 ~/.ssh/authorized_keys,并确保权限正确(目录 700、文件 600)。也可以在一台有该命令的机器上操作。
Q:密钥登录配好了,但一直提示 Permission denied (publickey)。
按顺序排查:一是客户端是否指定了正确的私钥(用 ssh -v 看详细过程);二是服务器 authorized_keys 里是否确实有对应公钥、内容是否完整(别被换行截断);三是权限是否正确(客户端私钥 600,服务器目录 700、文件 600);四是 RHEL 系是否被 SELinux 拦了。ssh -v 的输出能直接告诉你卡在哪一步。
Q:关掉密码登录后,我自己的旧连接还活着,但新连接进不去。
说明密钥侧还没配好。此时用还活着的旧连接,把 PasswordAuthentication 改回 yes 并 reload,先恢复访问,再从容排查密钥问题。这也是为什么改配置时绝对不能把旧会话关掉。
Q:能不能保留密码登录,只是作为兜底?
不建议在公网服务器上长期保留。密码登录是自动化爆破的主要目标,开着它,密钥再安全也等于留了一扇容易被撬的后门。如果确实担心,可以只允许密码登录来自内网或白名单 IP,把公网密码登录关掉。
Q:私钥设了口令后,脚本自动化登录怎么办?
脚本场景不应使用带口令的私钥交互输入。规范做法是为自动化单独生成一把专用的、不带口令的私钥,严格限制其能访问的目标和权限,并配合 ~/.ssh/config 与最小化的 command= 限制,而不是把人工私钥的口令去掉。
总结
SSH 密钥登录是公网服务器最基础也最有效的加固手段之一,它把"可被猜到的密码"换成了"离不开手的私钥",从根本上抬高了暴力破解的门槛。
生成密钥优先用 ed25519;分发公钥用 ssh-copy-id;服务器侧权限必须卡死为目录 700、authorized_keys 600、私钥 600,权限不对密钥登录必然失败。
加固的核心是关闭密码认证、按需禁止 root 登录、用 AllowUsers 限定可登录用户;改完先 sshd -t 校验语法、再 reload,并始终保持一条已建立的旧连接用于兜底。
日常管理上,按用途区分私钥与主机别名、用 ssh-agent 免重复输口令、给每台服务器留一把备用公钥;私钥只放在可信设备上,绝不外传。
最后一条纪律贯穿始终:任何可能影响登录的改动,都要"先验证新通道、再切断旧通道"。做到这一点,你既享受了密钥登录的安全,也不会被它反噬。
企业QQ咨询




