0%

协议是怎样炼成的 SSH下篇

《协议是怎样炼成的》· SSH(下篇) · 避坑:报错会骗你,握手不会

上篇把握手拆开给你看,中篇教你把它用好。这一篇讲的是:当它出问题时,怎么修。

而 SSH 的报错,是出了名的”嘴硬”。你输错了、权限不对、密钥没被接受,它统统只回你一句冷冰冰的 Permission denied (publickey)——到底差在哪,一个字都不肯多说。这不是它做得烂,而是故意的:把失败的真正原因告诉你,等于也告诉了正在试探的攻击者”再换个姿势还有戏”。所以对外,它永远只给一句最笼统的话。

这就决定了:排查 SSH,靠”背报错对照表”是走不远的——同一句 Permission denied,背后可能是十种毛病。真正管用的办法,是让握手过程自己开口

你在上篇见过那条握手链:连上、协商算法、换密钥、主机认证、切换加密、用户认证、开会话。

几乎每一个故障,都是这条链上某一环断了。只要让它把每一步都念出来,看它断在第几步,毛病就现形了。

让它开口的工具,就一个:-v

一、先学会用 -v:把握手读成一条进度条

ssh 加上 -v,它就会把握手的每一步打印出来(-vv-vvv 更详细,一般 -v 就够定位):

1
ssh -v user@host

你不用读懂每一行,只要认得几个路标——它们正好对应上篇那条握手链:

  • debug1: Connecting to host [IP] port 22 —— 正在建 TCP 连接(传输层之前)。
  • debug1: Remote protocol version ... —— 版本握上了。
  • debug1: kex: algorithm: curve25519-sha256 ... —— 算法协商成功(上篇第一关)。
  • debug1: Server host key: ...debug1: Host 'host' is known ... —— 主机认证那一环(上篇第三关)。
  • debug1: Authentications that can continue: publickey,password —— 服务器告诉你它接受哪些认证方式。
  • debug1: Offering public key: ... / debug1: Server accepts key: ... —— 用户认证(上篇第四关):你在递钥匙、服务器认不认。
  • debug1: Authenticated to host ... —— 认证通过,开始会话。

-v 在哪一行停下、或在哪一行开始报错,就锁定了毛病在握手的哪一环。 这是下面所有排查的总纲:先看它走到哪儿断了,再去查那一环。

服务器那头也能开口。如果你有服务器权限,最直接的是临时用调试模式跑一个 sshd、只接一个连接看个究竟:

1
/usr/sbin/sshd -d -p 2222     # 前台、调试模式,监听在 2222,不影响正在跑的 sshd

或者直接翻认证日志——它会写明拒绝的真实原因(客户端那句 Permission denied 不肯说的,日志里往往写得清清楚楚):

1
2
journalctl -u sshd            # systemd 系统
# 或 /var/log/auth.log(Debian/Ubuntu/麒麟)、/var/log/secure(RHEL/统信)

工具齐了,顺着握手链从头往下走。

二、连不上:握手还没真正开始

2.1 根本没握上:Connection refused / 连接超时

-v 卡在 Connecting to ... 之后就不动、或直接报错,说明 TCP 都没通——问题还在 SSH 之前。两种典型:

  • Connection refused:能到那台机器,但 22 端口没人应。多半是 sshd 没在跑,或端口/监听地址不对(比如 sshd 改了 ListenAddress、或绑了别的端口)。上服务器 systemctl status sshdss -tlnp | grep ssh 看一眼。
  • 连接超时(卡很久才失败):包根本没到,或回不来。基本是网络/防火墙——本地防火墙、云上安全组、或中间网络没放行该端口。

判断口诀:**refused 是”敲了门没人开”,超时 是”门都没找到”。** 前者查服务端进程与端口,后者查网络与防火墙。

2.2 算法谈不拢:no matching host key type found / no matching key exchange method

1
Unable to negotiate with X.X.X.X port 22: no matching host key type found. Their offer: ssh-rsa

这是算法协商那一环(上篇第一关 KEXINIT)没谈拢:你的新客户端,和一台老服务器(老交换机、老存储、老打印机的管理口最常见),在”用哪套算法”上没有交集。最常见的导火索,正是中篇埋下的那个区分:

OpenSSH 8.8(2021)默认禁用了 ssh-rsa 这个用 SHA-1 的老签名算法。 这里要再强调一遍中篇那个关键区分——**ssh-rsa 指的是”用 SHA-1 的签名算法”,不是”RSA 密钥类型”。RSA 密钥本身完全没被淘汰**,被禁的只是那套老签名方式。新客户端碰上只会这套老签名的老设备,就谈不拢了。

应急(连单台老设备时,临时把老算法加回来):

1
ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa user@host

+ 是”在默认之上追加”,比直接写死整张算法表更稳妥。常连的话,把它写进 ~/.ssh/config 的对应 Host 段(只对那一台开口子,别全局放开——这毕竟是退回到较弱的算法)。no matching key exchange methodno matching cipher 是同一类病、同一个治法:缺哪类就用对应的 -oKexAlgorithms=+...-oCiphers=+... 临时补上。

但请记住:这些都是让你今天能连上的应急绷带,根治永远是升级那台老设备。绷带打多了会忘,弱算法就一直留在那儿了。

2.3 host key 变了:REMOTE HOST IDENTIFICATION HAS CHANGED!

握手走到主机认证那一环,突然蹦出一大段惊悚的警告:

1
2
3
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

回想上篇:你的客户端在 known_hosts 里记着这台机器的主机公钥;这次连上来的主机公钥对不上了。SSH 不知道是”服务器自己换了 key”还是”有人在中间冒充”,于是按最坏情况拉响警报、直接拒连。这正是上篇讲的主机认证在起作用——它宁可错杀,也不让你稀里糊涂连上一台身份变了的机器。

绝大多数时候,原因是良性的:服务器重装了系统、或换了主机密钥。但你得先确认这一点(问运维、核对新指纹),别盲目清掉——万一真是中间人,这一步是你唯一的预警。确认无误后,删掉旧记录再重连即可:

1
2
ssh-keygen -R host                  # 删掉 known_hosts 里这台主机的旧行
ssh-keygen -R "[host]:2222" # 非标准端口要带方括号和端口

(如果整个机房刚集体重装,用证书的主机认证就能免掉这种满地清 known_hosts 的活儿——参见中篇的 SSH 证书。)

三、登不进:卡在认证那一环

握手已经走过主机认证、切到加密信道,却倒在用户认证上——-v 会显示 Authentications that can continue,然后你的钥匙被一一拒绝,最后是那句最不肯透底的:

1
Permission denied (publickey).

3.1 Permission denied (publickey):先查权限,再查别的

同一句报错背后一堆可能,按”最常被忽略、最坑”的顺序排查:

  1. 文件权限太松(头号隐形坑)。 sshd 默认开着 StrictModes:登录前它会检查你家目录和 ~/.ssh 的属主与权限,只要太开放,它就当作不安全、直接拒绝——而且对客户端只回一句 Permission denied,真正原因(Authentication refused: bad ownership or modes for ...)只写在服务器日志里。所以这类坑,光看客户端永远猜不到,必须翻服务端日志。正确权限:

    1
    2
    3
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    chmod 600 ~/.ssh/id_ed25519 # 私钥

    还有个容易漏的:家目录本身不能被同组或其他人写chmod g-w,o-w ~),否则同样被 StrictModes 否掉。

  2. **公钥没真正进到服务器的 authorized_keys**,或路径不对(自定义了 AuthorizedKeysFile)。

  3. 私钥没加载:如果用了 agent,确认钥匙在里头(ssh-add -l,回想中篇);或干脆 -i 显式指定私钥。

  4. 服务端没开公钥认证PubkeyAuthentication no),或你这个账号被 AllowUsers/AllowGroups 挡在外面(回想中篇加固那节——你自己设的白名单别把自己漏了)。

排查时,客户端 -v 看”它到底递没递这把钥匙、服务器接没接”,服务端日志看”它为什么拒”。两头一对,真相立现。

3.2 明明配了密钥,却还在反复问密码

这其实是上一条的”温柔版”:你的公钥认证悄悄失败了,sshd 按 Authentications that can continue 里的下一个方式回退到了密码。所以现象是”问密码”,病根还是”公钥没被接受”——十有八九又是上面那个权限问题,或公钥没进对地方。

