0%

协议是怎样炼成的 SSH中篇

《协议是怎样炼成的》· SSH(中篇) · 把 SSH 用得既安全又顺手

上篇讲清了 SSH 的内核——一次握手、两个方向的相互认证。协议本身已经够安全,可它能不能被你用得既安全、又顺手,是另一回事。

麻烦就麻烦在,日常里”安全”和”顺手”老像在跟你拧着来:私钥加上密码才安全,可每次登录都要输一遍,烦;几百台机器逐台核对指纹才安全,可谁耗得起,于是闭眼狂敲 yes但凡你被逼着在”安全”和”顺手”之间二选一,背后几乎都藏着同一句潜台词:你还缺一件趁手的工具。 本篇要讲的密钥、agent、配置、隧道、证书、加固,串起来正是同一条线——你的处境会从”一个人、一把钥匙”,一路升级到”几百台机器、一支团队”;每升一级,就有一件工具登场,把那道”要安全还是要顺手”的选择题,重新变回”安全的做法本身就是最顺手的做法”。

就从最基础、却几乎人人将错就错的第一件事说起:你登录用的那把钥匙,到底该选哪种?

一、登录钥匙:默认 Ed25519,古董系统才用 RSA

新生成密钥,无脑选 Ed25519;只有当你要连的是不支持它的古董系统时,才退回 RSA(至少 3072 位,求稳用 4096)。ECDSA 和 DSA 都不必考虑。

1
ssh-keygen -t ed25519 -C "你的标识,比如邮箱"

命令里的 -C可选的注释,纯给人看,不参与任何加解密。它会跟在公钥末尾、并原样进入服务器的 authorized_keys,作用就一个:当一台服务器上堆了好几把公钥时,让你一眼认出”哪把是谁的、干嘛的”。写法上没有硬规范,但有惯例:个人多设备用 用户@设备(如 ethan@macbook),团队或自动化用 用途@系统(如 deploy@ci),想方便日后轮换就再缀个日期。不写也能用,OpenSSH 会默认填成 用户名@主机名;注释写错随时能改,ssh-keygen -c -f ~/.ssh/id_ed25519 即可,无需重新生成密钥。

下面说清楚,为什么是这个结论。

二、钥匙候选都有谁

ssh-keygen -t 能选的类型就这几种:rsa、ecdsa、ed25519,外加它们的硬件版 ecdsa-sk / ed25519-sk,以及已经躺进棺材的 dsa。(-t 就是 type,”指定密钥类型”,这么记就不会忘。)逐个看。

DSA(dsa)——别用,已经没了。 它被限死在 1024 位、用脆弱的 SHA-1,安全性早就不够。OpenSSH 7.0(2015)默认禁用,到 OpenSSH 10.0(2025)干脆整个移除。今天你不会、也不该再生成它。

RSA(rsa)——最老、最通用,但要够长。 RSA 1977 年就有了,是兼容性最好的算法:几乎任何古董服务器都认。代价是安全等级靠密钥长度堆——1024 位早已不安全,如今底线是 3072 位(OpenSSH 8.0 起 ssh-keygen 的 RSA 默认就是 3072),求稳可上 4096。它的密钥又长又慢,但只用在认证那一下,不影响传输性能。

这里有个极易踩的坑要先分清:RSA 密钥本身没过时,过时的是 ssh-rsa 这个用 SHA-1 的老签名方式。 OpenSSH 8.8(2021)默认禁掉了 SHA-1 的 ssh-rsa 签名,于是不少人误以为”RSA 不能用了”。其实你的 RSA 密钥会自动改用更安全的 rsa-sha2-256/512 签名、照常工作——同一把 RSA 密钥,只是换了更强的签名算法。(这个区分很要紧,下篇”登不进”那节还会专门讲它引发的报错。)

ECDSA(ecdsa)——能用,但没理由选。 椭圆曲线,比 RSA 短而快。但它有两点让人却步:一是它依赖 NIST 制定的曲线(nistp256/384/521),这些曲线的来历一直有人不放心;二是 ECDSA 对随机数极度敏感——只要某次签名的随机数被复用或可预测,私钥就会直接泄露(索尼 PS3 当年正是栽在这上面)。既然有更好的 Ed25519,就没有选 ECDSA 的理由。

