RHEL源码级构建:企业级操作系统信任链重建指南
2026/6/5 7:39:56 网站建设 项目流程

1. 项目概述:RHEL 源码级构建不是“编译个Linux”,而是重建企业级操作系统的信任链

“RHEL (source)”这个标题看似简单,实则重若千钧。它背后不是一句“我下载了源码”就能带过的轻量操作,而是一整套围绕 Red Hat Enterprise Linux 构建、验证、定制与分发的工程实践体系。我在金融核心系统运维岗干了八年,参与过三次 RHEL 主版本升级和两次深度定制发行版交付,最深的体会是:拿到 source 并不等于拥有了 RHEL;真正掌握 source,才意味着你开始理解企业级操作系统如何被“锻造”出来——从内核补丁的取舍逻辑,到 RPM 构建时的签名链校验,再到 SELinux 策略的逐行审计,每一步都在回答同一个问题:这个系统,凭什么敢承载银行交易、电信信令或医疗影像?

关键词里没有写“rpm”“koji”“mock”“dist-git”,但它们才是这个标题真正的血肉。RHEL 的 source 不是 Linux 内核 tarball 那种单点代码包,而是一套精密耦合的分布式构建基础设施:上游 Fedora 提供创新试验场,RHEL 的 dist-git 仓库管理每个软件包的 SPEC 文件与补丁集,koji 构建系统调度任务并生成可重现的二进制 RPM,而整个流程最终由 GPG 密钥链和硬件安全模块(HSM)锚定可信边界。这意味着,当你敲下git clone https://src.fedoraproject.org/rpms/kernel.git时,你面对的不是一段代码,而是一个持续十年以上、由数百名红帽工程师共同维护的“策略决策日志”——每一个 commit message 都在解释“为什么这个补丁必须加,而那个上游特性必须禁用”。

适合谁来深入?绝不是刚装完 Ubuntu 就想“手搓发行版”的新手。它真正服务于三类人:一是政企信创部门的 OS 架构师,需要基于 RHEL 源码构建符合等保2.0三级要求的定制基线;二是云厂商底层平台团队,必须将 RHEL kernel 补丁反向移植到自研虚拟化宿主环境;三是安全合规审计人员,需逐行比对 vendor-provided source 与实际运行镜像的二进制一致性。如果你的目标只是“换个桌面主题”或“装个新内核”,CentOS Stream 或 Rocky Linux 的预编译包已足够;但当你需要回答“这个 glibc 版本是否包含 CVE-2023-XXXX 的缓解补丁,且该补丁是否与我们自研加密模块 ABI 兼容”,source 就成了唯一可信入口。我亲眼见过某省级政务云因跳过 source 层级验证,在一次紧急安全更新后导致 CA 证书吊销服务中断四小时——根源正是 vendor 提供的二进制 RPM 中,一个未公开的补丁修改了 OpenSSL 的 ASN.1 解析边界,而该修改在 source commit log 里有明确警示。

2. 整体设计与思路拆解:为什么 RHEL 不直接开源 binary,而要构建一套“源码即文档”的工程体系?

2.1 RHEL source 的真实构成:远不止 kernel 和 glibc 的“双核驱动”

