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 实际由四个不可分割的层级构成缺一不可上游源码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_RODATAy 以规避某些侧信道攻击面。这些补丁不是“锦上添花”而是 RHEL 能在 Dell PowerEdge R760 上稳定运行 18 个月不重启的底层保障。dist-git 仓库Distribution Git这是 RHEL 的“策略中枢”。每个软件包如kernel,glibc,systemd都有独立的 dist-git 仓库URL 格式https://src.fedoraproject.org/rpms/package。这里存放的不是源码而是SPECS/package.spec定义构建规则、依赖关系、安装路径、文件权限的“宪法性文件”。例如glibc.spec中--enable-stack-protectorstrong参数强制开启栈保护而--disable-werror则允许编译器警告存在避免因 GCC 版本差异导致构建失败。SOURCES/目录存放所有红帽定制补丁.patch、配置模板.conf.in、以及上游 tarball 的 SHA256 校验和package-ver.tar.gz.sha256。注意RHEL 从不修改上游 tarball 内容所有变更均通过 patch 文件叠加实现确保可追溯性。tests/目录包含红帽内部验证用的测试脚本如glibc的nss-testsuite用于验证补丁未破坏 NSS 模块功能。koji 构建系统Koji Build System这是 RHEL 的“中央工厂”。它不是一个简单的make调度器而是一个具备完整构建生命周期管理的平台Tagging 机制每个 RHEL 版本如rhel-9.2.0对应一个 koji tag该 tag 定义了构建此版本所需的全部软件包集合及其精确版本如kernel-5.14.0-284.el9。构建任务build task必须指定 tagkoji 自动解析依赖并拉取对应版本的源码和补丁。Buildroot 隔离每次构建在干净的 chroot 环境中进行预装的 buildroot 包含严格限定的工具链如gcc-11.3.1-4.3.el9杜绝“本地环境污染导致构建不可重现”的问题。签名与归档构建成功的 RPM 会自动用红帽私钥签名并存入 koji hub 的永久归档库提供sha256sum和gpg --verify双重校验能力。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 -ivh后rpmbuild -bb SPECS/kernel.spec然后期待得到一个“RHEL kernel”。结果往往失败或得到一个无法启动的内核。原因在于RHEL kernel 构建严重依赖其构建基础设施的隐式约定脱离 koji 环境几乎必然失败。根本原因在于红帽的补丁哲学——所有补丁都服务于一个终极目标在未知硬件组合上提供可预测的、长期稳定的运行时行为而非追求最新特性。以一个典型补丁为例0001-rhel-disable-CONFIG_RANDOM_TRUST_CPU.patch。上游 kernel 5.14 默认启用CONFIG_RANDOM_TRUST_CPUy利用 CPU 的 RDRAND 指令加速随机数生成。但红帽在 RHEL 9 中禁用它理由在 patch description 中写得清清楚楚“RDRAND 在部分 Intel CPU 上存在已知缺陷如 CVE-2018-11090且其熵源质量难以在生产环境中验证。企业客户要求随机数生成必须基于经过 FIPS 140-2 认证的 DRBG 算法因此强制回退到CONFIG_RANDOM_TRUST_CPUn并确保getrandom()系统调用始终阻塞直至熵池充足。” 这个决定牺牲了启动速度首次getrandom()可能等待数秒却换取了密码学合规性——而这正是金融行业采购 RHEL 的核心诉求。再看另一个例子0002-rhel-enable-CONFIG_KVM_GUEST.patch。这个补丁并非简单开启 KVM 支持而是添加了针对 VMware ESXi 和 Hyper-V 的 guest driver 适配层并在kernel.spec中通过%ifarch x86_64条件编译确保 ARM64 架构不引入冗余代码。更重要的是它绑定了kmod-kvdoKernel-based Virtual Delivery Optimizer模块的构建依赖因为 RHEL 要求所有虚拟化相关组件必须通过统一的kmod包管理便于热补丁Live Patching更新。这种“补丁即策略”的设计使得 RHEL source 成为一部活的、可执行的企业 IT 治理手册。2.3 构建路径选择为什么推荐从 CentOS Stream 9 的 source 开始而非直接硬啃 RHEL UBIRHEL 的 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 hubhttps://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 UBIUniversal Base Image虽然也提供 source但仅限于容器镜像内的软件包如ubi8-minimal:8.8的glibcsource缺失 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-12GCC 12.2.1但 kernel 构建要求gcc-toolset-11GCC 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-devel或kernel-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-protectorstrong \ # 关键安全加固 --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-protectorstrong。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-ldap和sssd相关补丁如果做 HPC就关注numa和mpi相关补丁。RHEL 的补丁集庞大聚焦才是高效之道。3.3 mock 构建环境为什么不用rpmbuild而要用mock创建隔离沙箱rpmbuild是一个强大的工具但它直接在你的主机环境中运行极易受到主机已安装软件包、环境变量、甚至用户~/.rpmmacros的污染。RHEL 的构建要求“可重现性”reproducibility即在任何时间、任何机器上给定相同的 dist-git commit hash 和 koji tag必须产出完全一致的 RPM。mock正是为此而生——它使用chroot或systemd-nspawn创建一个与主机完全隔离的、纯净的构建环境buildroot。安装与配置mockdnf install mock # 将当前用户加入 mock 组获得免 sudo 权限 usermod -a -G mock $USER # 重新登录或执行 newgrp mockmock的核心配置文件位于/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] nameRHEL 9.2 BaseOS baseurlhttps://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ enabled1 gpgcheck0 [rhel-9.2-appstream] nameRHEL 9.2 AppStream baseurlhttps://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/ enabled1 gpgcheck0 使用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.cfg的config_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.target4.2 组装完整 ISO使用 pungi 工具链从 RPM 集合到可启动介质单个 RPM 构建只是第一步。RHEL 的价值在于其软件包集合package set的协同工作。pungi是红帽官方的 compose 工具它读取 kickstart 文件从 koji hub 或本地 repo 拉取指定 tag 下的所有 RPM并按规则组装成 ISO。安装 pungidnf 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 --namelocal-build --baseurlfile:///path/to/your/rpms/ # 或使用公共源推荐用于首次测试 repo --namecentos-stream-9-baseos --baseurlhttps://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.kshd:LABELMyRHEL-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-core和kernel-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.el9CentOS 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 签名是信任链的基石。排查步骤确认公钥已导入rpm -q gpg-pubkey --qf %{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n应看到gpg-pubkey-fd431d51-5ae7a1e7RHEL 9 的公钥 ID。检查 RPM 的签名头rpm -qpi rpm-file.rpm | grep Signature确认 Signature 字段存在且非(none)。手动验证rpm --checksig rpm-file.rpm输出应为gpg(RSA/SHA256, Key ID fd431d51): OK。如果失败最常见原因是 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.md0 rd.lvm0 rd.dm0导致系统在 RAID 卡上无法识别 root 分区。教训是企业级硬件的驱动栈极其复杂initramfs 必须针对目标硬件进行专项测试不能依赖通用配置。6. 安全与合规性审计如何证明你的定制版 RHEL 符合等保、密评要求6.1 源码一致性审计从 ISO 到 dist-git commit hash 的逐层追溯等保2.0三级要求“操作系统应提供源代码审计能力”。这意味着你不能只说“我用了 RHEL source”而必须提供一份可验证的审计报告。我的标准流程是记录构建输入保存pungi命令的完整参数、kickstart 文件的 SHA256、以及所有参与构建的 RPM 的rpm -qi输出。反向追溯 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验证 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_ID20231027-001并在每次构建时递增。这个 ID 将贯穿于所有审计日志、Jenkins 构建记录、以及客户交付文档中成为你构建过程可追溯性的唯一锚点。它不改变系统行为却让每一次安全审计都变得无比清晰——这才是 RHEL source 项目真正的终点。
RHEL源码级构建:企业级操作系统信任链重建指南
发布时间:2026/6/5 7:40:09
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 实际由四个不可分割的层级构成缺一不可上游源码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_RODATAy 以规避某些侧信道攻击面。这些补丁不是“锦上添花”而是 RHEL 能在 Dell PowerEdge R760 上稳定运行 18 个月不重启的底层保障。dist-git 仓库Distribution Git这是 RHEL 的“策略中枢”。每个软件包如kernel,glibc,systemd都有独立的 dist-git 仓库URL 格式https://src.fedoraproject.org/rpms/package。这里存放的不是源码而是SPECS/package.spec定义构建规则、依赖关系、安装路径、文件权限的“宪法性文件”。例如glibc.spec中--enable-stack-protectorstrong参数强制开启栈保护而--disable-werror则允许编译器警告存在避免因 GCC 版本差异导致构建失败。SOURCES/目录存放所有红帽定制补丁.patch、配置模板.conf.in、以及上游 tarball 的 SHA256 校验和package-ver.tar.gz.sha256。注意RHEL 从不修改上游 tarball 内容所有变更均通过 patch 文件叠加实现确保可追溯性。tests/目录包含红帽内部验证用的测试脚本如glibc的nss-testsuite用于验证补丁未破坏 NSS 模块功能。koji 构建系统Koji Build System这是 RHEL 的“中央工厂”。它不是一个简单的make调度器而是一个具备完整构建生命周期管理的平台Tagging 机制每个 RHEL 版本如rhel-9.2.0对应一个 koji tag该 tag 定义了构建此版本所需的全部软件包集合及其精确版本如kernel-5.14.0-284.el9。构建任务build task必须指定 tagkoji 自动解析依赖并拉取对应版本的源码和补丁。Buildroot 隔离每次构建在干净的 chroot 环境中进行预装的 buildroot 包含严格限定的工具链如gcc-11.3.1-4.3.el9杜绝“本地环境污染导致构建不可重现”的问题。签名与归档构建成功的 RPM 会自动用红帽私钥签名并存入 koji hub 的永久归档库提供sha256sum和gpg --verify双重校验能力。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 -ivh后rpmbuild -bb SPECS/kernel.spec然后期待得到一个“RHEL kernel”。结果往往失败或得到一个无法启动的内核。原因在于RHEL kernel 构建严重依赖其构建基础设施的隐式约定脱离 koji 环境几乎必然失败。根本原因在于红帽的补丁哲学——所有补丁都服务于一个终极目标在未知硬件组合上提供可预测的、长期稳定的运行时行为而非追求最新特性。以一个典型补丁为例0001-rhel-disable-CONFIG_RANDOM_TRUST_CPU.patch。上游 kernel 5.14 默认启用CONFIG_RANDOM_TRUST_CPUy利用 CPU 的 RDRAND 指令加速随机数生成。但红帽在 RHEL 9 中禁用它理由在 patch description 中写得清清楚楚“RDRAND 在部分 Intel CPU 上存在已知缺陷如 CVE-2018-11090且其熵源质量难以在生产环境中验证。企业客户要求随机数生成必须基于经过 FIPS 140-2 认证的 DRBG 算法因此强制回退到CONFIG_RANDOM_TRUST_CPUn并确保getrandom()系统调用始终阻塞直至熵池充足。” 这个决定牺牲了启动速度首次getrandom()可能等待数秒却换取了密码学合规性——而这正是金融行业采购 RHEL 的核心诉求。再看另一个例子0002-rhel-enable-CONFIG_KVM_GUEST.patch。这个补丁并非简单开启 KVM 支持而是添加了针对 VMware ESXi 和 Hyper-V 的 guest driver 适配层并在kernel.spec中通过%ifarch x86_64条件编译确保 ARM64 架构不引入冗余代码。更重要的是它绑定了kmod-kvdoKernel-based Virtual Delivery Optimizer模块的构建依赖因为 RHEL 要求所有虚拟化相关组件必须通过统一的kmod包管理便于热补丁Live Patching更新。这种“补丁即策略”的设计使得 RHEL source 成为一部活的、可执行的企业 IT 治理手册。2.3 构建路径选择为什么推荐从 CentOS Stream 9 的 source 开始而非直接硬啃 RHEL UBIRHEL 的 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 hubhttps://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 UBIUniversal Base Image虽然也提供 source但仅限于容器镜像内的软件包如ubi8-minimal:8.8的glibcsource缺失 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-12GCC 12.2.1但 kernel 构建要求gcc-toolset-11GCC 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-devel或kernel-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-protectorstrong \ # 关键安全加固 --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-protectorstrong。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-ldap和sssd相关补丁如果做 HPC就关注numa和mpi相关补丁。RHEL 的补丁集庞大聚焦才是高效之道。3.3 mock 构建环境为什么不用rpmbuild而要用mock创建隔离沙箱rpmbuild是一个强大的工具但它直接在你的主机环境中运行极易受到主机已安装软件包、环境变量、甚至用户~/.rpmmacros的污染。RHEL 的构建要求“可重现性”reproducibility即在任何时间、任何机器上给定相同的 dist-git commit hash 和 koji tag必须产出完全一致的 RPM。mock正是为此而生——它使用chroot或systemd-nspawn创建一个与主机完全隔离的、纯净的构建环境buildroot。安装与配置mockdnf install mock # 将当前用户加入 mock 组获得免 sudo 权限 usermod -a -G mock $USER # 重新登录或执行 newgrp mockmock的核心配置文件位于/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] nameRHEL 9.2 BaseOS baseurlhttps://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ enabled1 gpgcheck0 [rhel-9.2-appstream] nameRHEL 9.2 AppStream baseurlhttps://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/ enabled1 gpgcheck0 使用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.cfg的config_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.target4.2 组装完整 ISO使用 pungi 工具链从 RPM 集合到可启动介质单个 RPM 构建只是第一步。RHEL 的价值在于其软件包集合package set的协同工作。pungi是红帽官方的 compose 工具它读取 kickstart 文件从 koji hub 或本地 repo 拉取指定 tag 下的所有 RPM并按规则组装成 ISO。安装 pungidnf 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 --namelocal-build --baseurlfile:///path/to/your/rpms/ # 或使用公共源推荐用于首次测试 repo --namecentos-stream-9-baseos --baseurlhttps://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.kshd:LABELMyRHEL-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-core和kernel-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.el9CentOS 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 签名是信任链的基石。排查步骤确认公钥已导入rpm -q gpg-pubkey --qf %{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n应看到gpg-pubkey-fd431d51-5ae7a1e7RHEL 9 的公钥 ID。检查 RPM 的签名头rpm -qpi rpm-file.rpm | grep Signature确认 Signature 字段存在且非(none)。手动验证rpm --checksig rpm-file.rpm输出应为gpg(RSA/SHA256, Key ID fd431d51): OK。如果失败最常见原因是 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.md0 rd.lvm0 rd.dm0导致系统在 RAID 卡上无法识别 root 分区。教训是企业级硬件的驱动栈极其复杂initramfs 必须针对目标硬件进行专项测试不能依赖通用配置。6. 安全与合规性审计如何证明你的定制版 RHEL 符合等保、密评要求6.1 源码一致性审计从 ISO 到 dist-git commit hash 的逐层追溯等保2.0三级要求“操作系统应提供源代码审计能力”。这意味着你不能只说“我用了 RHEL source”而必须提供一份可验证的审计报告。我的标准流程是记录构建输入保存pungi命令的完整参数、kickstart 文件的 SHA256、以及所有参与构建的 RPM 的rpm -qi输出。反向追溯 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验证 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_ID20231027-001并在每次构建时递增。这个 ID 将贯穿于所有审计日志、Jenkins 构建记录、以及客户交付文档中成为你构建过程可追溯性的唯一锚点。它不改变系统行为却让每一次安全审计都变得无比清晰——这才是 RHEL source 项目真正的终点。