Ed25519(ed25519)——当前首选。 它是 Daniel Bernstein 设计的 EdDSA,跑在 Curve25519 上,OpenSSH 自 6.5(2014)起支持。它几乎把 ECDSA 的毛病全修了:曲线来历透明可信;签名用的随机数是确定性生成的,从根上免疫”随机数复用泄私钥”那一类攻击;密钥极短(公钥就一行六十多个字符),生成和验证都快;安全强度约等于 128 位对称。新版 OpenSSH 也认了它的地位——从 9.5(2023 年 10 月)起,ssh-keygen 不指定 -t 时默认生成的就是 Ed25519(更老的版本默认还是 RSA,所以别图省事,显式写 -t ed25519 最稳)。

它唯一的短板是兼容性:OpenSSH 6.5 之前的服务器不认。但 6.5 是 2014 年的版本,今天还跑这之前 SSH 的机器极罕见——真碰上了,那才是 RSA 出场的场合。

三、想更进一步:把私钥锁进硬件

如果你对私钥安全要求很高,还有一档:ed25519-sk / ecdsa-sk(OpenSSH 8.2,2020 起)。-sk 是 security key 的意思——私钥不再是一个普通文件,而是绑定在一个 FIDO2 硬件令牌(YubiKey 之类)里,每次认证都要硬件在场、甚至按一下确认。好处是私钥没法从机器上被拷走:就算你的笔记本被入侵,没有那个物理令牌,攻击者也用不了你的密钥。

1
ssh-keygen -t ed25519-sk -C "你的标识"

四、小结

类型 选它吗 理由
Ed25519 ✅ 默认首选 安全、快、密钥短、抗随机数故障
RSA(≥3072) ⚠️ 仅为兼容古董系统 通用,但又长又慢
ECDSA 有更好的 Ed25519,且 NIST 曲线 / 随机数敏感
DSA ❌ 已移除 1024 位 + SHA-1,OpenSSH 10.0 已删
*-sk(硬件) 🔒 高安全场景 私钥锁进 FIDO2 令牌,拿不走

再补一招实用的:手头一把现成的钥匙,想知道它是什么类型、多长,不用猜——ssh-keygen -l -f ~/.ssh/id_xxx.pub 一看便知,输出末尾括号里就是算法(如 (ED25519)(RSA)),最前面的数字是位数。

1
2
ssh-keygen -l -f /root/.ssh/id_ed25519
256 SHA256:AH64kcT/dlszXw158I0XXXXXXXXXXXXXXXX1rPya2fsnRqQ ethan@alibaba (ED25519)

或者直接看 .pub 文件开头的前缀也行:ssh-ed25519ssh-rsaecdsa-sha2-nistp256,本身就标着类型。

1
2
cat /root/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+9bKK1eQ4SPG75d1hkng6IdcEfoPNPPfS0IhVfguES ethan@alibaba

钥匙选好、生成好了,新问题立刻冒出来:给私钥设了 passphrase 才安全,可这样每次连接都要解密、都要输一遍——烦不烦?这就引出下一节的 ssh-agent

五、ssh-agent:把”签名”这件事托管出去

上一节末尾那个尾巴,得先解开。私钥该不该设 passphrase?该——它把磁盘上的私钥文件加密,是私钥的最后一道防线:哪怕 ~/.ssh/ 被人整个拷走,没有 passphrase,那也只是一坨打不开的密文。

可一旦设了,新麻烦立刻来了:私钥既然是加密存着的,你每次用它登录,都得先解密、输一遍 passphrase。一天连几十台机器,就要输几十遍。久了人自然偷懒——干脆不设。结果最该保护的东西,因为太麻烦,反被你裸放在了磁盘上。

ssh-agent 就是为化解这个矛盾而生的:让你既给私钥上了锁,又不必每次都掏钥匙开锁。

它的办法,顺着上篇那条线就能想明白。还记得公钥认证靠的是什么吗?不是交出私钥,而是用私钥签个名。ssh-agent 是一个常驻后台的进程,专门替你保管解密后的私钥、并代你签名:

  • 一个会话里,你只在把钥匙交给它的那一次,输一遍 passphrase 解密;agent 把解密后的私钥留在自己内存里。
  • 此后每次登录,ssh 不再去碰磁盘上那个加密私钥,而是把”要签的数据”递给 agent,agent 在内存里签好、把签名还回来