别急着输密码,先加 -v 看公钥那一步发生了什么:看到 Offering public key 之后服务器没 accept、甚至出现 send_pubkey_test: no mutual signature algorithm,那又是 ssh-rsa 签名被禁那一类(见上一节)。把”为什么回退到密码”查清楚,比输那个密码重要得多。

3.3 Too many authentication failures

1
Received disconnect ... Too many authentication failures

这条常让人莫名其妙——明明有对的钥匙,怎么还说我试太多次?根因往往是:你的 agent 里装了一大把钥匙,ssh 会挨个拿去试,还没轮到那把对的,就先撞满了服务器的 MaxAuthTries(默认 6),被踹下线。

治法是让它别乱试,只用你指定的那把:

1
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 user@host

IdentitiesOnly=yes 的意思是”只用我 -i 指定的钥匙,别把 agent 里那一堆都拿来挨个试”。常连的主机,把这条连同 IdentityFile 写进 ~/.ssh/config 一劳永逸(这也是中篇 agent 那节提过的搭配)。

3.4 登录卡半天才给提示符:慢登录

慢登录最折磨人的地方在于——**它不是”登不进”,是”登得进、但要等”**。认证最终会成功,提示符也会出来,只是中间莫名卡十几秒到半分钟。正因为最后成功了,很多人就忍了,其实它几乎总是两个固定的”超时黑洞”之一,而且 ssh -v 能精确告诉你卡在哪。

判断方法:连接时加 -v,盯住哪两行日志之间出现长时间停顿。 卡顿就发生在停顿之前那一步——日志会一直刷,刷到某一行突然不动了,等十几秒才继续,那一行就是元凶。

黑洞一:反向 DNS 查询(最常见)。 10 到 30 秒的登录延迟,几乎都是反向 DNS 查询造成的。服务端收到你的连接后,默认会拿你的客户端 IP 反查主机名(PTR 记录),再用这个主机名正向查一次、验证能不能解析回同一个 IP。一旦 DNS 服务器慢、不可达,或者根本没有 PTR 记录,连接就会一直挂着,直到查询超时才继续。控制这个行为的开关是 sshd_config 里的 **UseDNS**(默认关闭)。

1
UseDNS no

对于不依赖反向 DNS 验证的环境,关掉它不会降低安全性——这正是中篇那条暗线的又一次印证:这个”安全检查”在你的场景里既挡不住真攻击者(IP 可伪造),又天天拖慢你,关掉它是安全和顺手双赢。内核邮件列表里甚至抓到过实锤包:服务端发了两次反向 DNS 请求、中间隔 5 秒——那 10 秒就这么白等掉的。

黑洞二:GSSAPI 认证协商。 如果关了 UseDNS 延迟还在,GSSAPI 认证就是下一个嫌疑犯——它(为 Kerberos 服务)在认证握手时同样会触发 DNS 查询。-v 下它的特征非常好认,你会看到 ssh 在正式问你密码/公钥之前,先去尝试 gssapi 方法、然后失败。

1
2
debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure.

这两行就是 GSSAPI 在空转——它先试 gssapi 方法,失败了,才轮到 publickey/password。客户端可临时 ssh -o GSSAPIAuthentication=no,服务端永久关闭则在 sshd_config

1
GSSAPIAuthentication no

总结: 把 UseDNS 设为 no、再关掉 GSSAPI 认证,能解决绝大多数 SSH 慢登录;对不依赖反向 DNS 或 Kerberos 的环境,这两项改动都不影响安全。改完照例 reload/restart sshd。

补充:GSSAPI(Kerberos)协商到底是什么?

因为它在 -v 里很显眼、却被绝大多数人无视。

GSSAPI 是 Generic Security Services API 的缩写,是一套通用的安全认证接口标准;Kerberos 是它背后最主流的那套认证体系——就是 Windows 活动目录(AD)域里”一次登录、全域通行”的那套票据机制(单点登录 SSO)。