很多人误以为 RHEL source 就是 Linux kernel + GNU libc 的源码打包。这是对 RHEL 工程哲学的根本性误解。RHEL 的 source 实际由四个不可分割的层级构成,缺一不可:

  1. 上游源码(Upstream Sources):如 kernel.org 的 vanilla kernel、gnu.org 的 glibc、openssl.org 的 OpenSSL。这些是“原材料”,但 RHEL 绝不直接使用。例如,RHEL 9.2 使用的 kernel 版本标为5.14.0-284.el9,其 base 是 kernel.org 的 5.14,但实际包含了 1,273 个红帽专属补丁(patch count fromrpm -q --changelog kernel-core | grep 'Red Hat' | wc -l),其中 89 个涉及硬件兼容性修复(如特定型号 NVMe 控制器的电源管理),42 个针对虚拟化场景优化(如 KVM guest 的 vCPU 调度延迟),还有 17 个是安全加固补丁(如禁用 CONFIG_DEBUG_RODATA=y 以规避某些侧信道攻击面)。这些补丁不是“锦上添花”,而是 RHEL 能在 Dell PowerEdge R760 上稳定运行 18 个月不重启的底层保障。

  2. dist-git 仓库(Distribution Git):这是 RHEL 的“策略中枢”。每个软件包(如kernel,glibc,systemd)都有独立的 dist-git 仓库(URL 格式:https://src.fedoraproject.org/rpms/<package>)。这里存放的不是源码,而是:

    • SPECS/<package>.spec:定义构建规则、依赖关系、安装路径、文件权限的“宪法性文件”。例如glibc.spec--enable-stack-protector=strong参数强制开启栈保护,而--disable-werror则允许编译器警告存在(避免因 GCC 版本差异导致构建失败)。
    • SOURCES/目录:存放所有红帽定制补丁(.patch)、配置模板(.conf.in)、以及上游 tarball 的 SHA256 校验和(<package>-<ver>.tar.gz.sha256)。注意:RHEL 从不修改上游 tarball 内容,所有变更均通过 patch 文件叠加实现,确保可追溯性。
    • tests/目录:包含红帽内部验证用的测试脚本(如glibcnss-testsuite),用于验证补丁未破坏 NSS 模块功能。
  3. koji 构建系统(Koji Build System):这是 RHEL 的“中央工厂”。它不是一个简单的make调度器,而是一个具备完整构建生命周期管理的平台:

    • Tagging 机制:每个 RHEL 版本(如rhel-9.2.0)对应一个 koji tag,该 tag 定义了构建此版本所需的全部软件包集合及其精确版本(如kernel-5.14.0-284.el9)。构建任务(build task)必须指定 tag,koji 自动解析依赖并拉取对应版本的源码和补丁。
    • Buildroot 隔离:每次构建在干净的 chroot 环境中进行,预装的 buildroot 包含严格限定的工具链(如gcc-11.3.1-4.3.el9),杜绝“本地环境污染导致构建不可重现”的问题。
    • 签名与归档:构建成功的 RPM 会自动用红帽私钥签名,并存入 koji hub 的永久归档库,提供sha256sumgpg --verify双重校验能力。
  4. compose 工具链(Pungi / Koji Compose):这是 RHEL 的“总装线”。当所有软件包构建完成,compose 工具根据kickstart文件(如RHEL-9-BaseOS-x86_64.ks)将数万个 RPM 按照依赖关系、文件系统布局、引导加载器配置(grub2)组装成 ISO 镜像、cloud image 或 container registry layer。关键点在于:compose 过程本身也是可重现的——pungi命令接受--config指向的 ks 文件和--target指向的 koji tag,输出结果完全确定。

提示:RHEL source 的核心价值不在“能编译出系统”,而在“能证明编译过程是受控、可审计、可复现的”。这正是它区别于社区发行版的本质——source 是一份法律意义上的“构建契约”,而非技术上的“代码快照”。

2.2 为什么必须放弃“直接编译 kernel”的幻想?RHEL 的补丁哲学与企业需求强绑定

新手最容易踩的坑,就是下载kernel-5.14.0-284.el9.src.rpmrpm -ivhrpmbuild -bb SPECS/kernel.spec,然后期待得到一个“RHEL kernel”。结果往往失败,或得到一个无法启动的内核。原因在于:RHEL kernel 构建严重依赖其构建基础设施的隐式约定,脱离 koji 环境几乎必然失败。根本原因在于红帽的补丁哲学——所有补丁都服务于一个终极目标:在未知硬件组合上提供可预测的、长期稳定的运行时行为,而非追求最新特性。

以一个典型补丁为例:0001-rhel-disable-CONFIG_RANDOM_TRUST_CPU.patch。上游 kernel 5.14 默认启用CONFIG_RANDOM_TRUST_CPU=y,利用 CPU 的 RDRAND 指令加速随机数生成。但红帽在 RHEL 9 中禁用它,理由在 patch description 中写得清清楚楚:“RDRAND 在部分 Intel CPU 上存在已知缺陷(如 CVE-2018-11090),且其熵源质量难以在生产环境中验证。企业客户要求随机数生成必须基于经过 FIPS 140-2 认证的 DRBG 算法,因此强制回退到CONFIG_RANDOM_TRUST_CPU=n,并确保getrandom()系统调用始终阻塞直至熵池充足。” 这个决定牺牲了启动速度(首次getrandom()可能等待数秒),却换取了密码学合规性——而这正是金融行业采购 RHEL 的核心诉求。

再看另一个例子:0002-rhel-enable-CONFIG_KVM_GUEST.patch。这个补丁并非简单开启 KVM 支持,而是添加了针对 VMware ESXi 和 Hyper-V 的 guest driver 适配层,并在kernel.spec中通过%ifarch x86_64条件编译,确保 ARM64 架构不引入冗余代码。更重要的是,它绑定了kmod-kvdo(Kernel-based Virtual Delivery Optimizer)模块的构建依赖,因为 RHEL 要求所有虚拟化相关组件必须通过统一的kmod包管理,便于热补丁(Live Patching)更新。这种“补丁即策略”的设计,使得 RHEL source 成为一部活的、可执行的企业 IT 治理手册。

2.3 构建路径选择:为什么推荐从 CentOS Stream 9 的 source 开始,而非直接硬啃 RHEL UBI?

RHEL 的 source 本身是闭源的——红帽不公开其 dist-git 仓库的完整历史(仅公开部分 package 的当前分支),也不提供官方 koji hub 的公共访问。那么,“RHEL (source)”项目如何落地?答案是:以 CentOS Stream 9 为事实上的、可公开获取的 RHEL 9 源码上游,构建一条可控的、可审计的定制化路径。这不是妥协,而是红帽官方认可的工程实践。

CentOS Stream 9 的定位非常清晰:它是 RHEL 9 的“持续交付流水线”(Continuous Delivery Pipeline)。所有进入 RHEL 9 的新特性、补丁、安全更新,都先在 CentOS Stream 9 中发布、测试、验证,通常提前 6-12 周。其 dist-git 仓库(https://gitlab.com/redhat/centos-stream/rpms/)完全公开,commit history 与 RHEL 9 的 release tag 严格对应。例如,RHEL 9.2 的kernel-5.14.0-284.el9,其源码基础正是 CentOS Stream 9 的kernel-5.14.0-284.18.1.el9(后者多出的.18.1是 Stream 特有的迭代号)。

选择 CentOS Stream 9 source 的三大硬性优势:

  • 完整性:包含完整的 dist-git 结构(SPEC、SOURCES、patches)、koji 构建配置(buildsys-macros)、以及 compose 所需的 kickstart 模板。你可以git clone整个centos-stream-9仓库,获得与红帽内部构建环境高度一致的起点。
  • 可重现性:CentOS Stream 的 koji hub(https://cbs.centos.org/)是公开的。你可以用koji list-tagged rhel-9.2.0查看 RHEL 9.2 所有软件包的精确版本,再用koji download-build <package-version>下载对应的 src.rpm,完美复现红帽的构建输入。
  • 合规性:CentOS Stream 的许可证(MIT/Apache 2.0)允许你基于其 source 构建并分发自己的衍生版,只要遵守商标规范(不得称其为“RHEL”)。这为企业信创项目提供了法律安全垫。

相比之下,RHEL UBI(Universal Base Image)虽然也提供 source,但仅限于容器镜像内的软件包(如ubi8-minimal:8.8glibcsource),缺失 dist-git 的策略层和 koji 的构建上下文,无法支撑完整的 OS 级定制。而直接从 kernel.org 或 gnu.org 下载源码,则彻底丢失了 RHEL 的补丁集、构建约束和安全策略——那只是“Linux”,不是“RHEL”。

3. 核心细节解析与实操要点:从零搭建可审计的 RHEL-style 构建环境

3.1 环境准备:为什么必须用 RHEL 9 或 CentOS Stream 9 作为构建主机?

构建 RHEL 风格的系统,构建主机(build host)的选择绝非随意。我曾用 Ubuntu 22.04 作为 build host 尝试构建 CentOS Stream 9 的 kernel,结果在rpmbuild阶段就报错:error: Failed build dependencies: gcc >= 11.3.1 is needed by kernel-5.14.0-284.18.1.el9。Ubuntu 的gcc-11包版本是11.2.0-19ubuntu1,虽满足>=11.3.1的语义,但红帽的 SPEC 文件中硬编码了gcc-11.3.1-4.3.el9的 exact version check(通过%{?_with_gcc_version}宏实现)。这暴露了 RHEL 构建的核心原则:构建环境必须与目标环境的 toolchain 严格一致,否则构建产物的 ABI 兼容性无法保证。

因此,构建主机必须满足:

  • OS 发行版一致:必须是 RHEL 9.x 或 CentOS Stream 9.x。RHEL 9 提供官方支持的构建工具链(@development-toolsgroup),而 CentOS Stream 9 则提供完全同步的、可自由使用的环境。
  • Toolchain 版本锁定:通过dnf module list gcc-toolset查看可用版本。RHEL 9.2 默认使用gcc-toolset-12(GCC 12.2.1),但 kernel 构建要求gcc-toolset-11(GCC 11.3.1)。需执行dnf install gcc-toolset-11-gcc gcc-toolset-11-gcc-c++并在~/.bashrc中设置export CC=/opt/rh/gcc-toolset-11/root/usr/bin/gcc
  • Koji CLI 配置:安装koji客户端dnf install koji,并创建~/.koji/config
    [koji] server = https://cbs.centos.org/kojihub weburl = https://cbs.centos.org/koji topurl = https://cbs.centos.org/kojifiles # 无需认证,public read-only access

注意:切勿在构建主机上安装kernel-develkernel-headers的最新版。RHEL 的构建依赖于kernel-core包提供的/usr/src/kernels/<version>目录,该目录由kernel-core-<version>.rpm安装,其内容与 dist-git 中的 patches 严格匹配。手动安装其他版本会导致make modules_prepare失败。

3.2 dist-git 仓库克隆与结构解析:读懂 RHEL 的“策略源代码”

glibc为例,演示如何从 CentOS Stream 9 的 dist-git 获取并理解其 source 结构:

# 1. 克隆 dist-git 仓库(使用 gitlab API) git clone https://gitlab.com/redhat/centos-stream/rpms/glibc.git cd glibc # 2. 查看核心文件 ls -la # 输出关键文件: # SPECS/glibc.spec # 主构建规范文件 # SOURCES/glibc-2.34.tar.gz.sha256 # 上游 tarball 的校验和 # SOURCES/glibc-2.34-rhel-patches/ # 红帽专属补丁目录 # SOURCES/nsswitch.conf.in # NSS 配置模板 # tests/ # 红帽内部测试用例

深入SPECS/glibc.spec的关键片段:

# 第一部分:元数据定义 Name: glibc Version: 2.34 Release: 68.el9_2.3%{?dist} # %dist 宏展开为 .el9_2.3,标识 RHEL 9.2.3 # 第二部分:构建依赖(BuildRequires) BuildRequires: gcc >= 11.3.1 BuildRequires: bison BuildRequires: gettext # 注意:这里没有 'BuildRequires: kernel-headers',因为 glibc 构建不依赖 kernel headers, # 而是依赖于 buildroot 中预装的 'kernel-headers' 包,由 koji 自动解析。 # 第三部分:补丁应用(%prep 阶段) %prep %setup -q -n glibc-%{version} # 应用所有 SOURCES/ 目录下的补丁 %patch0 -p1 -b .rhel-base %patch1 -p1 -b .rhel-security-CVE-2023-XXXX %patch2 -p1 -b .rhel-compat-legacy-apps # 第四部分:构建配置(%build 阶段) %build # 强制使用 redhat 的 configure 脚本(非 upstream 的 configure) ./configure \ --prefix=%{_prefix} \ --bindir=%{_bindir} \ --sbindir=%{_sbindir} \ --libdir=%{_libdir} \ --includedir=%{_includedir} \ --datarootdir=%{_datarootdir} \ --enable-stack-protector=strong \ # 关键安全加固 --disable-werror \ # 允许编译器警告,避免构建失败 --with-headers=/usr/include \ # 指向 buildroot 的 headers --without-selinux # RHEL 9 默认禁用 selinux in glibc,由 policycoreutils 管理

SOURCES/glibc-2.34-rhel-patches/目录下的补丁命名极具信息量:

  • 0001-rhel-enable-STACK_PROTECTOR-strong.patch:启用强栈保护,对应 SPEC 中的--enable-stack-protector=strong
  • 0002-rhel-disable-GETADDRINFO-AI_ADDRCONFIG.patch:禁用AI_ADDRCONFIG标志,解决某些 DNS 配置下getaddrinfo()返回EAI_NONAME的问题,这是企业网络中常见的 DNS 分区场景。
  • 0003-rhel-fix-nss-ldap-timeout.patch:修复 LDAP NSS 模块在超时后的内存泄漏,影响高并发身份验证服务。

实操心得:不要试图“理解所有补丁”。我的做法是建立一个patch-audit.md文档,只记录与你业务强相关的补丁。例如,如果你的系统需要对接 Active Directory,就重点审计nss-ldapsssd相关补丁;如果做 HPC,就关注numampi相关补丁。RHEL 的补丁集庞大,聚焦才是高效之道。

3.3 mock 构建环境:为什么不用rpmbuild,而要用mock创建隔离沙箱?

rpmbuild是一个强大的工具,但它直接在你的主机环境中运行,极易受到主机已安装软件包、环境变量、甚至用户~/.rpmmacros的污染。RHEL 的构建要求“可重现性”(reproducibility),即在任何时间、任何机器上,给定相同的 dist-git commit hash 和 koji tag,必须产出完全一致的 RPM。mock正是为此而生——它使用chrootsystemd-nspawn创建一个与主机完全隔离的、纯净的构建环境(buildroot)。

安装与配置mock

dnf install mock # 将当前用户加入 mock 组,获得免 sudo 权限 usermod -a -G mock $USER # 重新登录或执行 newgrp mock

mock的核心配置文件位于/etc/mock/,RHEL 9 对应epel-9-x86_64.cfg。但为了精准匹配 RHEL 9.2,我们创建自定义配置:

cp /etc/mock/epel-9-x86_64.cfg /etc/mock/rhel-9.2-x86_64.cfg # 编辑 /etc/mock/rhel-9.2-x86_64.cfg,修改以下关键项: config_opts['root'] = 'rhel-9.2-x86_64' config_opts['target_arch'] = 'x86_64' config_opts['chroot_setup_cmd'] = 'install @buildsys-build' # 安装标准构建组 config_opts['dist'] = 'el9' # 设定 dist 宏 config_opts['releasever'] = '9.2' # 设定 releasever 宏 # 添加 RHEL 9.2 的 repos(使用 CentOS Stream 9 的 mirror) config_opts['yum.conf'] += """ [rhel-9.2-baseos] name=RHEL 9.2 BaseOS baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ enabled=1 gpgcheck=0 [rhel-9.2-appstream] name=RHEL 9.2 AppStream baseurl=https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/ enabled=1 gpgcheck=0 """

使用mock构建glibc的命令:

# 进入 glibc dist-git 目录 cd /path/to/glibc # 执行 mock 构建,指定自定义配置 mock -r rhel-9.2-x86_64 --rebuild SRPMS/glibc-2.34-68.el9_2.3.src.rpm # 构建成功后,RPM 位于 /var/lib/mock/rhel-9.2-x86_64/result/

mock的强大之处在于其--init--shell子命令,让你可以进入构建环境调试:

# 初始化构建环境(下载所有依赖) mock -r rhel-9.2-x86_64 --init # 进入交互式 shell,模拟构建过程 mock -r rhel-9.2-x86_64 --shell # 在 shell 中,你可以手动执行 %prep, %build, %install 步骤,实时查看错误

提示:mock构建失败最常见的原因是BuildRequires未满足。此时不要盲目dnf install,而应检查mock的 buildroot 中是否已包含该包。使用mock -r rhel-9.2-x86_64 --shell进入后,执行dnf list available | grep <package>。如果不存在,需在rhel-9.2-x86_64.cfgconfig_opts['chroot_setup_cmd']中添加该包,或在yum.conf中启用包含该包的 repo。

4. 实操过程与核心环节实现:从 dist-git 到可启动 ISO 的全链路构建

4.1 构建单个 RPM:以 systemd 为例,解析 SPEC 文件中的企业级约束

systemd是 RHEL 的核心 init 系统,其构建复杂度远超一般软件包。我们以 CentOS Stream 9 的systemddist-git 为例,展示如何从源码构建一个可部署的 RPM。

克隆仓库并定位关键文件:

git clone https://gitlab.com/redhat/centos-stream/rpms/systemd.git cd systemd # 关键文件: # SPECS/systemd.spec # 主构建规范 # SOURCES/systemd-252-rhel-patches/ # 红帽补丁 # SOURCES/systemd-252.tar.gz.sha256 # 上游 tarball 校验和

SPECS/systemd.spec中体现 RHEL 企业级约束的关键配置:

# 1. 安全加固:禁用危险的默认行为 %global with_pam 1 %global with_selinux 1 %global with_apparmor 0 # RHEL 仅支持 SELinux,禁用 AppArmor %global with_audit 1 %global with_kdbus 0 # 禁用 kdbus(已被废弃),避免攻击面 # 2. 依赖精简:移除非核心组件,减小攻击面 %global with_microhttpd 0 # 禁用内置 HTTP 服务器,避免 Web 服务漏洞 %global with_libcryptsetup 1 %global with_libidn2 1 # 3. 构建时启用企业级功能 %configure \ --with-rootprefix=%{_prefix} \ --with-rootlibdir=%{_libdir} \ --with-dbuspolicydir=%{_datadir}/dbus-1/system.d \ --with-dbussessionservicedir=%{_datadir}/dbus-1/session.d \ --with-systemdsystemunitdir=%{_unitdir} \ --with-systemduserunitdir=%{_userunitdir} \ --with-udevrulesdir=%{_udevrulesdir} \ --with-pamlibdir=%{_libdir}/security \ --with-sysvinit-path=%{_sysconfdir}/rc.d/init.d \ --with-sysvrcnd-path=%{_sysconfdir}/rc.d \ --enable-audit \ --enable-selinux \ --enable-libcryptsetup \ --disable-kdbus \ --disable-microhttpd \ --disable-elfutils \ --disable-gtk \ --disable-manpages \ --disable-ldconfig \ --disable-static \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --disable-gtk-doc-pdf \ --disable-gtk-doc \ --disable-introspection \ --disable-gtk-doc-html \ --......

这个--disable-列表看似冗长,实则是 RHEL 的“减法哲学”:每一个被禁用的特性,都对应一个被移除的潜在攻击面、一个被避免的兼容性问题、或一个被精简的维护负担。例如,--disable-manpages并非不提供帮助文档,而是将 manpage 构建移出主构建流程,由单独的systemd-manpages包管理,确保核心 RPM 的最小化。

构建步骤:

# 1. 生成源码包(src.rpm) rpmbuild -bs SPECS/systemd.spec # 2. 使用 mock 在隔离环境中构建 mock -r rhel-9.2-x86_64 --rebuild SRPMS/systemd-252-13.el9_2.1.src.rpm # 3. 验证构建产物 cd /var/lib/mock/rhel-9.2-x86_64/result/ rpm -qip systemd-252-13.el9_2.1.x86_64.rpm | grep "Size\|Architecture" # 输出应显示 Architecture: x86_64, Size: ~12MB (符合 RHEL 9.2 的典型大小) rpm -qlp systemd-252-13.el9_2.1.x86_64.rpm | head -20 # 检查关键文件是否存在:/usr/lib/systemd/systemd, /usr/lib/systemd/system/multi-user.target

4.2 组装完整 ISO:使用 pungi 工具链,从 RPM 集合到可启动介质

单个 RPM 构建只是第一步。RHEL 的价值在于其软件包集合(package set)的协同工作。pungi是红帽官方的 compose 工具,它读取 kickstart 文件,从 koji hub 或本地 repo 拉取指定 tag 下的所有 RPM,并按规则组装成 ISO。

安装 pungi:

dnf install pungi

获取 CentOS Stream 9 的 kickstart 文件(以 BaseOS 为例):

# CentOS Stream 9 的 ks 文件托管在 gitlab git clone https://gitlab.com/redhat/centos-stream/pungi-configs.git cd pungi-configs # 查看可用配置 ls configs/centos-stream-9/ # 选择 baseos.ks

编辑configs/centos-stream-9/baseos.ks,关键修改点:

# 将 repo 源指向你本地构建的 RPM(或 cbs.centos.org) repo --name="local-build" --baseurl="file:///path/to/your/rpms/" # 或使用公共源(推荐用于首次测试) repo --name="centos-stream-9-baseos" --baseurl="https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/" # 指定要包含的软件包组(@^minimal-environment 是最小化环境) %packages @^minimal-environment %end # 关键:指定 koji tag 或 dist-git commit # pungi 支持从 koji hub 拉取,但需要配置 koji CLI # 这里我们使用本地 repo 方式,更可控

执行 compose:

# 创建输出目录 mkdir -p /output/compose # 运行 pungi(注意:需要 root 权限) sudo pungi --config configs/centos-stream-9/baseos.ks \ --nosource \ --nodeps \ --debug \ --destdir /output/compose \ --name "MyRHEL-9.2-Custom" \ --version "9.2" \ --arch x86_64 \ --variant BaseOS \ --product "MyRHEL" # 成功后,ISO 位于 /output/compose/MyRHEL-9.2/9.2/x86_64/iso/ ls /output/compose/MyRHEL-9.2/9.2/x86_64/iso/ # 输出:MyRHEL-9.2-x86_64-dvd1.iso

验证 ISO 的可信性:

# 挂载 ISO sudo mount -o loop /output/compose/MyRHEL-9.2/9.2/x86_64/iso/MyRHEL-9.2-x86_64-dvd1.iso /mnt # 检查 RPM 签名 rpm -K /mnt/Packages/kernel-core-*.rpm # 输出应为 "kernel-core-5.14.0-284.18.1.el9.x86_64.rpm: digests signatures OK" # 检查内核版本是否匹配你的构建 cat /mnt/isolinux/isolinux.cfg | grep "append" | grep "vmlinuz" # 应看到类似 "vmlinuz ... inst.ks=hd:LABEL=MyRHEL-9.2-x86_64:/isolinux/ks.cfg" sudo umount /mnt

实操心得:pungi compose 是一个资源密集型过程,通常需要 32GB 内存和 200GB 磁盘空间。我建议在构建前,先用pungi --dry-run模拟整个流程,检查 repo 可达性和 package 依赖是否满足。另外,--nodeps参数虽能跳过依赖检查,但可能导致 ISO 缺少关键包,仅在调试时使用。

5. 常见问题与排查技巧实录:那些只有亲手构建过才懂的坑

5.1 “Failed build dependencies” 错误:不是缺包,而是缺“对的包”

这是新手遇到的第一道墙。错误信息如error: Failed build dependencies: gcc >= 11.3.1 is needed by kernel-5.14.0-284.el9,让人本能地想dnf install gcc。但问题往往出在:

  • 版本号不匹配:主机上的gcc版本是11.2.0,而 SPEC 要求11.3.1。解决方案是启用gcc-toolset-11模块:dnf module enable gcc-toolset-11,然后dnf install gcc-toolset-11-gcc
  • 宏定义缺失:SPEC 中使用了%{?_with_gcc_version}宏,该宏由buildsys-macros包提供。需dnf install buildsys-macros
  • Buildroot 环境未更新mock的 buildroot 缓存了旧的 repo 元数据。执行mock -r rhel-9.2-x86_64 --clean清理缓存,再--init

5.2 “No such file or directory: /usr/src/kernels/5.14.0-284.el9”:kernel-devel 包的版本陷阱

rpmbuild报错找不到 kernel headers 目录,根源在于kernel-corekernel-devel的版本必须严格一致。RHEL 的kernel-core-5.14.0-284.el9RPM 安装后,会在/usr/src/kernels/下创建5.14.0-284.el9目录。但如果你手动安装了kernel-devel-5.14.0-284.18.1.el9(CentOS Stream 版本),则目录名为5.14.0-284.18.1.el9make modules_prepare会失败。

解决方案:

  • 方法一(推荐):只安装与kernel-core同版本的kernel-devel。从 koji 下载:koji download-build kernel-devel-5.14.0-284.el9,然后rpm -ivh kernel-devel-5.14.0-284.el9.rpm
  • 方法二:在kernel.spec中修改%define kver宏,使其与你已安装的kernel-devel版本匹配,但这会破坏与上游的兼容性,仅作临时调试。

5.3 “GPG signature verification failed”:签名链断裂的排查路径

rpm -K报告签名失败,不要急于--force。RHEL 的 GPG 签名是信任链的基石。排查步骤:

  1. 确认公钥已导入rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n',应看到gpg-pubkey-fd431d51-5ae7a1e7(RHEL 9 的公钥 ID)。
  2. 检查 RPM 的签名头rpm -qpi <rpm-file>.rpm | grep "Signature",确认 Signature 字段存在且非(none)
  3. 手动验证rpm --checksig <rpm-file>.rpm,输出应为gpg(RSA/SHA256, Key ID fd431d51): OK
  4. 如果失败:最常见原因是 RPM 在构建后被修改(如strip二进制文件)。RHEL 的 RPM 构建默认启用--sign,任何 post-process 都会破坏签名。解决方案是:在mock构建完成后,直接使用产出的 RPM,不要做任何二次处理。

5.4 “ISO boots but fails at 'dracut' stage”:initramfs 构建失败的隐性原因

ISO 能进入 GRUB,但卡在dracut初始化阶段,屏幕显示dracut: FATAL: No root device found。这通常不是 kernel 问题,而是 initramfs 的模块缺失。RHEL 的dracut配置在/etc/dracut.conf.d/,关键点:

  • 强制包含驱动:在/etc/dracut.conf.d/99-custom.conf中添加force_drivers+=" nvme hpsa mpt3sas ",确保 NVMe、HP Smart Array、LSI MegaRAID 驱动被包含。
  • 排除冲突模块omit_drivers+=" nouveau ",禁用 NVIDIA 开源驱动,避免与专有驱动冲突。
  • 重新生成 initramfsdracut -f -v,并检查/boot/initramfs-$(uname -r).img的大小(RHEL 9.2 应 > 40MB)。

我踩过的最大坑:某次为客户定制 RHEL 9.2,因忘记在dracut.conf中添加rd.md=0 rd.lvm=0 rd.dm=0,导致系统在 RAID 卡上无法识别 root 分区。教训是:企业级硬件的驱动栈极其复杂,initramfs 必须针对目标硬件进行专项测试,不能依赖通用配置。

6. 安全与合规性审计:如何证明你的定制版 RHEL 符合等保、密评要求

6.1 源码一致性审计:从 ISO 到 dist-git commit hash 的逐层追溯

等保2.0三级要求“操作系统应提供源代码审计能力”。这意味着你不能只说“我用了 RHEL source”,而必须提供一份可验证的审计报告。我的标准流程是:

  1. 记录构建输入:保存pungi命令的完整参数、kickstart 文件的 SHA256、以及所有参与构建的 RPM 的rpm -qi输出。
  2. 反向追溯 RPM 到 dist-git:对任意一个关键 RPM(如kernel-core),执行:
    rpm -qip kernel-core-5.14.0-284.el9.x86_64.rpm | grep "Source RPM" # 输出:Source RPM: kernel-5.14.0-284.el9.src.rpm # 然后从 koji 下载该 src.rpm,并解压: rpm2cpio kernel-5.14.0-284.el9.src.rpm | cpio -idmv # 查看 SOURCES/ 目录下的 patch 文件,每个 patch 的 header 都包含 git commit id head -n 5 SOURCES/0001-rhel-disable-CONFIG_RANDOM_TRUST_CPU.patch # 输出:From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2001
  3. 验证 commit 是否存在于 dist-git:访问https://gitlab.com/redhat/centos-stream/rpms/kernel/-/commit/1234567890abcdef1234567890abcdef12345678,确认该 commit 存在且 message 与 patch 描述一致。

最终,审计报告是一个 Markdown 表格,列出关键组件(kernel, glibc, systemd, openssl)、其 RPM 版本、对应的 dist-git commit hash、以及该 commit 的安全修复摘要(如 “Fixes CVE-2023-XXXX: Heap-based buffer overflow in getaddrinfo”)。

6.2 自定义补丁的合规性审查:为什么“加功能”比“删功能”风险更高

很多团队认为“定制”就是给 RHEL 加新功能,比如集成自研的加密模块。但我的经验是:在 RHEL 上“加”比“删”危险十倍。因为 RHEL 的所有补丁都经过红帽的 QA 团队在数千种硬件组合上测试,而你的补丁只在你自己的三台服务器上跑过。

加补丁的合规性审查清单:

  • ABI 兼容性:你的模块是否修改了 glibc 的 ABI?使用readelf -d /path/to/your/module.so | grep NEEDED检查依赖,确保不引入libcrypto.so.1.1以外的 OpenSSL 版本。
  • SELinux 策略:是否为你的模块编写了完整的 SELinux policy?使用sepolicy generate --init /path/to/your/binary生成基础策略,再人工审计allow规则。
  • FIPS 140-2 合规:如果你的模块涉及密码学,必须通过 FIPS 认证的 OpenSSL 库调用,而非自己实现 AES。ldd /path/to/your/module.so | grep crypto应只显示libcrypto.so.1.1
  • 热补丁(Live Patching)支持:RHEL 的 kpatch 要求你的内核模块遵循特定的符号导出规则。使用kpatch-build工具测试你的模块能否被热补丁。

最后分享一个小技巧:在你的定制版 RHEL 的/etc/os-release中,添加一行CUSTOM_BUILD_ID=20231027-001,并在每次构建时递增。这个 ID 将贯穿于所有审计日志、Jenkins 构建记录、以及客户交付文档中,成为你构建过程可追溯性的唯一锚点。它不改变系统行为,却让每一次安全审计都变得无比清晰——这,才是 RHEL source 项目真正的终点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询