passphrase 一个会话只输一遍,后面全由 agent 代签。而关键在于——agent 始终不把私钥交出去,它对外只提供”签名”这一个能力。这跟公钥认证同一套逻辑(私钥证明持有、绝不外露),只是把”持有私钥的那个角色”从你本人,换成了一个你授权的进程。(也正因如此,把 agent 转发到别的机器很危险——这个留到下篇专门算账。)

ssh 怎么找到这个 agent?靠一个环境变量 **SSH_AUTH_SOCK**——它指向 agent 监听的一个本地 socket,ssh 和 ssh-add 都认准它去找 agent。(记住它,下篇讲 agent 转发时,主角就是它。)

但这里要纠正一个普遍的误解:agent 并不会凭空就在那儿等你。 它在不在运行,取决于你的环境:

  • 桌面环境里(GNOME、macOS 等),登录桌面时系统通常已经替你起好了一个 agent;
  • 但你 SSH 进一台服务器、或在精简系统 / 纯命令行里,默认往往没有 agent 在跑。

所以会出现这种情形:你刚 ssh-keygen 生成完密钥,随手敲 ssh-add -l 想看看,却报 Could not open a connection to your authentication agent。这不是出错——它就是字面意思:根本没有 agent 可连。而且,密钥也不会因为”生成出来了”就自动进 agent,得你手动加。

没有就先起一个,两步:

1
2
eval "$(ssh-agent)"          # 在当前 shell 起一个 agent,并设好 SSH_AUTH_SOCK 等变量
ssh-add ~/.ssh/id_ed25519 # 把私钥加载进去(这一步才会问你 passphrase)

第一句里的 eval 是点睛之笔:ssh-agent 启动后会往屏幕吐几行变量赋值(SSH_AUTH_SOCK=... 之类),eval 把它们设进当前 shell;少了这步,agent 虽然起来了,你的 shell 却不知道它在哪,照样连不上——这正是上面那个报错最常见的根因。

agent 跑起来、钥匙也加好之后,几个日常命令:

1
2
ssh-add -l    # 列出 agent 里已装了哪些钥匙
ssh-add -D # 清空 agent 里所有钥匙

另有两个开关让 agent 更安全:ssh-add -t <时长> 给钥匙设有效期(如 -t 1h,到点 agent 自动忘掉,省得它在内存里常驻一整天);ssh-add -c 让每次签名都弹窗要你手动确认一下,即便有程序想偷借你的 agent 签名,也得过你这关。如果用的是上一节那种硬件密钥(-sk),它同样走 agent,只是最后那一下签名在令牌里完成、通常还得你摸一下才放行。

钥匙选好、agent 理顺,”设了 passphrase 就得反复输”这道安全与顺手的两难,被”一个会话只输一遍”解掉了——这正是工具到位后,安全的做法反倒成了最省事的做法。但要看清 agent 的本分:它是单机单人时代的省力工具,让你一个人管一把钥匙时又稳又轻松,仅此而已。

agent 替你免去了”反复输 passphrase”这一种重复。可日常里还有另一种重复天天磨人:每次连机器,都要敲一长串 ssh -p 2222 -i ~/.ssh/id_xxx user@10.0.0.5——又臭又长、还容易记错。下一节那件工具,就专治这个。

六、~/.ssh/config:让”顺手”成为默认

先看清这种重复是怎么一步步把你引向不安全的。一台机器换了非标端口、要指定专用私钥、登录名又和本地不同,你每次就得敲:

1
ssh -p 2222 -i ~/.ssh/id_prod deploy@10.0.0.5

机器一多,这种长命令记不住、也敲不对。于是人会本能地”优化”:干脆所有机器都用默认 22 端口、共用同一把钥匙、统一一个登录名——只为少敲几个字,把’每台机器专用钥匙、最小权限’这些安全习惯,一条条让了出去。又是那个老剧本:嫌麻烦,于是牺牲安全。

~/.ssh/config 就是来终结这种妥协的。