SSH 支持一种认证方式叫 gssapi-with-mic,意思是:如果你所在的环境有 Kerberos/AD 域,你登 SSH 时可以不输密码、也不用公钥,直接拿你域登录时领到的”票据”(ticket)来证明身份。 这在大型企业域环境里很方便,是真正的杀手锏功能。

但关键在于:绝大多数信创/Linux 服务器并不在 Kerberos 域里。 而 OpenSSH 默认又常把 GSSAPI 认证摆在认证方法列表的前面。于是每次登录,ssh 都会先一本正经地去尝试 Kerberos——找票据、(有时还伴随 DNS 查询找 KDC),折腾一圈发现根本没有域环境,报一句 Unspecified GSS failure,才退回去走你真正在用的公钥或密码。你等的那一会儿,就是它在为一个你压根没用的功能空转。

所以对不在域里的机器,GSSAPIAuthentication no 是无脑该关的——它关掉的不是你在用的能力,而是一个你用不上、却每次都要试一遍的累赘。这和加固那一节”只关自己用不上的功能”是同一个道理:攻击面和等待时间一起少了,日常却纹丝不动。

一句话记住 GSSAPI: 它是给”域环境单点登录”准备的高级通道;你不在域里,它就只是一段每次登录都要空跑、最后必然失败的前奏——关掉它,等于把这段前奏直接跳过。

四、登进去了,但行为不对:连接层的坑

人进来了,握手和认证都没问题,毛病出在上篇第四关之后的”会话/通道”层。

4.1 隧道建好了吗?它在哪、怎么拆

先立一个心智模型,这一节后面所有的坑都站在它上面:隧道不是系统里的持久对象,它就是那个 ssh 进程本身——进程活,隧道活;进程死,隧道亡。系统里没有一条”删除隧道”的命令,因为根本没有独立于进程的”隧道”可删。

由此推出三件日常事:

一、建立。 ssh -L 13306:10.0.0.8:3306 ops@bastion 敲下去,你得到的不是”无返回”,而是一个正常的 bastion 登录 shell——提示符照样出现,隧道只是这次登录”顺带”建起来的。如果你只要隧道、不要 shell,加 -N(不执行远程命令):

1
ssh -N -L 13306:10.0.0.8:3306 ops@bastion

这时终端才是真的”挂着不动”——光标停那儿不是卡了,是隧道正在干活。再要丢到后台,加 -f

二、验证。 隧道建没建成,看入口的监听口在不在——而入口开在哪头,-L/-R 是镜像的:

1
2
3
4
5
# -L:入口在我本机,本机查
ss -tlnp | grep 13306

# -R:入口在对端,要到对端查(在你本机永远查不到)
ssh ops@vps 'ss -tlnp | grep 18080'

初学者最常见的误判就在 -R 上:本机一查没有 18080,以为隧道没建起来——口子本来就不在你这台机器上。

-R 还有个更阴的坑:它会静默失败。对端那个口已被占用时,你的登录照样成功,只在登录输出里夹一行:

1
Warning: remote port forwarding failed for listen port 18080

刷屏一快根本看不见,人还以为隧道好了。这又是那句话——登录成功会骗你,那行 Warning 不会-L 反而老实:本地端口被占,当场报错拒绝启动。

三、拆除。 既然隧道=进程,拆隧道=结束进程,对号入座:

  • 带 shell 的:在那个会话里 exit(或 Ctrl-D);会话卡死退不出,新起一行敲 ~.(SSH 逃逸序列,客户端立即断开)。
  • -N 前台挂着的:在那个终端 Ctrl-C。
  • -f -N 后台的:pgrep -af 'ssh.*13306' 找到 PID,kill 之。

拆完同样用上面的 ss 验证口子已释放。一个小现象别慌:kill 之后立刻重建同名隧道,偶尔报端口被占——旧连接还没完全断干净,等一两秒再建即可;这个现象在 -R 上更常见,因为对端口子要等那头的 sshd 察觉连接已断才释放。

(生产里要”永远在线”的隧道,靠的不是手工 ssh 而是 systemd 服务或 autossh 拉活——那是部署话题,超出本篇排障范围,按下不表。)

4.2 -R 远程转发,别人却连不上

