简介:OpenSSH 8.8p1 源代码包面向运维、安全与嵌入式开发人员,提供安全远程登录、加密文件传输和密钥管理套件的完整实现,可用于源码编译升级、安全补丁验证及协议实现研究。压缩包共 854 个文件,约 1.73MB,以 C 源码(287 个)和头文件(123 个)为主体,另含 Shell 脚本、Makefile、公钥样例、证书与配置测试文件,覆盖 sshd、ssh、scp、sftp、ssh-keygen、ssh-agent 等核心组件,并附带命令行手册页与配置说明。已有 344 人学习下载,适合对照官方发布流程完成编译安装,也可深入阅读密钥交换、身份认证与加密算法实现。包内测试脚本和配置模板有助于搭建实验环境、验证系统兼容性,并排查连接失败、权限设置与密钥认证等常见问题。对日常部署维护、版本升级和代码审计均有参考价值,也便于理解各组件之间的调用关系与安全边界。
1. openssh-8.8p1.tar.gz 究竟解决了什么问题
运维交接时拿到一台跑了七八年的跳板机,ssh -V一敲,输出停在 OpenSSH_7.4p1,安全扫描报告红了一片,整改项写着「SSH 服务端版本过低、支持弱算法」。想升级又不敢动,因为远程登录是唯一入口,sshd 一旦起不来,人就只能抱着键盘往机房跑。openssh-8.8p1.tar.gz面对的正是这种局面:它把 ssh 客户端、sshd 服务端、ssh-keygen、sftp-server 打在一个包里,编译安装后能原地替换旧版本,不必受发行版仓库里那个可能早已冻结的包牵制。它适合三类人——需要自己卡加密算法白名单的、需要把内网几十台机器 SSH 版本拉齐到同一基线的、以及离线环境里根本用不了包管理器的。顺带提一句,搜Openssh-8.8sp1的多半是手滑多打了个 s,包名以 tar.gz 里的为准。不是所有人都该编译源码,但只要合规扫描器盯着你的机器,下面这些步骤迟早要过一遍。
2. 编译前的取舍:从 tar.gz 到一个能跑的 sshd 要走几步
拿到源码包先别急着./configure && make。OpenSSH 的构建系统有几个默认值会直接影响后面的运维方式,比如配置目录装在哪、特权分离目录用哪个、要不要链 PAM。这些在第一分钟定错,后面要么重编,要么在配置文件里打补丁打到怀疑人生。
2.1 什么情况下值得自己编译,什么情况别折腾
先说结论:能走包管理器就走包管理器。发行版维护的 openssh 包会跟随安全公告打补丁,依赖关系也自动解决,升级一条命令的事。自己编译拿到的最大好处是版本确定和参数可控,代价是后续每个 CVE 都得自己盯、自己重编、自己再走一遍替换流程。
我一般按三条标准判断:第一,安全扫描明确要求服务端版本不低于某个号,而仓库里没有;第二,需要精确控制KexAlgorithms、Ciphers、PubkeyAcceptedAlgorithms这几项白名单,且不能接受发行版默认值;第三,目标机器在内网隔离区,连包源都够不着。三条中两条成立,编译就是划算的。反过来,如果只是家用小主机或者临时测试机,为了一个版本号折腾半天不划算。
还有一个容易被忽略的判断维度:这台机器是不是你唯一的登录入口。如果是,编译方案的复杂度要按「出事之后怎么救」来倒推——你需要一个带外通道、一个已登录且不退出的会话、或者一个恰好还活着的旧 sshd 进程。这三样至少占一样,才谈得上动手。
2.2 解包后先认路:几个必须打开的目录和文件
解包之后不要直接cd进去敲 configure,先花十分钟把这几个位置看一遍,能省掉后面一半的疑问。
| 路径 | 作用 | 是否需要动 |
|---|---|---|
INSTALL | 官方构建说明,含 configure 全部开关 | 通读一遍 |
README | 版本信息与平台支持列表 | 扫一眼 |
configure | 构建脚本,参数决定产物形态 | 只传参不改 |
sshd_config | 服务端配置模板,含全部注释项 | 参考,不直接用 |
sshd_config.5 | 配置项手册,查默认值最快 | 反复查 |
contrib/ | 各发行版的 init 脚本、PAM 配置样例 | 有 PAM 需求必看 |
openbsd-compat/ | 跨平台兼容层 | 编译报错时排查 |
sshd_config.5这份 man 源文件值得单独说。网上流传的配置模板大量来自 6.x、7.x 时代,里面有些指令在 8.8p1 里已经不存在了,直接抄会启动失败。判断某个指令还认不认,最快的办法就是man 5 sshd_config,或者直接grep这份 man 源文件。养成这个习惯之后,别人给你的配置模板你会先怀疑一遍,而不是先上线再怀疑。
contrib/目录同样有用。里面通常有适配各系统的启动脚本和 PAM 配置文件样例,如果你的目标机启用了 PAM 认证,这里的sshd.pam样例比你自己从零写省事得多,且能避开「认证走通但会话没建立」这类不好查的问题。
2.3 configure 参数:五个真正需要你拍板的开关
参数有一大堆,绝大多数保持默认就行。真正需要你主动决定的只有下面这几个,其余交给自动探测。
# 解包并进入目录 tar -xzf openssh-8.8p1.tar.gz cd openssh-8.8p1 # 关键:先确认依赖头文件在不在,避免 configure 跑到一半退出 ls /usr/include/openssl/ssl.h /usr/include/zlib.h /usr/include/security/pam_appl.h ./configure \ --prefix=/usr \ --sysconfdir=/etc/ssh \ --with-ssl-dir=/usr \ --with-zlib=/usr \ --with-pam \ --with-privsep-path=/var/lib/sshd \ --with-pid-dir=/run逐个说清楚:
--prefix=/usr决定二进制装到/usr/bin和/usr/sbin。别改成/usr/local,否则系统里会同时存在两套 ssh,which ssh和实际被调用的那个经常对不上,排查时白白浪费半小时。
--sysconfdir=/etc/ssh是配置文件目录。这一项决定了make install会往哪里写sshd_config,也决定了后续所有-f参数怎么写。保持和系统原有目录一致,能让systemctl restart后的行为符合预期。
--with-ssl-dir=/usr指定 OpenSSL 位置。如果你的 OpenSSL 是自己编译装在/usr/local/openssl,这里就要指过去,否则链接时会报找不到libcrypto。判断依据是openssl version -d输出的 OPENSSLDIR 上级目录。
--with-pam启用 PAM 认证。启用后必须配套一份/etc/pam.d/sshd,否则表现是密码输对了但立刻被踢,日志里出现PAM: authentication failure或Could not open /etc/pam.d/sshd。不需要 PAM 的机器(比如纯密钥登录的跳板机)可以不加这个开关,少一层依赖。
--with-privsep-path=/var/lib/sshd指定特权分离使用的空目录。这个目录必须存在、属主是 root、权限 700、里面什么都没有。默认值是/var/empty,有些系统的文件系统布局里这个目录已经另作他用,所以显式改掉更稳妥。
--with-pid-dir=/run让 sshd 把 pid 文件写到/run而不是/var/run。现代系统上两者通常是软链,但显式指定可以避免容器环境里 pid 文件写不进去导致systemctl认为启动失败。
configure 输出末尾会打印一份摘要,把 PAM、SELinux、systemd notify、OpenSSL 版本这几项都列出来。这份摘要建议存档,后面出问题时对照着看,能快速排除掉「编译时压根没开这个特性」这种方向。
2.4 make 与 make install:先看清覆盖范围再按回车
编译本身没什么坑,真正要小心的是安装阶段。make install会覆盖目标路径下的同名文件,而且不会问你。
# 并行编译,核数按机器实际情况给 make -j"$(nproc)" # 建议先装到临时根目录看清单,别直接装到系统里 make install DESTDIR=/tmp/ssh-stage # 确认清单无误后再装到真实路径 make installmake -j"$(nproc)"里的核数别开太大,老机器上nproc返回几十核但实际是共享 CPU 配额的情况很常见,编译进程互相抢内存反而更慢,甚至被 OOM killer 干掉。我一般上限给 8。
DESTDIR=/tmp/ssh-stage这一步是后悔药。它把所有产物按相对路径铺到临时目录,你可以find /tmp/ssh-stage -type f看一遍完整清单,确认没有哪个路径出乎意料。这个习惯是从一次事故里养成的:某次--sysconfdir少写了一个斜杠,配置文件被装到了/etc/ssh/etc/ssh下面,make install又是一路无声通过,直到重启 sshd 才发现读的还是老配置。
直接make install时,有三类文件会被动到:/usr/bin下的客户端工具、/usr/sbin/sshd、以及--sysconfdir指向目录里的sshd_config和ssh_config。第三类是重灾区——它会把你在旧版本上精心调过的配置直接盖成默认模板。所以安装前必须先把/etc/ssh/sshd_config备份到别的地方,装完再按需合并。
主机密钥文件ssh_host_*不在覆盖范围内,这一点可以放心。但如果你在安装前手动清过/etc/ssh,或者用了--sysconfdir指向一个全新目录,那边就没有主机密钥了,启动时会看到sshd: no hostkeys available -- exiting,需要用ssh-keygen -A重新生成。生成的瞬间,所有客户端的 known_hosts 都会报警,这个后果要提前想到。
3. sshd_config 落地:一份能过扫描的配置长什么样
装完之后二进制是新的,配置文件却往往是旧的,或者被覆盖成了默认模板。这一章把配置按「能跑起来 → 能被扫描器接受 → 能扛住日常使用」三层往深里推。
3.1 最小可用骨架与几个必须删掉的残留项
先给一份可以直接抄的骨架,适用于大多数内网跳板机场景:
# 监听 Port 22 AddressFamily inet ListenAddress 0.0.0.0 # 认证 PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no PermitEmptyPasswords no MaxAuthTries 3 LoginGraceTime 30 # 会话 ClientAliveInterval 300 ClientAliveCountMax 2 X11Forwarding no AllowTcpForwarding no PermitTunnel no MaxSessions 10 # 子系统 Subsystem sftp internal-sftp # 日志 SyslogFacility AUTH LogLevel VERBOSE这段配置里每一项都有明确意图。PermitRootLogin prohibit-password允许 root 用密钥登录但禁掉密码,比直接no更实用——很多自动化脚本就是以 root 加密钥跑任务的。MaxAuthTries 3把暴力尝试的成本抬上去,配合LoginGraceTime 30让半开连接不会长期占着资源。ClientAliveInterval 300配合ClientAliveCountMax 2,意味着网络断了大约十分钟后会清理会话,避免僵死连接堆积。
Subsystem sftp internal-sftp用内置实现替代外部sftp-server,减少一个可执行文件暴露面,并且支持在Match块里做目录限制。
有两个常见的残留项必须删掉。一个是UsePrivilegeSeparation,这个指令在 7.5 之后就被移除了,特权分离变成强制行为,配置文件里留着它,sshd -t会直接报Bad configuration option然后退出——注意是退出,不是警告。另一个是Protocol,8.x 的sshd_config里已经查不到这个指令了,只支持协议 2,写了同样报错。这两个词在老配置模板里出现频率极高,抄模板时务必逐行核对。
改完一定要先验证再重启:
# -t 只做语法检查,不启动服务,必须在替换二进制之前跑 /usr/sbin/sshd -t -f /etc/ssh/sshd_config # -T 打印最终生效的配置(需 root),把 include 展开后的真实值都列出来 /usr/sbin/sshd -T -f /etc/ssh/sshd_config | head -50-t是必做项,它能在不影响现有连接的前提下把配置错误全部暴露出来。-T更狠,它输出的是合并默认值之后的最终结果,你在配置文件里没写但系统默认开了的东西,这里全都看得见。排查「我明明配了为什么没生效」这类问题时,-T的输出比配置文件本身更可信。
3.2 算法白名单:ssh-rsa 默认被禁之后怎么配
8.8p1 最需要注意的变更,是默认禁用了基于 SHA-1 的ssh-rsa签名算法。这个改动方向是对的,但它会让一批老客户端和老设备直接连不上,报错长这样:no matching host key type found. Their offer: ssh-rsa。
先看清楚当前二进制到底支持哪些算法,再去写白名单:
# 查询当前编译产物支持的算法族,按需查 key / kex / cipher / mac ssh -Q key ssh -Q kex ssh -Q cipher ssh -Q mac # 查看服务端实际生效的算法集合 sshd -T | grep -Ei 'kexalgorithms|ciphers|macs|hostkeyalgorithms|pubkeyaccepted'ssh -Q输出的是「这个二进制能用的全部算法」,sshd -T输出的是「当前配置下实际会用的算法」。两者对照着看,就知道你的白名单是不是写窄了。写窄了的表现是新客户端也连不上,报no matching key exchange method found,比老客户端的问题更尴尬。
一份收紧后的白名单是这样:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com HostKeyAlgorithms rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519 PubkeyAcceptedAlgorithms rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519KexAlgorithms里去掉了所有sha1结尾的选项,保留group16-sha512和group18-sha512是为了兼容某些只支持 DH group 的老设备。Ciphers优先用 AEAD 类(gcm、chacha20-poly1305),它们自带完整性校验,性能也更好。MACs全部带-etm后缀,即 encrypt-then-MAC,比先 MAC 后加密安全。
HostKeyAlgorithms和PubkeyAcceptedAlgorithms是两份不同的白名单,前者管服务端向客户端证明自己身份时用的主机密钥算法,后者管认证用户公钥时接受的算法。这里用rsa-sha2-512、rsa-sha2-256而不是ssh-rsa,是因为前者用 SHA-2 做签名,后者用 SHA-1 已被默认禁用。如果你的主机密钥是 RSA 类型,这两行必须配上,否则服务端根本没法用这把密钥跟客户端协商。
提示:
PubkeyAcceptedKeyTypes是旧名字,8.8p1 里仍然认,但会提示改名。新写配置统一用PubkeyAcceptedAlgorithms,避免后续版本彻底移除后配置失效。
如果确实有一批老设备必须用ssh-rsa连进来,别全局放开,用Match块只对特定来源网段放开:
# 全局保持收紧,只对老设备所在网段放开 SHA-1 签名 Match Address 10.20.0.0/24 PubkeyAcceptedAlgorithms +ssh-rsa HostKeyAlgorithms +ssh-rsaMatch块里的+前缀表示「在全局基础上追加」,而不是替换。这个写法比直接覆盖安全得多,因为你不会漏掉全局那些更安全的算法。注意Match块必须写在文件末尾,且内部的指令只对匹配到的连接生效。
3.3 权限隔离:目录、属主与 StrictModes
sshd 对文件权限的检查非常严格,这是它挡住的一类真实攻击面,但也经常成为「密钥明明配对了却登不上」的原因。
# 特权分离目录:root 拥有、700、内部为空 install -d -m 700 -o root -g root /var/lib/sshd # 用户家目录与 .ssh 目录 chmod 755 /home/deploy chmod 700 /home/deploy/.ssh chmod 600 /home/deploy/.ssh/authorized_keys chown -R deploy:deploy /home/deploy/.sshStrictModes yes默认就是开启的,它要求authorized_keys及其上各级目录都不能被非属主写入。实践中最常见的翻车是:/home/deploy的属主被改成别人了,或者.ssh给了 777。这两种情况 sshd 都会拒绝使用这把密钥,而且日志里只给一句Authentication refused: bad ownership or modes for directory,不告诉你是哪一级。定位方法是namei -l /home/deploy/.ssh/authorized_keys,它会把每一级目录的权限和属主列成一条链,问题一眼就出来。
另一个容易忽略的点是AuthorizedKeysFile的默认值.ssh/authorized_keys .ssh/authorized_keys2。第二个路径是历史遗留,如果你不想让别人往那儿放密钥,可以显式改成只有第一个。同时建议配AuthorizedKeysCommand none(默认即如此),避免有人通过外部命令注入密钥来源。
如果要启用 PAM,还得配套一份/etc/pam.d/sshd。构建目录里的contrib/下有样例,抄过来按发行版习惯调整即可。PAM 配置错误的表现很有迷惑性:密码明明是对的,日志里Accepted password也打印了,但紧接着会话就断,客户端只看到Connection closed by remote host。这种时候去翻/var/log/secure或journalctl -u sshd,能看到 PAM 阶段的报错。
3.4 交给 systemd:unit 文件与启动校验
源码安装不会自动生成 unit 文件,需要手写一份。下面这份是长期验证过的最小版本:
[Unit] Description=OpenSSH server daemon Documentation=man:sshd(8) man:sshd_config(5) After=network.target auditd.service ConditionPathExists=/etc/ssh/sshd_config [Service] Type=simple ExecStartPre=/usr/sbin/sshd -t ExecStart=/usr/sbin/sshd -D ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.targetType=simple是有意选的,不是notify。原因在避坑那一章细说,简单讲:notify要求编译时链上了 libsystemd,构建机没有这个开发包时,编译出的 sshd 根本不会发启动完成信号,systemd 会干等六十秒然后判定失败,而实际上服务早就起来了。
ExecStartPre=/usr/sbin/sshd -t让每次启动前都先跑一遍配置检查。配置写错时,systemd 直接报control process exited,不会出现「启动成功但端口没监听」这种更难查的状态。
KillMode=process这一行是保命用的。默认的control-group会杀掉服务的整个进程组,而 sshd 每个连接都是一个独立子进程,用默认值重启时会把所有在线会话一起踢掉。改成process只杀主进程,已建立的会话不受影响。这个差异在正常重启时看不出来,出事那天就是天壤之别。
# 重载 systemd 配置,enable 并启动 systemctl daemon-reload systemctl enable sshd systemctl start sshd # 三项校验:是否 active、端口是否监听、进程是否为新二进制 systemctl is-active sshd ss -lntp | grep ':22' readlink -f /proc/"$(cat /run/sshd.pid)"/exe最后那条readlink是在确认「正在跑的确实是新装的那个二进制」。软链接、PATH 顺序、以及忘记make install这三件事,都会让旧进程继续占着 22 端口,而你以为已经升级完了。
4. 平滑替换:把线上 sshd 从旧版本换到 8.8p1
前面两章准备的是「新的一套能跑」,这一章解决的是「怎么把旧的换下来而不掉线」。顺序错了,代价是失联。
4.1 动手前的备份清单
备份不是把文件复制一份就完事,关键是每份备份都要对应一个明确的可回滚动作。
# 时间戳统一,便于整体回滚 TS=$(date +%Y%m%d%H%M) # 1. 旧二进制与库依赖 cp -a /usr/sbin/sshd /root/backup/sshd.bak.$TS ldd /usr/sbin/sshd > /root/backup/sshd.ldd.$TS # 2. 整个配置目录(含主机密钥) tar -czf /root/backup/etc-ssh.$TS.tar.gz /etc/ssh # 3. 旧版本号与算法集,用于前后对比 ssh -V > /root/backup/version.$TS 2>&1 sshd -T > /root/backup/sshd-T.$TS 2>&1ldd那份输出值得单独存。源码编译的 sshd 可能链接了你自己装的 OpenSSL,回滚时如果只还原了二进制而库已经变了,旧二进制照样跑不起来。有了这份依赖清单,回滚时能快速判断缺哪个库。
/etc/ssh整目录打包包含了主机密钥,这是最重要的一份。主机密钥丢了意味着所有客户端的 known_hosts 失效,规模大的内网里这会演变成一场人工核对风暴。
sshd -T的旧快照用于前后对比。升级后跑一次新的sshd -T,用diff一比,哪些默认值变了、哪些配置项被忽略了,一目了然。
4.2 并行安装:先在旁路端口跑一个 8.8p1
不要直接覆盖 22 端口上的服务。先把新版本跑在 2222 上,用真实客户端连一遍,确认没问题再切。
# 准备独立的配置与 pid 文件,避免和线上实例互相干扰 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.test sed -i 's/^Port 22/Port 2222/' /etc/ssh/sshd_config.test # 前台调试启动,-d 表示不 fork、输出调试日志 /usr/sbin/sshd -d -f /etc/ssh/sshd_config.test-d模式会把协商过程、认证过程、配置加载过程全部打到终端,是排查「为什么连不上」时最快的手段。缺点是它一次只处理一个连接,验证完就得停掉。
验证清单按这个顺序走:先用ssh -p 2222 -o PreferredAuthentications=publickey试密钥登录;再用ssh -p 2222 -vvv看协商出来的算法是不是白名单里的那几个;然后从一台老设备上试,确认它没被 ssh-rsa 的默认禁用卡住;最后用sftp -P 2222确认子系统正常。
旁路实例跑通之后,可以直接把这个 2222 端口留着当应急入口。平时它不对外,真出事时至少还有一条路能进去改配置。这是我从一次配置写错导致 22 端口起不来的经历里学到的。
4.3 窗口期切换:不断线的操作顺序
切换的核心原则只有一条:手上必须始终有一个活着的会话。具体顺序如下。
第一步,开两个终端,都用 root 登录到目标机,其中一个全程不执行任何命令,就挂着。这是你的安全绳。
第二步,在另一个终端里确认新二进制就位:
# 确认待安装的二进制版本正确 /usr/local/sbin/sshd.new -V 2>&1 || /usr/sbin/sshd -V 2>&1 # 配置预检,必须通过才继续 /usr/sbin/sshd -t -f /etc/ssh/sshd_config第三步,执行替换并重启。注意restart而不是reload,reload只重读配置不换二进制。
systemctl restart sshd # 立刻验证:状态、端口、进程 systemctl is-active sshd ss -lntp | grep ':22'第四步,不要关闭第一个终端,另开第三个终端做一次全新登录。登录成功,安全绳才可以撤。
这里有个细节:systemctl restart之后,第一步挂着的那个会话其实已经和新的 sshd 主进程没有父子关系了,它由旧主进程 fork 出来后独立存在。所以即使新 sshd 起不来,那个会话依然可用——前提是 unit 文件里KillMode=process。如果用了默认的control-group,restart 时会把整个 cgroup 里的进程全杀掉,包括你挂着的那个会话,安全绳直接断掉。这也是为什么第 3 章坚持要写那一行。
4.4 切换后的四项验证
重启成功不等于升级完成,这四项要逐一过。
# 1. 版本号 ssh -V # 2. 生效配置与服务端算法 sshd -T | grep -Ei 'port|permitrootlogin|pubkeyaccepted|kexalgorithms' # 3. 实际协商结果(从另一台机器发起) ssh -vvv user@target 2>&1 | grep -E 'remote software version|kex: algorithm|host key algorithm' # 4. 老客户端兼容性(按需) ssh -o PubkeyAcceptedAlgorithms=ssh-rsa -o HostKeyAlgorithms=ssh-rsa user@target第三条最值得看。remote software version会打印对端返回的版本串,确认是OpenSSH_8.8p1;kex: algorithm和host key algorithm显示实际协商出来的算法,拿去跟白名单对一下,就知道配置有没有真正生效。
第四条是灰度验证。显式指定老算法去连,如果连上了说明你的Match兼容块起作用了;如果连不上且报no matching,说明兼容块没匹配到,去检查来源地址段写对没有。
四项全过之后,把旁路 2222 实例停掉,但保留它的配置文件,下次升级直接复用。
5. 避坑与排查:五个真实翻车现场
5.1 配置里残留老指令,sshd -t 直接退出
现象:systemctl restart sshd返回失败,journalctl -u sshd里只有一行Bad configuration option: useprivilegeseparation,服务没起来,22 端口无监听。
原因:UsePrivilegeSeparation在早期版本里是个可配开关,后来的版本改成强制开启并移除了这个指令。8.8p1 遇到不认识的指令会直接拒绝启动,而不是忽略。同理还有Protocol,只支持协议 2 之后这个指令也被移除了。老配置模板里这两个词出现率极高。
解决:注释掉或删除这两行,然后sshd -t -f /etc/ssh/sshd_config复检。更稳妥的做法是在替换前先跑一遍预检,把语法错误挡在重启之前。对于不确定还认不认的指令,man 5 sshd_config里查不到就是没了。
5.2 客户端报 no matching host key type found
现象:服务端升级完成,本机登录正常,但几台老设备和几个老客户端连不上,报Unable to negotiate with x.x.x.x port 22: no matching host key type found. Their offer: ssh-rsa或no matching key exchange method found。
原因:8.8p1 默认禁用了 SHA-1 签名的ssh-rsa,同时默认 KEX 集合里也去掉了一批弱算法。老客户端只提供这些,协商自然失败。注意报错里的主语是Their offer,指的是对方提供的算法,方向别搞反。
解决:优先升级客户端,这是根治。确实升不动的设备,用Match Address只对它的来源网段放开+ssh-rsa,不要全局放开。改完sshd -t复检,systemctl reload sshd即可生效(改算法不需要重启主进程)。
5.3 make install 把自定义配置盖成默认模板
现象:升级完重启,发现之前配的PermitRootLogin、PasswordAuthentication全变回默认值,root 密码登录突然被允许了,或者反过来密钥用户登不上了。
原因:make install会无条件把构建目录里的sshd_config和ssh_config写到--sysconfdir指向的目录,覆盖同名文件,且不做备份。这个行为在 Makefile 里是写死的,没有交互确认。
解决:装之前先cp -a /etc/ssh /root/backup/etc-ssh.$(date +%s)备份整个目录,装完用diff对比,把自定义项重新合并回去。更省事的办法是安装时用DESTDIR铺到临时目录,只从中挑二进制文件复制到系统路径,配置文件完全不动。这个做法我在后面几台机器上都改成了默认流程。
5.4 systemd 用 Type=notify 但二进制不带 notify 支持
现象:systemctl start sshd卡住不返回,大约一分钟后报Job for sshd.service failed because a timeout was exceeded,但此时ss -lntp | grep 22能看到端口已经在监听,手动ssh也能连上。
原因:unit 里写了Type=notify,systemd 会等服务进程发READY=1信号才认为启动完成。而源码编译时如果构建机上没有 libsystemd 开发包,configure 就检测不到这个特性,编译出的 sshd 根本不会发这个信号。服务实际起来了,systemd 却一直等。
解决:unit 改成Type=simple,或者装好 libsystemd 开发包重新 configure 编译。判断依据是 configure 输出摘要里有没有 systemd notify 那一项。我现在的习惯是编译前先看摘要,摘要里没有的就不在 unit 里用。
5.5 PAM 头文件缺失,configure 中途退出
现象:./configure --with-pam跑到一半报checking for pam_appl.h... no然后configure: error: PAM headers not found,整个流程中断。
原因:构建机只装了 PAM 运行库,没装开发头文件。运行库能让已有程序跑,但编译需要.h文件和链接用的.so符号链接,这两样在开发包里。
解决:装上对应开发包再重新 configure。判断是否装全,用ls /usr/include/security/pam_appl.h和ls /usr/lib*/libpam.so两条命令检查,两个都在才算齐。如果你压根不需要 PAM 认证,直接去掉--with-pam更快,但要注意后续如果改用密码认证就得重编。
6. 进阶:把这次升级固化成下次能直接复用的一套流程
单次升级做完就忘,下次换机器还得重新踩一遍。真正省时间的是把「构建参数 + 配置基线 + 自检脚本」这三样固化下来,做成一份可以重复执行的东西。
6.1 固化构建参数与配置基线
构建参数建议写成一个小脚本,参数和判断逻辑都在里面,而不是靠记忆敲:
#!/usr/bin/env bash # build-openssh.sh —— 构建参数集中管理,避免每次靠回忆 set -euo pipefail SRC_VER="8.8p1" PREFIX="/usr" SYSCONFDIR="/etc/ssh" PRIVSEP="/var/lib/sshd" # PAM 按需开启,通过环境变量控制而不是改脚本 PAM_FLAG="" if [ "${WITH_PAM:-no}" = "yes" ]; then PAM_FLAG="--with-pam" fi tar -xzf "openssh-${SRC_VER}.tar.gz" cd "openssh-${SRC_VER}" ./configure \ --prefix="${PREFIX}" \ --sysconfdir="${SYSCONFDIR}" \ --with-ssl-dir=/usr \ --with-zlib=/usr \ ${PAM_FLAG} \ --with-privsep-path="${PRIVSEP}" \ --with-pid-dir=/run | tee "../configure-${SRC_VER}.log" grep -Ei 'pam|systemd|openssl' "../configure-${SRC_VER}.log"set -euo pipefail让脚本在任一命令失败时立刻停下,避免带着半成品继续往下跑。tee把 configure 输出同时写文件,后面 grep 摘要那一步就是靠这份日志——PAM、systemd notify、OpenSSL 版本这三项有没有,全在摘要里。WITH_PAM用环境变量控制,同一份脚本在不同机器上不用改代码。
配置基线同样要版本化。我会在仓库里放三份:sshd_config.base(全局收紧版)、sshd_config.compat(带 Match 兼容块)、sshd_config.internal(纯内网跳板用)。部署时按机器角色选一份,再用sshd -t卡一道。
6.2 升级前后各跑一遍的自检脚本
验证不能靠「连上了就行」,要有可对比的输出。下面这个脚本升级前后各跑一次,把结果 diff 一下,变化点就是风险点。
#!/usr/bin/env bash # ssh-audit.sh —— 采集版本、算法、关键配置,输出到文件 OUT="${1:-/tmp/ssh-audit-$(date +%s).txt}" { echo "=== version ===" ssh -V 2>&1 echo "=== listening ===" ss -lntp | grep ':22' || echo "port 22 not listening" echo "=== effective config ===" sshd -T 2>/dev/null | grep -Ei \ '^(port|permitrootlogin|passwordauthentication|pubkeyauthentication|maxauthtries|kexalgorithms|ciphers|macs|hostkeyalgorithms|pubkeyacceptedalgorithms)' echo "=== supported algs ===" ssh -Q key | head -20 } > "${OUT}" 2>&1 echo "written: ${OUT}"sshd -T那段 grep 只挑关键项,输出短才好 diff。ssh -Q key只取前 20 行,是为了控制输出长度,真正需要全量对比时把head去掉。输出文件名带时间戳,避免前后两份互相覆盖。
用法是升级前跑一次存成before.txt,升级后跑一次存成after.txt,然后diff before.txt after.txt。有几类差异是「预期内」的,比如版本号必然变;有几类是「必须追」的,比如permitrootlogin从prohibit-password变成了yes,那说明配置文件被覆盖了,得回头查安装流程。
最后说个踩过的坑。早些年我升级完只验证了「本机能 ssh 上去」,就宣布完工,结果第二天业务侧反馈批量任务全部失败——原因是那些任务用的 RSA 密钥,8.8p1 默认禁了ssh-rsa之后认证走不通,而我本机用的是 ed25519 密钥,完全没暴露问题。从那以后我每次升级完,都会拿三类不同密钥类型的账号各登录一次,并且把sshd -T的输出和升级前 diff 一遍再收工。希望帮到你。
本文还有配套的精品资源,点击获取