特别注意:它是 ssh 客户端的配置文件(放在你自己机器的 ~/.ssh/config,跟服务器端的 sshd_config 是两回事),让你把每台机器的连接参数起个别名、固化下来

1
2
3
4
5
6
7
8
9
# ~/.ssh/config
Host prod
HostName 10.0.0.5
User deploy
Port 2222
IdentityFile ~/.ssh/id_prod

Host *
AddKeysToAgent yes

写好之后,先前那条长命令就缩成一句:

1
ssh prod

HostNameUserPortIdentityFile 全从别名里自动取。关键变化在于——现在”给这台机器配专用端口、专用钥匙”反而比偷懒更省事了:一次写进 config,往后 ssh prod 张口就来。这正是本篇那条暗线最直白的一次兑现:工具到位后,最安全的做法(一机一钥、最小权限)同时成了最省力的做法,你再没有理由去图那点省事的安全债。

末尾那个 Host * 是通配块,对所有主机生效,适合放共用默认值。这里顺手填了一条 AddKeysToAgent yes:它把上一节的 agent 又抹平了一截——首次用到某把钥匙时,ssh 自动把它加进 agent,连手动 ssh-add 都省了。(配置按”从上往下、首次匹配生效”的规则读,所以专用的 Host 块写在前、通配的 Host * 垫在后。)

进阶一招|连同一台机器,第二次秒进
如果你常对同一台机器反复开会话(编辑、跑命令、传文件各开一个),可以让它们复用同一条底层连接

1
2
3
4
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m

第一条连接建好后,后续连同一台机器的会话直接搭车、秒连且不必再认证ControlPersist 10m 让主连接在你全部退出后再留 10 分钟备用。代价是会多出一个 socket 常驻,偶发”主连接不退导致改了配置不生效”的小坑——知道有这么个东西,需要时再开即可。

至于”先 SSH 上跳板机、再从它登内网机”这种多跳,config 里也有对应写法——一行 ProxyJump bastion 就能让 ssh 自动经跳板机中转。不过实话说,生产环境里这活儿基本让给堡垒机了:你不会手搓一长串跳板配置,而是统一登一台堡垒机、由它接管后端的访问。所以这里点到为止,记住”多跳有 ProxyJump 这么个原生解法”就够——那个把跳板机制度化的”堡垒机”,正是本篇后面证书一节真正的主角。

config 把你连得上的那些机器,伺候得又顺手又规矩。可总有些机器,你根本连不上——它躲在内网、端口没开、被防火墙挡着,钥匙再好也够不着。这时候,SSH 那条加密信道能帮的,就不只是登录了。这就是下一节最被低估的能力:SSH 隧道

七、SSH 隧道:端口转发的原理与场景

这一节我们换个讲法——只讲原理和场景,不抠命令参数。因为端口转发这件事,你将来多半是在堡垒机、或某个客户端的”隧道 / 转发”面板里点几下完成的,工具各式各样、参数互不相同;但底下究竟是哪种转发、为什么能这么干,这套理解换任何工具都通用。认得出”这是哪类转发”,远比记住某条命令有用。

原理不复杂:SSH 那条已经建好的加密连接,不止能跑你的 shell;你还能在它里头额外开”通道”,把某个本来够不着的端口 / 服务,搬运到连接的另一端来访问。这就是”端口转发”,也叫”SSH 隧道”——本质是借这条现成的加密隧道,做一次”端口搬运”。

为什么有用?因为现实里常是这种局面:你想访问的那个服务,你直接连不上(在内网、没开放端口、被防火墙挡着),但另一台你能 SSH 上去的机器,却连得上它。端口转发就是借那台机器当中转,把服务”引”到你面前——而且全程在加密隧道里走。

转发分三种,对应三类场景。记的时候别背命令,盯住一个问题:你要访问的那个服务,在隧道的哪一头?

7.1 本地转发:把对端的服务,拉到我面前

最常用的一种。你要的服务在对端那侧(SSH 服务器连得到、你直接连不到),你想在自己机器上访问它。

典型场景:生产库 db.internal:5432 不对公网开放,只有跳板机连得进去。你 SSH 上跳板机、开一条本地转发,就能在自己笔记本上像连本地一样连那台内网库——流量从你本机出发,钻进加密隧道,到跳板机那头再转发给内网库。