这是中篇远程转发那节我留的坑:你用 -R 把一个端口挂到了远端,自己在远端本机能连,别人却连不上。因为 -R 挂出来的监听口,默认只绑在远端的 loopback(127.0.0.1),纯粹是出于安全——不让你随手就把一个端口暴露给整个网络。

参数恒为「入口 : 服务地址 : 服务端口」|L = 入口开在本地,R = 入口开在对端

要对外,得两边都松口:服务端 sshd_configGatewayPorts yes(或 clientspecified),客户端把绑定地址写成 0.0.0.0

1
ssh -R 0.0.0.0:8080:localhost:80 user@gateway

这是有意为之的安全默认,不是 bug。 真要对外暴露,想清楚那个口子谁能碰到。

4.3 还是没想通 -R?口子凭什么开在对面

如果你对 -R 始终隔着一层——“明明是我发起的连接,凭什么口子开在对面?GatewayPorts 又是谁家的参数?”——这一小节把这层窗户纸捅破。

用一个具体场景走一遍。你家里一台电脑跑着网页服务(8080 端口),没有公网 IP;你在云上有台 VPS。想让外面的人经 VPS 访问到家里的 8080,你在家里电脑上敲:

1
ssh -R 18080:localhost:8080 ops@vps

注意,这首先就是一次普普通通的 SSH 登录——从家里连到 VPS,方向和你平时登服务器一模一样。-R 只是让你在登录时顺嘴拜托了一件事,等于对 VPS 上的 sshd 说:

“兄弟,我登上来了。麻烦你在你那边开一个 18080 替我守着。以后谁连你的 18080,你就把流量顺着咱俩这条已建好的加密连接倒灌回我这儿,我转给本地的 8080。”

看清楚了:那个 18080 的口子,是 VPS 上的 sshd 应你的请求、开在它自己身上的。 不是你隔空在别人机器上凿了个洞——监听、收流量这些活,全是对端 sshd 在干;你这头的 ssh 进程只负责接住倒灌回来的流量、转给本地服务。这一下,前面两件”怪事”全通了:为什么你本机 ss 查不到 18080(口子根本不在你机器上);为什么验证隧道生死要去对端查(口子的”尸体”也在对端)。

再看 GatewayPorts 是谁家的参数——它是 VPS 上 sshd 的”家规”,住在 VPS 的 /etc/ssh/sshd_config 里,和你客户端的命令行是两个世界。它管的事一句话:“客人拜托我开的口子,我允许朝谁开?”

  • no(默认):”口子可以开,但只开在我的 127.0.0.1 上——只有登在我这台机器上的人能用,绝不朝公网开。”所以你在命令里写不写 0.0.0.0,它都当没听见。这是 sshd 替机器兜底:别人一条命令就能在我身上开公网口子?不行,得我的管理员改配置点头。
  • yes:”客人要开的口子,一律朝全网开。”(你写啥都一样。)
  • clientspecified:”客人说朝哪开就朝哪开。”——只有这一档,你命令里的 0.0.0.0: 才真正说了算。

所以 -R 的全链条上站着两个门卫:你的命令行是申请(”帮我开个口,最好朝公网”),对端 sshd 的 GatewayPorts审批(”按我家规来”)。申请几乎不会失败,但审批可能降级——而且降级不报错,口子悄悄落回 loopback。这正是上一小节那个坑的全部来历,也又一次应了那句话:命令”成功”会骗你,去对端 ss 一眼监听地址,不会。

最后,两分钟实操,胜过读十遍(找两台能 ssh 互通的机器,A 当”家”,B 当”VPS”):

1
2
3
4
5
6
7
8
9
10
# 在 A 上:随便起个服务,再挂 -R
python3 -m http.server 8080 &
ssh -R 18080:localhost:8080 user@B

# 登上 B 之后,在 B 上看三件事:
ss -tlnp | grep 18080 # 看到 127.0.0.1:18080 —— 口子在 B,且绑 loopback
curl localhost:18080 # 通!内容就是 A 上 8080 的页面
exit # 退出登录

# 退出后在 B 上再 ss 一次 —— 18080 没了:隧道=进程,进程亡口子亡

跑完这四步,-R 的方向感、口子在哪、GatewayPorts 管什么、隧道的生死,全部落地。

