写本篇目的
近期,业务环境中的OpenSSH暴露了一个远程代码执行漏洞(CVE-2024-6387,又被称为regreSSHion),影响版本8.5p1<=版本<9.8p1,该漏洞影响基于glibc的Linux系统上的OpenSSH Server。未经身份验证的攻击者可以通过恶意构造的SSH客户端请求,利用此漏洞在OpenSSH服务器上执行任意代码。该漏洞利用复杂但影响面很广,故本篇主要提供一种制作openssh RPM包的思路及方法,适用情况请自行测试。如需要基于openEuler 20.03的安装包请点击原文连接。
使用源码制作opensshRPM包
以编译制作openssh-9.8p1版本的RPM包为例
原理介绍
RPM打包的时候需要编译源码,需要把编译好的配置文件、二进制命令文件等放到合适的位置,还要根据需要对RPM的包进行测试,这些都需要先有一个“工作空间”。rpmbuild命令使用一套标准化的“工作空间”:
$ rpmdev-setuptree
rpmdev-setuptree这个命令就是安装 rpmdevtools 带来的。可以看到运行了这个命令之后,在"/root"目录(非root用户为"/home/用户名"目录)下多了一个 rpmbuild 的文件夹,目录结构如下:
$ tree rpmbuildrpmbuild├── BUILD├── RPMS├── SOURCES├── SPECS└── SRPMS
内容相关的说明如下:
| 目录 | 宏代码 | 名称 | 功能 |
|---|---|---|---|
| ~/rpmbuild/BUILD | %_builddir | 构建目录 | 源码包被解压至此,并在该目录的子目录完成编译 |
| ~/rpmbuild/RPMS | %_rpmdir | 标准 RPM 包目录 | 生成/保存二进制 RPM 包 |
| ~/rpmbuild/SOURCES | %_sourcedir | 源代码目录 | 保存源码包(如 .tar 包)和所有 patch 补丁 |
| ~/rpmbuild/SPECS | %_specdir | Spec 文件目录 | 保存 RPM 包配置(.spec)文件 |
| ~/rpmbuild/SRPMS | %_srcrpmdir | 源代码 RPM 包目录 | 生成/保存源码 RPM 包(SRPM) |
SPECS 下是RPM包的配置文件,是RPM打包的“图纸”,这个文件会告诉rpmbuild命令如何去打包。“宏代码”这一列就可以在SPEC文件中用来代指所对应的目录,类似于编程语言中的宏或全局变量。
打包流程
打包的过程主要分为如下步骤:
把源代码放到%_sourcedir中。
进行编译,编译的过程是在%_builddir中完成的,一般情况下,源代码是压缩包格式,需要先进行解压。
进行“安装”,类似于预先组装软件包,把软件包应该包含的内容(比如二进制文件、配置文件、man文档等)复制到%buildrootdir中,并按照实际安装后的目录结构组装,比如二进制命令可能会放在/usr/bin下,那么就在%buildrootdir下也按照同样的目录结构放置。
做一些必要的配置,比如在实际安装前的准备,安装后的清理等等。这些都是通过配置在SPEC文件中来告诉rpmbuild命令。
检查软件是否正常运行。
生成的RPM包放置到%rpmdir,源码包放置到%srcrpmdir下。
在SPEC文件中,各个阶段说明如下:
| 阶段 | 读取的目录 | 写入的目录 | 具体动作 |
|---|---|---|---|
| %prep | %_sourcedir | %_builddir | 读取位于 %sourcedir 目录的源代码和 patch 。之后,解压源代码至%builddir 的子目录并应用所有 patch。 |
| %build | %_builddir | %_builddir | 编译位于 %_builddir 构建目录下的文件。通过执行类似 ./configure && make的命令实现。 |
| %install | %_builddir | %_buildrootdir | 读取位于 %builddir 构建目录下的文件并将其安装至 %buildrootdir 目录。这些文件就是用户安装 RPM 后,最终得到的文件。 |
| %check | %_builddir | %_builddir | 检查软件是否正常运行。通过执行类似 make test 的命令实现。 |
| bin | %_buildrootdir | %_rpmdir | 读取位于 %buildrootdir 最终安装目录下的文件,以便最终在 %rpmdir 目录下创建 RPM 包。在该目录下,不同架构的 RPM 包会分别保存至不同子目录, noarch 目录保存适用于所有架构的 RPM 包。这些 RPM 文件就是用户最终安装的 RPM 包。 |
| src | %_sourcedir | %_srcrpmdir | 创建源码 RPM 包(简称 SRPM,以.src.rpm 作为后缀名),并保存至%_srcrpmdir 目录。SRPM 包通常用于审核和升级软件包。 |
打包选项
通过rpmbuild命令构建软件包。rpmbuild构建软件包一般可以通过构建SPEC文件、tar文件、source文件实现。
rpmbuild命令格式为:rpmbuild [option...]
表 rpmbuild常用打包选项如下:
| option取值 | 说明 |
|---|---|
| -ba specfile | 通过specfile文件构建源码包和二进制包。 |
| -bb specfile | 通过specfile文件构建二进制包。 |
| -bs specfile | 通过specfile文件构建源码包 |
本地构建RPM操作步骤
用户可以直接使用DNF工具安装 rpmdevtools,其中包含 rpm-build 等命令以及相关依赖(例如make、gdb)。使用如下命令:
dnf install rpmdevtools* -y
接下来以在openEuler 20.03 (LTS-SP4)版本的系统上本地构建openssh-9.8p1版本的RPM包为例进行演示。
构建工作空间
使用rpmdev-setuptree命令构建rpmbuild工作空间
[root@localhost ~]# rpmdev-setuptree[root@localhost ~]# tree ~/rpmbuild//root/rpmbuild/├── BUILD├── RPMS├── SOURCES├── SPECS└── SRPMS
安装依赖环境
[root@localhost SPECS]# yum install gtk2-devel imake krb5-devel libXt-devel openssl-devel pam-devel下载源码
直接下载官方最新的openssh版本源码,使用如下命令:
[root@localhost ~]# cd ~/rpmbuild/SOURCES/[root@localhost SOURCES]# wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz[root@localhost SOURCES]# wget https://src.fedoraproject.org/repo/pkgs/openssh/x11-ssh-askpass-1.2.4.1.tar.gz/8f2e41f3f7eaa8543a2440454637f3c3/x11-ssh-askpass-1.2.4.1.tar.gz
不下载x11-ssh-askpass包在构建时会报错error: Bad source: /root/rpmbuild/SOURCES/x11-ssh-askpass-1.2.4.1.tar.gz: No such file or directory
准备SPEC文件
在~/rpmbuild/SPECS目录下准备spec文件,命令如下:
#解压下班的openssh软件包源码,复制出官方提供的spec文件,rpm-build需要根据这个文件来制作rpm包[root@localhost SOURCES]# tar -zxvf openssh-9.8p1.tar.gz[root@localhost SOURCES]# cp openssh-9.8p1/contrib/redhat/openssh.spec ../SPECS/
编辑SPEC文件
默认openssh源码中是没有ssh-copy-id相关参数的,如果直接编译安装,会发现安装后没有ssh-copy-id命令,因此如果需要用到该命令,需要修改编译参数控制文件openssh.spec
......%installrm -rf $RPM_BUILD_ROOTmkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/sshmkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/opensshmkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshdmake install DESTDIR=$RPM_BUILD_ROOTinstall -d $RPM_BUILD_ROOT/etc/pam.d/install -d $RPM_BUILD_ROOT/etc/rc.d/init.dinstall -d $RPM_BUILD_ROOT%{_libexecdir}/opensshinstall -m644 contrib/redhat/sshd.pam $RPM_BUILD_ROOT/etc/pam.d/sshdinstall -m755 contrib/redhat/sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd#添加如下行内容,大约282行处install -m755 contrib/ssh-copy-id $RPM_BUILD_ROOT/usr/bin/ssh-copy-id......%attr(0755,root,root) %{_bindir}/ssh-keygen#添加如下行内容,大约363行处%attr(0755,root,root) %{_bindir}/ssh-copy-id
构建RPM包
构建源码、二进制和包含调试信息的软件包,在spec文件所在目录执行如下命令:
[root@localhost SPECS]# rpmbuild -ba openssh.spec执行成功后,查看结果,使用如下命令:
[root@localhost SPECS]# tree ~/rpmbuild/*RPMS/root/rpmbuild/RPMS└── x86_64├── openssh-9.8p1-1.x86_64.rpm├── openssh-askpass-9.8p1-1.x86_64.rpm├── openssh-askpass-gnome-9.8p1-1.x86_64.rpm├── openssh-clients-9.8p1-1.x86_64.rpm├── openssh-debuginfo-9.8p1-1.x86_64.rpm├── openssh-debugsource-9.8p1-1.x86_64.rpm└── openssh-server-9.8p1-1.x86_64.rpm/root/rpmbuild/SRPMS└── openssh-9.8p1-1.src.rpm1 directory, 8 files
对openssh原配置相关文件进行备份
对openssh原配置相关文件进行备份,因为安装新的软件包后,相关文件会被覆盖,如配置文件/etc/ssh/sshd_config及认证文件/etc/pam.d/sshd
[root@localhost SPECS]# cp -ar /etc/ssh/ /etc/ssh_8.2p1[root@localhost SPECS]# cp -p /etc/pam.d/sshd /etc/pam.d/sshd_8.2p1
使用构建的RPM包进行升级测试
在对原openssh配置相关文件备份后使用构建后的新版本OpenSSH_9.8p1软件包进行升级安装
#进入到构建的RPM包目录,使用yum localhost 命令进行安装升级[root@localhost SPECS]# cd /root/rpmbuild/RPMS/x86_64[root@localhost x86_64]# yum localinstall openssh* -y#升级后查看,可以确认升级后openssh相关包版本为OpenSSH_9.8p1[root@localhost x86_64]# rpm -qa | grep opensshopenssh-server-9.8p1-1.x86_64openssh-askpass-9.8p1-1.x86_64openssh-9.8p1-1.x86_64openssh-askpass-gnome-9.8p1-1.x86_64openssh-debugsource-9.8p1-1.x86_64openssh-debuginfo-9.8p1-1.x86_64openssh-clients-9.8p1-1.x86_64[root@localhost x86_64]# ssh -VOpenSSH_9.8p1, without OpenSSL
备份升级后的pam相关的sshd文件,然后使用升级前的备份文件
#使用备份文件恢复/etc/pam.d/sshd文件内容mv /etc/pam.d/sshd /etc/pam.d/sshd_9.8p1mv /etc/pam.d/sshd_8.2p1 /etc/pam.d/sshd
在重启一下ssh服务试试,确认均正常
[root@localhost pam.d]# systemctl restart sshd[root@localhost pam.d]# systemctl status sshd● sshd.service - SYSV: OpenSSH server daemonLoaded: loaded (/etc/rc.d/init.d/sshd; generated)Active: active (running) since Fri 2024-07-05 20:27:18 CST; 5s agoDocs: man:systemd-sysv-generator(8)Process: 14831 ExecStart=/etc/rc.d/init.d/sshd start (code=exited, status=0/SUCCESS)Main PID: 14840 (sshd)Tasks: 1Memory: 292.0KCGroup: /system.slice/sshd.service└─14840 sshd: /usr/sbin/sshd [listener] 0 of 10-100 startupsJul 05 20:27:18 localhost systemd[1]: Starting SYSV: OpenSSH server daemon...Jul 05 20:27:18 localhost sshd[14831]: Starting sshd:Jul 05 20:27:18 localhost sshd[14839]: Unable to load host key "/etc/ssh/ssh_host_rsa_key": unknown or unsupported key typeJul 05 20:27:18 localhost sshd[14839]: Unable to load host key: /etc/ssh/ssh_host_rsa_keyJul 05 20:27:18 localhost sshd[14839]: Unable to load host key "/etc/ssh/ssh_host_ecdsa_key": unknown or unsupported key typeJul 05 20:27:18 localhost sshd[14839]: Unable to load host key: /etc/ssh/ssh_host_ecdsa_keyJul 05 20:27:18 localhost sshd[14840]: Server listening on 0.0.0.0 port 22.Jul 05 20:27:18 localhost sshd[14840]: Server listening on :: port 22.Jul 05 20:27:18 localhost sshd[14831]: [ OK ]Jul 05 20:27:18 localhost systemd[1]: Started SYSV: OpenSSH server daemon.
最后进行连接测试确认无问题,能正常ssh登录。验证ssh相关命令,确认存在ssh-copy-id命令
[root@localhost SPECS]# ssss ssh ssh-add ssh-agent ssh-copy-id sshd ssh-keygen ssh-keyscan
一键构建openssh RPM工具推荐
除了上述自己通过rpmdevtools工具构建RPM的方式外,这里在推荐一个开源工具。使用该工具只需要执行脚本就行了,几乎属于一键构建。
github地址:https://github.com/boypt/openssh-rpms
支持的发行版(已完成测试)
CentOS 5/6/7/8/Stream 8/9
Amazon Linux 1/2/2023
UnionTech OS Server 20
openEuler 22.03 (LTS-SP1)
AnolisOS 7.9/8.6
上面是官方的说法,此次我基于openEuler 20.03 (LTS-SP4)构建的RPM也是可以使用的,其他的发行版请自行测试
准备编译环境
yum groupinstall -y "Development Tools"yum install -y imake rpm-build pam-devel krb5-devel zlib-devel libXt-devel libX11-devel gtk2-devel perl perl-IPC-Cmd# For CentOS5 only:yum install -y gcc44
工具下载
[root@localhost opt]# wget https://github.com/boypt/openssh-rpms/archive/refs/heads/main.zip[root@localhost opt]# unzip main.zip
解压后文件结构是这样子的
[root@localhost opt]# tree openssh-rpms-main/openssh-rpms-main/├── amzn1│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.amzn1│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── amzn2│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.amzn2│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── amzn2023│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.amzn2023│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── compile.sh├── docker│ ├── Dockerfile.amazonlinux│ ├── Dockerfile.centos│ ├── Dockerfile.centos-stream│ ├── modify_dnf_source.pl│ └── modify_yum_source.sh├── docker.README.md├── downloads│ └── x11-ssh-askpass-1.2.4.1.tar.gz├── el5│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.el5│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── el6│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.el6│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── el7│ ├── BUILD│ ├── RPMS│ ├── SOURCES│ │ └── sshd.pam.el7│ ├── SPECS│ │ └── openssh.spec│ └── SRPMS├── pullsrc.sh├── README.md└── version.env38 directories, 23 files
说明:
1、compile.sh:编译脚本,执行生成RPM包用的,特殊说明了下,要是5版本的系统需要./compile.sh el52、其中downloads里放的源码包3、x11-ssh-askpass-1.2.4.1.tar.gz4、el5/el6/el7对就要编译的系统版本,EL7/SPEC里面包含编译用到的文件5、RPMS里是存储编译好的RPM包5、sshd.pam.el7 就是对应操作系统里的/etc/pam.d里的模板文件原始版本6、openssh.spec。编译好的rpm包放在EL7/RPMS目录下,有能力的小伙伴可以在这里修改相关的参数。7、pullsrc.sh:openssh相关源码文件下载脚本,如果没有网的话,也可以手动下载源码包,不使用这个脚本8、version.env:定义了openssh及openssl源码的版本信息
源码包下载
在解压目录中运行pullsrc.sh脚本文件,这将会下载version.env文件中定义的源码包到downloads目录,一般包括openssl、openssh文件。x11-ssh-askpass-1.2.4.1.tar.gz官方已经很久没有发布新版本了,所以该文件默认已经存放到downloads目录了
[root@localhost openssh-rpms-main]# ./pullsrc.sh#上述脚步运行完成后可以发现downloads目录下存在多了openssh跟openssl文件,这就是编译要用到的源码文件[root@localhost openssh-rpms-main]# ll downloads/total 17M-rw-r--r-- 1 root root 1.9M Jul 1 15:34 openssh-9.8p1.tar.gz-rw-r--r-- 1 root root 15M Jun 4 23:04 openssl-3.0.14.tar.gz-rw-r--r-- 1 root root 29K Jul 5 17:52 x11-ssh-askpass-1.2.4.1.tar.gz
如果机器出不了外网,把对应的源码包下载下来存到
downloads目录也行
构建RPM
源码包下载完成后再解压目录运行compile.sh脚步文件,自动构建openssh RPM
[root@localhost openssh-rpms-main]# ./compile.sh
安装RPM
通过cd $(./compile.sh RPMDIR)命令转到生成的RPMS目录
[root@localhost openssh-rpms-main]# cd $(./compile.sh RPMDIR)[root@localhost x86_64]# pwd/opt/openssh-rpms-main/el7/RPMS/x86_64# 你会在这个目录下找到多个RPM文件,这即是编译成功的openssh相关RPM文件[root@localhost x86_64]# lsopenssh-9.8p1-1.x86_64.rpm openssh-clients-9.8p1-1.x86_64.rpm openssh-debuginfo-9.8p1-1.x86_64.rpm openssh-debugsource-9.8p1-1.x86_64.rpm openssh-server-9.8p1-1.x86_64.rpm# 备份当前ssh配置文件[[ -f /etc/ssh/sshd_config ]] && mv /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +%Y%m%d)# 安装rpm包。排除所有调试包find . ! -name '*debug*' -name '*.rpm' | xargs sudo yum --disablerepo=* localinstall -y# 修改主机秘钥文件权限chmod -v 600 /etc/ssh/ssh_host_*_key# For CentOS7+:# in some cases previously installed systemd unit file is left on disk after upgrade.# causes systemd mixing unit files and initscripts units provided by this package.if [[ -d /run/systemd/system && -f /usr/lib/systemd/system/sshd.service ]]; thenmv /usr/lib/systemd/system/sshd.service /usr/lib/systemd/system/sshd.service.$(date +%Y%m%d)systemctl daemon-reloadfi
检查验证
注意一定不要断开当前的 SSH 连接,直接打开一个新的 shell 并登录以验证 sshd 是否正常工作以及版本是否正常
# 检查安装版本[root@localhost x86_64]# ssh -V && /usr/sbin/sshd -VOpenSSH_9.8p1, OpenSSL 3.0.14 4 Jun 2024OpenSSH_9.8p1, OpenSSL 3.0.14 4 Jun 2024# 重启ssh服务再次验证[root@localhost x86_64]# systemctl restart sshd[root@localhost x86_64]# ssh -V && /usr/sbin/sshd -VOpenSSH_9.8p1, OpenSSL 3.0.14 4 Jun 2024OpenSSH_9.8p1, OpenSSL 3.0.14 4 Jun 2024#同时在验证是否能通过当前主机直接ssh连接其他主机,某些情况下可能因为一些算法的问题导致无法ssh连接其他主机[root@localhost x86_64]# ssh [username]@[ip]
注意事项
依赖报错
在yum localinstall过程中,可能会收到一些报错。这主要是因为一些子包依赖于主openssh包,仅升级主包将不适合它们的依赖关系。通常,在安装构建的rpm之前需要移除这些包。
yum erase openssh-askpass openssh-keycat openssh-cavs openssh-askpass openssh-askpass-gnome openssh-debuginfo openssh-help注意:
具体移除的软件包请根据报错视情况而定,而不是一键移除所有,除非你清楚的知道移除它们有什么影响。
强制安装
不推荐,除非确实解决不了依赖问题
rpm -ivh --force --nodeps --replacepkgs --replacefiles openssh-*.rpm安全笔记
以下是升级后常见的一些安全层面的修改,可根据实际情况按需配置
PubkeyAcceptedAlgorithms +ssh-rsaPermitRootLogin yesPasswordAuthentication yesUseDNS noUsePAM yes#如果需要支持旧算法,/etc/ssh/sshd_config中修改,之后重启SSHDKexAlgorithms -diffie-hellman-group1-sha1,diffie-hellman-group1-sha256,diffie-hellman-group14-sha1,diffie-hellman-group14-sha256,diffie-hellman-group15-sha256,diffie-hellman-group15-sha512,diffie-hellman-group16-sha256,diffie-hellman-group16-sha512,diffie-hellman-group17-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha512