判断标志:服务在远端,我开个口子把它拉过来。(命令行里它是 -L,Local;工具面板里常叫”本地转发 / Local Forward”。)

7.2 远程转发:把我这边的服务,推到对端去

方向正好反过来。你要暴露的服务在你这侧,你想让对端、或对端能接触到的人,访问到它。

典型场景:你本机正开发一个跑在 localhost:3000 的服务,想临时让一台公网服务器那边的同事访问;又或者一台出不了网、也连不进来的内网机器,主动 SSH 连到外部跳板机、把自己的某个端口”反向”挂上去,好让你之后能从跳板机这头够到它。

判断标志:服务在我这端,我把它推到远端去。(命令行里是 -R,Remote;面板里常叫”远程转发 / Remote Forward”。一个常见坑:这个挂在远端的口子,默认只在远端本机(loopback)监听,别人连不到,要对外还得额外放开——留到下篇细说。)

7.3 动态转发:不指定服务,整体走隧道出去

前两种都是为”某一个具体服务”开一条专线。动态转发不一样:它在你本机起一个 SOCKS 代理,凡是配置了走这个代理的程序(比如浏览器),流量整体钻进 SSH 隧道、从对端那台机器出网。

典型场景:你想让浏览器临时”经由某台服务器上网”——访问只对那台服务器开放的内部站点,或者让出口流量看起来从那台机器发出。一次性给浏览器套个隧道,不必为每个目标单独开转发。

判断标志:不针对某个具体服务,而是给一类流量整体开个出口。(命令行里是 -D,Dynamic;本质就是一个 SOCKS 代理。)

7.4 怎么不再记混:一个问题定方向

三种里,-D 动态转发是独立的——它就是个 SOCKS 代理,给一类流量整体开出口,不和谁纠缠,单记即可。真正让人记混的,是 -L-R。破解它,只问一个问题就够

你眼下缺的那个服务,在隧道的哪一头?

  • 服务在对端(我够不着)→ 用 -L 本地转发。 我在自己本地开一个入口,把对端的服务接过来——我去取
  • 服务在我本地(想给对端用)→ 用 -R 远程转发。 我在对端开一个入口,把本地的服务送过去——我去送

记忆的锚点只有一条事实:**-L/-R 那个字母,指的就是”入口(监听口)开在哪一端”——L = Local 开在本地,R = Remote 开在远端。再配一句话把方向锁死:入口永远开在”使用者”那一侧。** 我要用对端的服务,入口就在我本地(-L);对端的人要用我的服务,入口就在对端(-R)。

方向理顺了,剩下的交给工具:无论堡垒机自带的转发功能、还是客户端的隧道面板,选项再花哨,先问自己”我缺的服务在哪头”,对上 L 或 R,照着填就行。

密钥、agent、配置、隧道——单机单人怎么把 SSH 用得又安全又顺手,到这儿基本齐了。但这些工具有个共同的隐含前提:你一个人,管得过来。可一旦机器从几台变成几百台、用的人从你一个变成一支团队,这个前提就垮了——纯靠 known_hostsauthorized_keys 逐把维护,”安全”和”顺手”又一次掰起手腕:要安全就得逐台核对、逐台分发,烦到没人做得到;图省事就闭眼敲 yes、钥匙乱塞,安全荡然无存。老办法在规模面前两头不讨好,逼出了下一个问题:信任怎么规模化? 这就是下一节要谈的 SSH 证书。

增加了关于参数的理解的图

八、SSH 证书:把信任从”逐把钥匙”上移到”一个 CA”