4.4 AuthenticationMethods 的逗号是”与”,不是”或”

想做多因子、要求”公钥 + 密码都过”时,最容易栽在这条的语义上:

1
AuthenticationMethods publickey,password

逗号分隔的,是”全都要完成”(与)——上面这行要求你先过公钥、再过密码,两个都成功才放行。很多人误以为是”二选一”,配下去发现明明公钥对了还登不进,就是把”与”看成了”或”。真要”或”(任一即可),得用空格分隔成多个列表:publickey password 才是”公钥或密码”。改这条之前,务必按下一节的铁律留好后路。

4.5 agent 转发:中篇欠下的那笔账

中篇说过,把 agent 转发到别的机器很危险,这里把账算清。ForwardAgent yes(或 ssh -A)会把你本机 agent 的那个 socket(SSH_AUTH_SOCK,记得它吗)暴露到你登上去的那台远端机器。好处是你能从远端再跳下一台、复用本地钥匙不必落地私钥;但代价是——只要那台远端机器被人控制(哪怕只是 root),他就能在你连着的这段时间里,借用你的 agent 替他签名,去登录任何信任你的机器。你的私钥虽然没泄露,但”用私钥签名”这个能力被人借走了,效果一样致命。

所以:agent 转发只对你完全信任的机器开,且开则用、用完即断;能不转发就不转发。要做多级跳转,优先用中篇提过的 ProxyJumpssh -J 跳板 目标)——它在跳板上只做转发、根本不碰你的 agent,从机制上就没有这个风险。

五、最凶的坑:把自己锁在门外

前面所有坑,最坏是连不上、登不进,重试就好。只有这一类是灾难级的:你在远程改 sshd 配置、一 restart,新配置把你自己挡在了门外——PasswordAuthentication no 但公钥还没配好、AllowUsers 漏了自己、改了端口忘了放行防火墙……而你手上没有别的进机器的路

记住三条铁律,这类事故几乎可以归零:

  1. 改完先验语法再生效。 sshd 有自带的配置体检:

    1
    sshd -t          # 检查 sshd_config 语法,有错会指出行号;没输出就是通过
  2. 永远留一条活路。 改认证相关项时,保持当前这个会话不要关,另开一个终端去登一次,确认能进,再回头关掉旧会话。无论 reload 还是 restart,真正会要你命的不是”它踢掉你当前的连接”(已建立的会话通常还活着),而是”新配置让你再也建不了新连接“——所以那个能成功的新会话,就是你的验证和保险。

  3. 分清 reloadrestart systemctl reload sshd 让 sshd 重读配置、对已有连接更温和;restart 是整个守护进程重启。日常改配置优先 reload。但无论哪个,第 2 条都不能省。

万一真锁死了,就只剩带外的路:物理终端、云厂商控制台的 VNC/串口、或带外管理卡(IPMI/BMC)。所以——别在唯一一条 SSH 通道上,赌一次没验证过的配置改动。

六、写在三部曲结尾

回头看这一篇做的全部事情,其实只有一件:拿着上篇那张握手时序图,对照 -v 的输出,看它停在第几步。

  • 连不上 —— 握手还没真正开始(TCP、网络、host key、算法协商);
  • 登不进 —— 倒在用户认证那一环(权限、公钥、回退、重试超限);
  • 行为不对 —— 卡在认证之后的会话与转发层;
  • 锁门外 —— 服务端配置把门焊死了。

每一个坑,都能在握手链上找到它对应的那一环。报错文本会骗你——那句 Permission denied 什么都不肯说;但握手不会骗你,-v 把每一步摊开,断点就是病灶。 这,就是这一篇真正想交给你的东西:不是一张报错对照表,而是一套”顺着握手链定位”的排障直觉。

三篇到此合龙。上篇问”它到底是什么”——答案是认证,一次握手、两个方向的相互验明正身;中篇问”怎么把它用好、用安全”——钥匙、agent、隧道、证书、加固;下篇问”它坏了怎么办”——让握手自己开口。这三个问题答完,SSH 就不再是一个你天天敲、却从没真正看清的命令,而是一个你能讲清、用稳、修好的协议。

《协议是怎样炼成的》——SSH 这一炉,到此出炉。