多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

自制OpenSSH 9.6p1 RPM包:统信UOS aarch64升级实战

自制OpenSSH 9.6p1 RPM包:统信UOS aarch64升级实战 简介面向统信UOS aarch64UEL20环境的OpenSSH升级资源专为运维人员打造。针对9.6p1以下版本存在的安全与兼容性问题以自制rpm安装包方式提供升级至9.6p1的完整方案适配国产化信创与等保整改场景。压缩包体积仅1.5MB共4个文件3个rpm安装包分别对应OpenSSH主体、客户端与服务端另有1个升级脚本可在离线或内网环境中快速部署。已有1286人学习/下载。通过预编译rpm与自动化脚本可省去源码编译和依赖处理降低手工替换关键系统组件时的风险对于需要批量运维多台统信UOS设备的场景也能显著提升效率同时方便留存升级记录。整体内容精简实用适合具备基础Linux操作能力、想尽快完成OpenSSH安全升级的运维与安全人员。1. 为什么需要自制 OpenSSH 9.6p1 的 RPM 包aarch64 上升级的独立方案统信UOS的服务器或桌面环境里官方源里的OpenSSH版本往往落后于上游好几个大版本。等官方仓库更新到9.6p1可能要等上一轮安全审计而漏洞可不等人。我在aarch64机器上处理过不少回扫报告几乎都卡在同一个问题上UOS本身是deb系的但客户环境强制要求以rpm包形式交付升级包因为他们的CMDB和固件基线只认rpm格式。这种场景下下载官方源码编译安装是临时救火但升一次管一次换台机器还得重新编手里没有可复用的rpm包后续批量升级就要挨个服务器去吃二遍苦。自制的价值就在于把OpenSSH 9.6p1变成一枚标准的aarch64 rpm包既能放进自建源又能通过rpm命令做升级、回滚和校验。这篇笔记我就按“打包链路 → 构建步骤 → 安装验证 → 翻车避坑 → 沉淀为基线”的顺序把在UOS aarch64上自制OpenSSH 9.6p1 rpm包的全过程讲清楚。2. 自制 RPM 包的逻辑从源码到 aarch64 目标的打包链路2.1 OpenSSH 9.6p1 源码包与构建依赖先理解OpenSSH 9.6p1这个东西。它是一组程序sshd、ssh、scp、sftp、ssh-keygen等。源码包就一个tar.gz但真正编译时依赖的系统库却不少。常见依赖包括zlib、openssl-devel、pam-devel、libedit-devel、krb5-devel等。在aarch64上这些依赖本身需要存在64位版本否则链接阶段会报“跳过不兼容的二进制”。UOS系统里由于桌面版和服务器版的软件源不一样我通常会先用dpkg -l或rpm -qa确认基础依赖是否就位再决定要不要从源代码编译zlib和openssl。对于想要自制rpm包的人来说最大的误区是一上来就用源码直接configure、make、make install然后打包文件。这种做法倒也能生成一个rpm但会把源码目录里的编译中间文件也塞进去而且升级时rpm无法卸载干净。正确的做法是先写好一个spec文件用rpmbuild -ba一次性得到src.rpm和二进制rpm。OpenSSH的spec文件在/usr/share/doc/openssh*里往往有参考模板但那些模板是针对发行版默认路径的在UOS上要改掉许多段落。2.2 RPM spec 文件的基本骨架与变量变量spec文件是rpm打包的灵魂。它定义了源代码位置、补丁、构建阶段、安装目标、文件清单、脚本钩子。对于OpenSSH 9.6p1我要强调几个关键变量%define version 9.6p1但真正解压出来的目录是openssh-9.6p1所以Name和Version要匹配。注意rpm的Version字段不允许带“p1”这种后缀标准做法是Version: 9.6Release: 1%{?dist}而把p1体现在Source压缩包命名里。这样在rpm -qa时显示的版本才是符合rpm规范的。BuildRequires至少要有gcc make openssl-devel zlib-devel pam-devel。如果UOS源里没有openssl-devel需要先安装libssl-dev并额外提供符号链接这个坑在后面章节展开。Source0指向本地存放的openssh-9.6p1.tar.gzrpmbuild会把~/rpmbuild/SOURCES里的文件自动解包到~/rpmbuild/BUILD/下。%configure脚本会传入一系列--with-*参数。我通常用--with-md5-passwords --with-pam --with-selinux这三个固定项外加--with-privsep-path/var/empty/sshd。在UOS上因为没有selinux默认启用我一般去掉selinux那一项否则配置阶段就可能因为找不到libselinux-dev而中断。2.3 用 rpmdev-setuptree 搭建打包目录虽然UOS是deb系但环境里只要装了rpm打包工具就可以按Fedora/CentOS习惯搭建目录树。先执行rpmdev-setuptree这个命令会自动创建~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}五个子目录。如果UOS源里没有rpmdevtools就手动创建这些目录。手建的话还要注意~/.rpmmacros里的%_topdir是否指向了用户目录。如果机器上同时存在/usr/src/rpm和~/rpmbuild在aarch64环境里我遇到过rpmbuild只认/usr/src/rpm的系统级路径导致普通用户没有写权限。解决办法是往~/.rpmmacros写%_topdir %{getenv:HOME}/rpmbuild强制指定。3. 用 rpmbuild 在 UOS aarch64 上构建 OpenSSH 9.6p1 的完整步骤3.1 安装打包工具链并下载对应源码在一台干净的UOS aarch64机器上先确认是否已经有打包工具。注意UOS经常没有预装rpmbuild所以要先用deb包管理装rpm、rpmbuild以及依赖编译工具。这一步看起来基础却是最容易卡住的因为UOS的软件源里可能没有rpm-build这个包。我的做法是添加阿里云或者清华的兼容源但更稳妥的是直接使用欧拉或兼容CentOS的rpm包。这里给一个可复现的命令序列sudo apt update sudo apt install -y rpm rpm-build gcc make python3 wget tar # 如果apt源里有libssl-dev和zlib1g-dev直接装上 sudo apt install -y libssl-dev zlib1g-dev libpam0g-dev # 验证rpmbuild是否可用 rpmbuild --version我看到rpmbuild版本是4.x后再下载OpenSSH源码。这里有个习惯从OpenBSD官方下载openssh-9.6p1.tar.gz和对应的sig文件用gpg --verify校验签名。校验源码包dajudi完整性不是可选步骤尤其是自制rpm升级到安全版本如果源码被污染打包出来的东西就是后门定时炸弹。下载完成后把tar.gz连同spec文件放到~/rpmbuild/SOURCES命名不能改动否则%setup解包时找不到文件。3.2 编写针对 UOS 的 specconfigure 参数与 install 路径在~/rpmbuild/SPECS里创建openssh.spec。这个spec要从零写不要直接照抄CentOS的版本因为UOS的PAM配置和sshd配置路径有差异。我一般会把/etc/pam.d/sshd作为依赖项保留而不是像CentOS那样由系统自带。spec里最重要的脚本段是%build和%install。下面是我实践后能跑通的简化版# ~/rpmbuild/SPECS/openssh.spec Name: openssh Version: 9.6 Release: 1%{?dist} Summary: OpenSSH client and server Source0: openssh-9.6p1.tar.gz License: BSD BuildRequires: gcc, make, openssl-devel, zlib-devel, pam-devel, rpm-build Requires: /etc/pam.d/sshd %description Open Source Secure Shell components extracted with rpm packaging. %prep %setup -q -n openssh-9.6p1 %build %configure --with-md5-passwords --with-pam --with-privsep-path/var/empty/sshd \ --with-default-path/usr/local/bin:/usr/bin:/bin \ --with-superuser-path/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ --sysconfdir/etc/ssh --disable-etc-default-login make -j $(nproc) %install make install DESTDIR%{buildroot} # 保留系统原有配置文件避免安装时覆盖 mkdir -p %{buildroot}/etc/ssh touch %{buildroot}/etc/ssh/sshd_config注意在%install阶段我没直接用make install到%{buildroot}的默认路径而是用--sysconfdir/etc/ssh固定配置文件位置。同时我在%files里把/etc/ssh/sshd_config标记为%config(noreplace)这样升级时rpm不会用新文件覆盖已有配置。另外OpenSSH的sshd需要/var/empty/sshd目录这个目录必须在%files里带上并且权限设为0755 root:root。由于UOS的systemd服务通常会直接调用/usr/sbin/sshd打包完成后必须确认二进制落在这个路径而不是/usr/local/sbin。3.3 执行 rpmbuild -ba 并处理依赖缺失在~/rpmbuild/SPECS下执行rpmbuild -ba openssh.spec第一次多半会在依赖检查或编译阶段失败。常见的编译错误是找不到openssl/opensslv.h这是因为UOS把OpenSSL头文件放在了/usr/include/openssl但因为aarch64多架构问题系统的openssl-devel包没被正确安装。我处理的方式是手动创建一个symlink# 检查头文件是否存在 ls -l /usr/include/openssl/opensslv.h # 如果缺失从系统openssl库包文件里解压出来补上 sudo apt install -y libssl-dev # 再次确认pkg-config能发现openssl pkg-config --modversion openssl如果pkg-config报告版本低于1.1.1OpenSSH 9.6会拒绝编译因为编译期要求OpenSSL 1.0.1以上但某些新特性会依赖更高版本。此时不建议升级系统OpenSSL因为UOS的系统库依赖链很长直接换OpenSSL可能导致sshd以外的服务崩掉。我一般选择在spec里加--with-ssl-dir/usr并锁定openssl-devel的版本让它不要跑去另一个路径。处理依赖缺失时不要盲目装额外的rpm先看error: Failed build dependencies提示了哪个包然后在UOS的软件源里用apt-file search找对应的头文件包。如果rpmbuild提示无法找到rpath或libcrypto可能是LDFLAGS没传可以在spec的%build里加export LDFLAGS-Wl,-rpath -Wl,/usr/lib64但aarch64的库目录是/usr/lib/aarch64-linux-gnu这一点在configure日志里要特别检查。4. 安装升级与验证替换系统 SSH 后必须检查的五个点4.1 先在测试机安装并确认关键文件归属构建出的二进制rpm位于~/rpmbuild/RPMS/aarch64/openssh-9.6-1.*.aarch64.rpm。在正式批量升级前一定先找一台同架构、同UOS小版本的测试机执行rpm -ivh openssh-*.aarch64.rpm --test做预演。预演通过后再执行rpm -ivh或rpm -Uvh。注意如果旧版OpenSSH也是rpm管理的用-Uvh升级如果之前是源码安装的裸二进制则需要先手工停掉旧sshd再用-ivh新装否则会出现/usr/sbin/sshd被占用而rpm拒绝覆盖。安装完成后执行rpm -ql openssh | head -20重点看三处/usr/sbin/sshd是否存在、/etc/ssh/sshd_config是否被noreplace保留、/usr/bin/ssh的版本是否为9.6。我在测试机上最常犯的错是忘了检查/usr/libexec/openssh/sftp-server是否是rpm包自带的如果UOS的老版本还在/usr/libexec/sftp-server新sshd配置文件里会显式调用这个落空的路径导致sftp连接失败。4.2 sshd 服务脚本与新二进制的关系UOS使用systemd管理服务升级后systemctl restart sshd之前要确认unit文件。UOS的sshd.service位于/usr/lib/systemd/system/sshd.service内容中ExecStart/usr/sbin/sshd -D $OPTIONS默认指向/usr/sbin/sshd。如果spec在%install里把sshd装到了/usr/local/sbinsystemd服务就会拉起旧文件版本号显示还是老样子。解决方式有二在spec里强制安装到/usr/sbin或者在打包后做软链ln -sf /usr/local/sbin/sshd /usr/sbin/sshd。我推荐第一种让rpm包本身直接管理/usr/sbin/sshd避免软链在下次rpm覆盖时被清掉。检查完服务文件还要执行systemctl daemon-reload因为sshd.service可能引用了新的环境变量。接着用systemctl status sshd观察状态重点看日志里的Failed to start sshd.service: Unit refuses to die这类现象。这种问题通常不是sshd本身而是升级后/run/sshd.pid权限不对sshd以root运行时写不进PID文件。用sshd -t测试配置文件通过后再重启服务。4.3 验证协议版本和弱算法开关升级完成后我习惯用下面一段命令验证新版本生效以及是否禁用了老旧算法ssh -V 21 # 输出应包含 OpenSSH_9.6p1, OpenSSL 或 LibreSSL 相关版本 sshd -T | grep -E protocol|macs|kexalgorithmsUOS的默认sshd配置里KexAlgorithms和MACs往往白名单很宽升级到9.6后建议顺手收紧。比如在/etc/ssh/sshd_config.d/security.conf里写KexAlgorithms curve25519-sha256,ecdh-sha2-nistp256 MACs umac-128-etmopenssh.com,hmac-sha2-256-etmopenssh.com这里要留意如果系统里还有老客户端用ssh-dss密钥升级后默认禁用DSA会导致部分自动化运维脚本连接失败。我先把PubkeyAcceptedAlgorithms里的ssh-rsa保留一段过渡期等堡垒机全部适配后再收紧。验证时不要只测ssh -V还要跑一次真实的ssh rootlocalhost登录确认PAM认证链路没断。PAM如果断了密码登录会显示Permission denied但密钥登录却正常这种静默半坏状态最难排查后面避坑章会专门讲。5. UOS aarch64 下升级 OpenSSH 的翻车避坑常见问题与排查5.1 坑PAM 模块链接崩溃导致登录失败现象升级后通过ssh客户端连服务器密码正确仍提示Permission denied (publickey,password)但排查/var/log/secure时只看到pam_unix(sshd:auth): authentication failure系统日志里没有更多细节。原因自制rpm时如果%configure没有带--with-pam或者链接时没找到libpam.so新sshd会退回纯密码验证但UOS的/etc/pam.d/sshd文件里有大量pam_*规则引用而这些规则只对支持PAM的sshd生效。如果编译时把--with-pam漏掉sshd会忽略/etc/pam.d/sshd但默认配置里又启用了UsePAM yes于是PAM库尝试加载系统策略又不清楚为什么失败。解决在spec的%build里确认--with-pam并添加--with-pam对应的BuildRequires: pam-devel。如果已经安装了错误包查看ldd /usr/sbin/sshd | grep pam。若没有库链接重打rpm。若链接存在但依旧失败检查/usr/lib/aarch64-linux-gnu/security/下pam_unix.so是否存在UOS的pam模块有多个路径必要时在/etc/pam.d/sshd里把pam_unix.so的绝对路径改成pam_unix.so让动态库搜索器自行找到正确架构。5.2 坑自定义安装到 /usr/local 后 PATH 不生效现象升级后命令行执行ssh -V显示老版本但/usr/local/bin/ssh -V显示新版本。原因spec的%configure默认会使用/usr/local/bin作为执行目录。在UOS上普通用户的PATH是/usr/bin:/bin优先于/usr/local/bin所以即使新sshd已经安装用户敲ssh时仍命中旧的/usr/bin/ssh。解决在%install里不要用默认make install而是把二进制拷贝到系统路径make install DESTDIR%{buildroot} install -m 0755 ssh %{buildroot}/usr/bin/ssh install -m 0755 sshd %{buildroot}/usr/sbin/sshd如果坚持用/usr/local就要在包安装更新/etc/profile或/etc/bash.bashrc里调整PATH顺序但这会让系统的rpm管理变得混乱而且后续自动化工具用ssh时会莫名其妙连接旧版。我建议直接把bindir设定为/usr/bin在spec的%configure里加--bindir/usr/bin --sbindir/usr/sbin --libexecdir/usr/libexec/openssh。5.3 坑RPM 打包时 OpenSSL 版本与系统库不匹配现象rpmbuild执行到make时报错openssl/opensslv.h: No such file or directory或者链接出现undefined reference to OPENSSL_init_ssl。原因UOS aarch64的官方repo里可能既有libssl1.1又有libssl3头文件与库文件版本错位。打包机上的openssl-devel包版本是1.1.1但系统运行时加载的libcrypto.so是3.x导致编译和运行行为不一致。解决在spec的%build里显式指定OpenSSL的头文件和库目录。先执行openssl version确认系统主版本然后在configure时加上%configure --with-ssl-dir/usr --with-openssl-header-checkno如果--with-openssl-header-checkno不被OpenSSH的configure识别就用环境变量强制export CPPFLAGS-I/usr/include/openssl -L/usr/lib/aarch64-linux-gnu还要注意UOS上/usr/lib/aarch64-linux-gnu和/usr/lib64经常共存rpmbuild默认LDFLAGS可能指向不存在的/usr/lib64因此spec里我经常写入export LDFLAGS-L/usr/lib/aarch64-linux-gnu。打好包后在目标机上执行ldd /usr/sbin/sshd确认所有libssl.so和libcrypto.so都指向同一个版本路径不要看到not found就算完。5.4 坑升级后主机密钥权限被强制更改现象重启sshd后客户端提示Host key verification failed。检查/etc/ssh/里的ssh_host_rsa_key权限发现变成了0644。原因spec的%files段里如果没给密钥文件显式设置权限rpmbuild会把源文件在%{buildroot}中的权限原样打包。而OpenSSH的make install在安装新密钥样例时可能生成0644权限但运行时sshd以sshd用户读取私钥时要求0600。解决在spec的%files中为密钥文件单独声明权限%attr(0600,root,root) /etc/ssh/ssh_host_*_key %attr(0644,root,root) /etc/ssh/ssh_host_*_key.pub同时在%post脚本里加上自动修正权限的兜底命令chmod 0600 /etc/ssh/ssh_host_*_key 2/dev/null || true chmod 0644 /etc/ssh/ssh_host_*_key.pub 2/dev/null || true这条兜底在实际运维中很有用因为UOS的SSH密钥可能被某个老脚本改过权限而rpm不会追踪非rpm文件的权限变化。升级后立即检查sshd -T | grep -i hostkey确认文件路径权限修正后重启服务避免半夜被密钥告警叫醒。5.5 坑sshd 服务无法通过 systemd 启动现象systemctl restart sshd提示Job for sshd.service failed但手工执行/usr/sbin/sshd -D却能前台启动。原因UOS的systemd单元文件里带有Restarton-failure和RuntimeDirectorysshd但升级后sshd的私有临时目录/run/sshd没有被正确拥有。当rpm安装时建目录的动作发生在%pre或%post而systemd的RuntimeDirectory是由服务进程自己创建的。如果新sshd在启动时提前检查目录所有权就会因为/run/sshd属于root而失败。解决在%post脚本中创建目录并设置属主mkdir -p /run/sshd chown root:root /run/sshd chmod 0755 /run/sshd如果仍然失败查看journalctl -u sshd -n 20。我遇到过错误是Missing privilege separation directory: /var/empty/sshd这表示spec里的--with-privsep-path路径和实际系统路径不一致。统一设置为/var/empty/sshd并确保目录存在即可。还有一个坑是UOS的apparmor配置里对sshd二进制做了限制升级后路径变化触发apparmorDENIED需要执行aa-complain /usr/sbin/sshd或更新profile而不是无脑重启。6. 把这套 RPM 做成内部基线自动化验证与回滚技巧在UOS aarch64上自制OpenSSH 9.6p1的rpm包最值得沉淀的不是那一次构建而是把构建和验证流程固化下来。我会在打包机上的~/rpmbuild目录里维护一个Makefile把下载源码、校验签名、rpmbuild、拷贝到本地源、执行安装预演这几步串成一条命令。这样当OpenSSH从9.6p1升级到10.x时只要改spec里的版本号和Source0就能快速产出新包。验证动作可以在多台机器上并行先在一台机器上执行rpm -V openssh检查所有关键文件是否被篡改或丢失。rpm -V的输出如果出现S.5....T.说明文档或二进制大小有变化需要确认不是升级导致的预期变更。接着跑一组自动化测试脚本用ssh连接本机回环地址执行远程命令echo ok同时统计从连接建立到命令返回的耗时。这个耗时在升级后如果突然增加往往不是加密算法变慢而是DNS反查或PAM阶段耗时变大排查方向就明了。回滚技巧也写死在运维手册里用rpm -Uvh --oldpackage openssh-老版本.rpm即可。但要注意回滚后/etc/ssh/sshd_config是否会回退取决于老包是否带%config(noreplace)。如果老包当初没标记noreplace回滚可能把当前配置覆盖掉。所以我每次在spec里都坚持%config(noreplace)并且把自定义配置放在/etc/ssh/sshd_config.d/security.conf这样即使回滚主配置保留新增的安全配置还在。回滚完成后立刻重启sshd并抓连接日志别等用户来报障。整个自制rpm的体系一旦跑通后续再遇到OpenSSH新版本半天内就能从裸源码变成可批量部署的内部基线。这套活儿没有太多玄学每一次翻车都是因为某个隐藏路径或权限没照顾到。希望这篇笔记能帮你在UOS aarch64上省下那些试错时间。本文还有配套的精品资源点击获取
返回列表