先看清楚,到底是什么扛不住了。单机单人时,SSH 的信任靠两个文件手工维护,规模一上来,两头都崩:

  • 主机这头(known_hosts:每台新机器第一次连,都要你肉眼核对指纹、攒进 known_hosts。几百台机器、还时不时重装换 host key——结果要么大家闭眼狂敲 yes(上篇说的 TOFU 形同虚设),要么某台一换 key 就满屏 REMOTE HOST IDENTIFICATION HAS CHANGED,运维忙着到处删 known_hosts 旧行。
  • 用户这头(authorized_keys:来个新人、或换把新钥匙,就得往每一台服务器的 authorized_keys 里塞公钥;人走了,又得从每一台删干净。几百台 × 一支团队,是维护地狱,而且极易漏删——离职半年,钥匙还在某台机器上躺着。

根子在于:known_hostsauthorized_keys 都是”逐把钥匙“地建立信任。钥匙越多、机器越多,要维护的信任关系就是个乘积,迟早失控。

SSH 证书换了个思路:不再逐把信任钥匙,而是只信任一个 CA。 这里的 CA(证书颁发机构)不神秘——它就是一对普通的 SSH 密钥。CA 用自己的私钥,给某把主机公钥或用户公钥”签个名”,签出来的东西就叫证书。谁信任这个 CA,就自动信任所有被它签过名的钥匙。

1
ssh-keygen -t ed25519 -f ssh_ca          # 建一个 CA(其实就是生成一对密钥)

于是两头的维护地狱都化掉了:

  • 主机证书:给每台服务器的主机公钥签一张证书(签命令多带一个 -h 表示主机),让 sshd 用 HostCertificate 把它亮出来。客户端只需在 known_hosts 里写一行 @cert-authority *.example.com <CA公钥>,声明”我信任这个 CA”——此后连任何一台被它签过的机器,都不再 TOFU、不再逐台核对;host key 轮换了,只要重新签张证书,客户端那头悄无声息,再不会满屏报错。
  • 用户证书:给用户公钥签证书(不带 -h),服务器在 sshd_config 里写一行 TrustedUserCAKeys /etc/ssh/user_ca.pub,声明”我信任这个 CA 签的用户”。从此任何持有效证书的人都能登录,**不必再往每台机器分发 authorized_keys**;人走了,停止给他续签即可,不用逐台清钥匙。
1
2
ssh-keygen -s ssh_ca -I ethan -n ethan,deploy -V +8h  id_ed25519.pub
# └签发用的CA └证书标识 └允许登的账号(principals) └8小时后过期 └被签的用户公钥

证书比裸钥匙强的地方,正在那几个能”刻进证书”的约束:principals(这张证书允许登哪些账号)、有效期-V,能签成几小时就作废的短时证书)、来源地址限制、强制命令。其中有效期是关键的一跃:长期静态钥匙一旦泄露,你往往根本不知道,它会一直有效;而短时证书”过期即废”,把”钥匙泄露”的危险窗口从”永远”压缩到”几小时”。

这套东西对做信创运维的你尤其眼熟,因为现代堡垒机的内核就是它:你刷脸/扫码通过堡垒机的身份核验后,它背后的 CA 当场给你签一张”几小时有效、只准登某几台机器”的短时用户证书,你揣着它去登后端机器;后端机器只认这个 CA。你日常感受到的”进了堡垒机就能登一片机器、下班自动失效”,底下跑的就是 SSH 证书。

代价也得说清楚:信任集中到 CA,方便的另一面是——CA 私钥成了单点要害,它一旦泄露,等于整个信任域同时沦陷。所以 CA 私钥必须离线、严加保管,签发流程要管控。这是”规模化便利”的对价。

具体配置(@cert-authorityTrustedUserCAKeysHostCertificate 的摆放)多数人是在堡垒机后面享受现成的,无需手搓;真要自建,man ssh-keygen 的 CERTIFICATES 一节最权威。这里你只要记住那个跃迁:**信任从”逐把钥匙”上移到”一个 CA”**——这才是规模化的钥匙。它又一次印证了那条暗线:短时证书”过期即废”是最安全的做法,偏偏它同时免去了逐台分发、离职清钥的苦力——安全与顺手,再一次被一件对的工具拧成了同一件事。

九、加固 sshd:把不该进的挡在门外,把攻击面缩到最小

到这里,钥匙、agent、配置、隧道、证书,解决的都是同一件事的正面:让对的人方便又安全地进来。 但还有反面没管——sshd 的默认配置,是为”开箱即能连通”调的,不是为”最小攻击面”调的。认证做对之后的最后一步,是反过来主动收紧这扇门。

下面这几行 sshd_config,是绝大多数生产机都该设的底线:

1
2
3
4
5
6
PasswordAuthentication no
PermitRootLogin no
AllowUsers alice bob
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30

逐条说为什么:

只认公钥,关掉密码登录(PasswordAuthentication no)。 它的默认值是 yes,于是你的 22 端口天天被全网扫号、撞库。关掉它,攻击者没有你的私钥就根本撞不动,绝大多数自动化爆破当场失效。这是性价比最高的一条。前提:先确认你的公钥确实能登进去,再关,否则就把自己锁外面了。

禁掉 root 直接登录(PermitRootLogin no)。 这里有个普遍误解要破:现代 OpenSSH(7.0 起,2015 年)的默认值早已是 prohibit-password,root 已经不能用密码登了——但仍然能用密钥登。要彻底,就显式写成 no:所有人都以普通账号登录、再 sudo 提权。好处一是 root 是全网爆破的头号目标(用户名都不用猜),二是”谁动了 root”能在日志里追溯到具体某个人,而不是一笔糊涂账。

限定谁能登(AllowUsers / AllowGroups)。 默认任何系统账号都能来尝试认证;列一份白名单,等于在认证之前就把绝大多数账号挡在门外。

缩小攻击面:用不到的功能就关掉。 不需要图形转发,X11Forwarding no;这台机器不打算当跳板,就 AllowTcpForwarding noAllowAgentForwarding no。每关掉一个用不上的能力,攻击者能利用的入口就少一个。(agent 转发为什么尤其危险,下篇专门算这笔账。)

给爆破上点成本(MaxAuthTriesLoginGraceTime)。 前者限一次连接最多试几次认证,后者限多少秒内没认证完就断开;再配合 fail2ban 这类工具,对反复失败的 IP 临时封禁。这些挡不住有钥匙的人,但能把无脑爆破的噪音狠狠压下去。

然后破一个最流行的”伪加固”:

改端口 ≠ 加固。 把 22 改成别的端口,确实能让日志清净很多(自动扫描器多半只盯 22)——但这只是”减少噪音”,不是”增加防护”。端口号根本不是秘密,真正盯上你的人一扫便知;把改端口当成安全措施,是典型的”换个地方藏钥匙”式自我安慰。改,可以,图个清净;但千万别让它替掉上面那几条真正管用的。

收紧这扇门,乍看是全篇头一回让”顺手”给”安全”让路——关掉一个功能,总归少一份方便。但诀窍正在这儿:你关的全是自己用不上的能力(密码登录、root 直登、图形转发……),于是攻击面小了,日常却纹丝不动。唯独有一类改动碰不得马虎——认证相关项。

所以最后两句务必记住,它们也是下篇的开场伏笔:

一是任何改动都要 reload / restart sshd 才生效;

二是改认证相关项时,永远先开一个新会话验证能登进去,再关掉手里这个旧会话。

否则一行配置写错,你就被自己锁在门外了——而这,恰恰是下篇要讲的头号坑。

十、写在中篇结尾

“用好 SSH”这条线,到这里就走完了。回头看,它其实是一条处境升级的阶梯:一个人一把钥匙,到天天要用几十遍、敲几十条长命令,到要够内网里够不着的机器,再到几百台机器、一支团队——每升一级,就有一件工具接手:密钥、agent、配置、隧道、证书、加固。

而这一路还埋着一条暗线:你每次觉得”要安全就得忍受麻烦”,几乎都是错觉——那只说明你还没用上对的工具。设了 passphrase 又不想反复输,agent 接住;嫌长命令难记而想共用弱配置,config 接住;几百台机器逐把维护信任要人命,证书接住;门户大开不安全、全锁死又没法干活,加固只替你关掉用不上的那些。

一件件工具到位之后,”安全”和”顺手”从来不是二选一,它们本就是同一件事。

这,才是”把 SSH 用得既安全又顺手”这个标题真正的分量。

但凡动手的人都清楚,真实世界从不照理想剧本走:钥匙换了登不进、host key 变了报一串吓人的错、明明配了密钥却还在问密码、登录莫名卡半天、改完配置把自己关在门外……这些坑,几乎每个用 SSH 的人都踩过。

下篇就专门收这些——教你在”登不进、连不上、被锁门外”时,怎么靠 ssh -v 把真相一层层挖出来。

上篇讲内核、中篇讲用好、下篇讲避坑。三篇合起来,SSH 这道协议,才算真正”炼成”。