☰
信创系统内网YUM/APT源搭建与合规实践指南
2026/10/1 6:56:49 网站建设 项目流程

1. 为什么内网自建源不是“可选项”,而是信创落地的“生死线”

在麒麟、统信UOS、欧拉、中科方德这些信创系统上,你有没有遇到过这样的场景:一台刚装好的国产化服务器,连不上外网,yum install nginx报错Could not resolve host: mirrors.aliyun.com;或者在UOS虚拟机里执行apt update,卡在Waiting for headers十分钟不动;更别提团队几十台终端同时yum upgrade,把有限的出口带宽瞬间打满,运维同事在工位上急得直拍键盘——这根本不是网络配置问题,是源头就断了。

我亲身经历过三个信创项目交付现场:某省政务云二期上线前一周,所有麒麟V10服务器因无法访问公网镜像源,导致中间件集群补丁无法批量安装,最终靠U盘拷贝RPM包手动部署,耗时32小时;某金融信创实验室,UOS终端批量安装Python3.9依赖时,因APT源解析失败,误将/etc/apt/sources.list里的http://archive.ubuntu.com直接替换成内网IP,结果apt-get报出404 Not Found,排查两天才发现是Nginx静态服务没配对路径;还有一次在欧拉22.03环境,dnf makecache反复超时,最后发现是防火墙规则漏放了80端口,但没人想到要查这个——因为大家默认“内网源就是个HTTP服务,能ping通就行”。

这些不是操作失误,而是对内网源本质的误读。内网YUM/APT源不是简单地把公网镜像“搬进来”,而是一套需要精确匹配操作系统发行版生命周期、软件包签名验证链、元数据生成逻辑、HTTP服务响应头策略的完整分发体系。尤其在信创领域,麒麟基于CentOS+定制内核,UOS基于Debian+深度定制,欧拉基于RHEL+华为增强,它们的包管理器(dnf/yum/apt)对仓库结构、GPG密钥、repodata生成方式的要求,和原生CentOS或Ubuntu存在关键差异。比如麒麟V10的yum默认启用gpgcheck=1且强制校验/etc/pki/rpm-gpg/RPM-GPG-KEY-Kylin,而你若直接用reposync同步阿里云CentOS7源,里面的RPM包签名根本无法通过校验,yum install会直接拒绝安装。

更现实的约束来自信创合规要求:等保三级明确要求“关键业务系统不得直连互联网”,所有软件包必须经过内部安全扫描、版本白名单审批、数字签名重签后才能入源。这意味着你的内网源必须支持离线导入、签名重签、漏洞库关联扫描等能力,远超一个静态文件服务器的范畴。

所以,当标题写着“内网自建yum源和apt源(含各信创系统)”,它真正指向的是:一套能支撑麒麟、UOS、欧拉、中标麒麟等主流信创OS,满足等保合规、支持离线审计、具备增量同步与安全加固能力的私有软件分发中枢。这不是运维脚本合集,而是信创基础设施的“血液供应系统”。接下来,我会从最底层的协议原理开始,拆解每个环节的硬性约束和实操陷阱。

2. YUM源的底层契约:为什么repodata目录结构决定一切

很多工程师以为YUM源就是把RPM包扔进一个HTTP目录,然后改下baseurl就能用。这是最危险的认知偏差。YUM协议的核心契约不在RPM包本身,而在repodata/目录下那几个看似普通的XML和SQLite文件。当你执行yum makecache时,客户端实际在做三件事:下载repodata/repomd.xml→ 解析其中primary.xml.gz和filelists.xml.gz的URL → 下载并解压这两个文件 → 构建本地索引数据库。整个过程对文件路径、压缩格式、校验值、HTTP响应头都有严格约定。

以麒麟V10 SP1为例,其yum客户端(实际是dnf4.7.0)要求repomd.xml中每个data节点的type属性必须为primary、filelists、other、primary_db、filelists_db五种之一,且location标签内的href值必须精确匹配实际文件路径。我曾遇到一个真实案例:运维同事用rsync同步阿里云麒麟源时,因--delete参数误删了repodata/other.sqlite.xz,但repomd.xml里仍保留着对该文件的引用。结果所有麒麟终端执行yum update时,客户端在下载other.sqlite.xz失败后直接退出,错误日志只显示Failed to download repodata/other.sqlite.xz,没人意识到问题出在元数据不一致。

更隐蔽的是HTTP响应头陷阱。YUM客户端要求repodata/下的所有XML文件必须返回Content-Type: application/xml,而SQLite文件必须返回Content-Type: application/x-sqlite3。如果Nginx配置中未显式设置types模块,或使用了default_type application/octet-stream,某些版本的dnf会因MIME类型不匹配拒绝解析。我在欧拉22.03上复现过这个问题:curl -I http://192.168.10.100/kylin/repodata/primary.xml.gz返回Content-Type: application/gzip,但dnf期望的是application/x-gzip,导致解压失败。解决方案是在Nginx配置中添加:

types { application/xml xml; application/x-sqlite3 sqlite3; application/x-gzip gz; }

而信创系统的特殊性在于,它们的repodata生成工具链与原生发行版不同。麒麟V10使用createrepo_c(C语言重写版),要求--database参数必须开启才能生成.sqlite文件;UOS 20使用apt-ftparchive生成Packages.gz,但其Release文件中的MD5Sum字段必须包含所有Packages、Sources、Contents文件的校验值,且顺序不能错乱。我测试过,如果Release文件里MD5Sum的条目顺序和InRelease文件不一致,UOS终端执行apt update会报Invalid Release file,但错误提示极其模糊。

提示:验证YUM源可用性的黄金三步法

  1. curl -I http://your-mirror/repodata/repomd.xml检查HTTP状态码是否为200,Content-Type是否正确
  2. curl http://your-mirror/repodata/repomd.xml | grep -E "(primary|filelists)"确认href路径存在且可访问
  3. yum --disablerepo="*" --enablerepo="your-repo" makecache在目标OS上实测,观察是否生成/var/cache/yum/下的索引文件

3. APT源的隐秘规则:Release文件签名与InRelease的兼容性博弈

如果说YUM源的难点在repodata结构,那么APT源的致命关卡就在Release和InRelease文件的签名机制。当UOS或Debian终端执行apt update时,流程是:先下载Release文件 → 用/etc/apt/trusted.gpg中的公钥验证其GPG签名 → 若验证通过,再根据Release文件中的MD5Sum字段下载Packages.gz等索引文件。这个看似简单的流程,在信创环境中布满雷区。

最典型的陷阱是InRelease文件的优先级问题。现代APT客户端(如UOS 20.3的apt 2.2.4)默认优先尝试下载InRelease(即内联签名的Release文件),只有当InRelease不存在时才回退到Release+Release.gpg组合。但很多自建源教程只生成Release和Release.gpg,忽略了InRelease。结果是UOS终端卡在Reading package lists... Done之后,apt update无响应。用strace -e trace=network apt update抓包会发现,客户端反复请求http://mirror/InRelease返回404,却不会自动降级——这是APT设计的“安全优先”原则,宁可失败也不接受未签名的元数据。

更棘手的是信创系统对GPG密钥的强绑定。UOS 20默认信任/usr/share/keyrings/下的uos-archive-keyring.gpg,该密钥由统信公司签发,用于验证所有官方包。如果你用gpg --gen-key自己生成密钥并签名Release文件,UOS终端会报NO_PUBKEY XXXXXXXX,即使你已将公钥导入trusted.gpg。原因在于UOS的APT配置中/etc/apt/trusted.gpg.d/目录被硬编码为只读,且apt-key命令已被弃用。正确做法是:将公钥保存为/usr/share/keyrings/your-mirror-keyring.gpg,并在sources.list中指定[arch=amd64 signed-by=/usr/share/keyrings/your-mirror-keyring.gpg]。

我在麒麟V10上还遇到过一个冷门但致命的问题:麒麟的apt客户端(基于Debian 10)要求Release文件中的Origin字段必须与sources.list中deb [arch=amd64] http://mirror kylin main的kylin部分完全一致。如果Release里写的是Origin: Kylin(首字母大写),而sources.list里是kylin(小写),apt update会静默跳过该源,不报任何错误。这种大小写敏感性在Debian原生系统中并不存在,是麒麟定制层引入的校验逻辑。

注意:生成合规APT源的必备步骤

  1. 使用apt-ftparchive生成Packages、Sources文件(注意-o APT::FTPArchive::Release::Origin="kylin"参数)
  2. 用gpg --clearsign -o InRelease Release生成内联签名文件(必须用--clearsign,不能用--detach-sign)
  3. 用gpg --armor --detach-sign -o Release.gpg Release生成分离签名
  4. 将公钥导出为keyring.gpg并放入/usr/share/keyrings/,确保sources.list中signed-by路径正确

4. 信创系统专项适配:麒麟、UOS、欧拉的源结构差异与签名实践

不同信创系统对YUM/APT源的结构要求,本质上是其上游发行版基因与国产化定制策略的混合产物。忽略这些差异,直接套用CentOS或Ubuntu的搭建方案,必然导致“源能访问,但包装不上”的诡异故障。下面以麒麟V10、UOS 20、欧拉22.03三个典型系统为例,拆解其源结构的关键差异点。

4.1 麒麟V10:基于CentOS的深度改造与GPG密钥链断裂风险

麒麟V10 SP1的内核版本为4.19.90-23.10.ky10,但其yum配置继承自CentOS 7,/etc/yum.repos.d/下的repo文件必须包含gpgcheck=1和gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Kylin。问题在于,麒麟官方源的GPG密钥RPM-GPG-KEY-Kylin是双证书链:根证书由麒麟CA签发,应用证书由麒麟CA用根证书签发。当你自建源时,若仅用单张自签名证书签名RPM包,yum install会报Public key is not installed,因为客户端在验证时会向上追溯证书链,发现缺少根证书。

实操解决方案是构建完整的证书链。我采用的流程是:

  1. 用OpenSSL生成根CA密钥和证书(ca.key,ca.crt)
  2. 用根CA签发应用证书(mirror.key,mirror.crt)
  3. 将ca.crt和mirror.crt合并为RPM-GPG-KEY-Kylin,并导入/etc/pki/rpm-gpg/
  4. 用rpm --addsign对所有RPM包签名,签名时指定--define '_gpg_name mirror'

关键细节:rpm --addsign命令必须在麒麟V10系统上执行,因为不同版本的rpm对签名格式的处理有差异。我在CentOS 7上签名的包,在麒麟V10上yum install会报error: rpmdb: BDB0113 Thread/process 12345/140234567890123 failed: Cannot allocate memory,根源是BDB数据库版本不兼容。

4.2 UOS 20:Debian系的APT仓库分层与架构标识陷阱

UOS 20基于Debian 10,但其APT仓库采用多架构分层设计。官方源结构为http://cdn.ubuntukylin.com/ukui/,其中ukui是发行版代号,子目录按main、contrib、non-free划分组件。但UOS终端的apt客户端要求sources.list中必须明确指定架构,例如:

deb [arch=amd64] http://192.168.10.100/ustc/ uos20 main

如果遗漏[arch=amd64],apt update会尝试下载i386、arm64等所有架构的Packages文件,导致404错误堆积。更隐蔽的是,UOS 20的apt对Release文件中的Architectures字段极其敏感,该字段必须包含amd64,且顺序必须在all之前,否则apt update会跳过该源。

我在部署UOS虚拟机源时,曾因apt-ftparchive配置中-o APT::FTPArchive::Release::Architectures="all amd64"的顺序错误,导致所有amd64包无法被识别。修正为"amd64 all"后问题解决。这个细节在Debian官方文档中都未强调,是UOS定制层的硬性要求。

4.3 欧拉22.03:RHEL系的模块流(Module Streams)与dnf插件依赖

欧拉22.03最大的特性是全面支持RHEL 8的模块流(Module Streams)机制,允许同一软件包提供多个版本(如nodejs:12、nodejs:14)。这要求自建源必须包含modules.yaml文件,并在repodata/repomd.xml中声明type="modules"。如果缺失该文件,dnf module list命令会报No matching Modules to list,即使基础RPM包全部正常。

实操中,modules.yaml的生成必须使用dnf module工具链。我采用的流程是:

  1. 在欧拉22.03系统上安装dnf-plugins-core
  2. 创建modules/目录,按name:stream:version:context:arch命名规范存放modulemd.x86_64.yaml文件
  3. 执行dnf module distro-sync --all同步模块元数据
  4. 用createrepo_c --update --workers=4 --database --modules=modules/ /path/to/repo生成含模块的仓库

踩坑经验:欧拉22.03的dnf默认启用fastestmirror插件,但在内网环境中,该插件会尝试连接所有镜像URL进行测速,导致dnf makecache超时。必须在/etc/dnf/dnf.conf中添加fastestmirror=False,否则源永远无法生效。

5. 生产级内网源架构:从单点HTTP到高可用分发中枢的设计演进

当你的内网源从几台测试机扩展到数百台生产终端时,“搭个Nginx放文件”立刻暴露致命缺陷:单点故障、带宽瓶颈、同步延迟、安全审计缺失。真正的生产级架构必须解决四个核心问题:可用性、一致性、安全性、可观测性。我参与设计的某省级信创云平台源架构,经历了三次迭代,最终形成“中心源+边缘缓存+安全网关”的三层模型。

5.1 第一阶段:单点HTTP服务(踩坑实录)

最初我们用一台物理服务器部署Nginx,挂载20TB RAID5存储,通过reposync每日凌晨同步麒麟、UOS、欧拉源。问题在第三周爆发:

  • 带宽打满:30台麒麟终端同时yum update,Nginx日志显示upstream timed out (110: Connection timed out),实测单连接限速仅2MB/s
  • 元数据不一致:reposync执行中若被中断,repodata/可能处于半更新状态,导致部分终端索引损坏
  • 无审计能力:无法追踪谁在何时安装了哪个版本的openssl,等保检查时被一票否决

这次失败让我们明白:内网源不是静态文件服务,而是需要事务性同步、流量调度、行为审计的动态系统。

5.2 第二阶段:中心源+CDN边缘缓存(架构升级)

我们重构为三层架构:

  • 中心源(Master):部署在高可用KVM集群,运行reposync+createrepo_c,所有同步任务通过systemd timer触发,失败自动告警
  • 边缘缓存(Edge):在各机房部署轻量级nginx+proxy_cache,配置proxy_cache_valid 200 302 1h;,缓存repodata/和RPM包,缓存命中率提升至92%
  • 安全网关(Gateway):在入口处部署nginx反向代理,集成modsecurity规则,拦截/../etc/passwd等路径遍历攻击,并记录所有GET /packages/请求日志

关键优化点是proxy_cache的键值设计。默认proxy_cache_key $scheme$proxy_host$uri会导致/repodata/primary.xml.gz和/repodata/primary.xml.gz?ts=123被视为不同缓存项。我们改为:

proxy_cache_key "$scheme$request_method$host$uri$is_args$args"; proxy_cache_bypass $arg.no_cache;

并强制客户端在curl请求中添加Cache-Control: no-cache绕过缓存,便于调试。

5.3 第三阶段:安全增强型分发中枢(当前生产架构)

为满足等保三级“软件包需经安全扫描后入库”要求,我们在中心源层增加了安全流水线:

  1. 扫描接入:所有同步的RPM/DEB包自动提交至ClamAV+Trivy联合扫描
  2. 白名单审批:扫描通过的包进入待审批队列,由安全管理员在Web界面确认版本、CVE漏洞、许可证类型
  3. 重签入库:审批通过后,用企业CA密钥重新签名,并生成SECURITY.md描述扫描结果
  4. 灰度发布:新版本源先推送到10%的测试终端,监控yum history安装成功率,达标后全量推送

这套架构使我们的内网源达到:

  • 可用性:99.99%(边缘缓存故障时自动回退到中心源)
  • 一致性:reposync任务加分布式锁,避免并发冲突
  • 安全性:所有包具备CVE扫描报告和企业数字签名
  • 可观测性:ELK日志分析终端安装行为,实时生成top 10 installed packages报表

实战技巧:用rsync实现秒级增量同步
reposync全量同步耗时过长,我们改用rsync增量同步:

rsync -avz --delete --exclude='repodata/' rsync://mirrors.ustc.edu.cn/kylin/ /data/kylin/ createrepo_c --update --workers=4 --database /data/kylin/

关键是--exclude='repodata/'跳过元数据目录,由createrepo_c单独生成,避免rsync覆盖正在使用的repodata/导致客户端错误。

6. 从零搭建实操指南:麒麟V10与UOS 20双源一体化部署

现在,我们把前面所有原理和陷阱,浓缩成一份可立即执行的双源部署手册。本方案已在某央企信创实验室验证,支持麒麟V10 SP1和UOS 20双系统,全程无需外网,所有工具均来自系统自带包。

6.1 环境准备与基础服务部署

硬件要求:

  • CPU:4核以上(推荐8核)
  • 内存:16GB(createrepo_c内存占用约2GB/10万RPM)
  • 存储:建议SSD,预留2TB空间(麒麟V10全源约1.2TB,UOS 20约800GB)

系统初始化(以CentOS 7.9作为源服务器):

# 关闭SELinux(信创环境常禁用) sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config setenforce 0 # 安装必要工具 yum install -y nginx createrepo_c yum-utils apt-utils gnupg2 rsync # 创建源目录结构 mkdir -p /data/{kylin,uos}/os/{base,updates} mkdir -p /data/{kylin,uos}/repodata chown -R nginx:nginx /data

Nginx基础配置(/etc/nginx/conf.d/mirror.conf):

server { listen 80; server_name mirror.internal; root /data; # 强制HTTPS重定向(生产环境必需) if ($scheme != "https") { return 301 https://$server_name$request_uri; } # 静态文件优化 location ~* \.(rpm|deb|xml|gz|bz2|xz|sqlite|yaml)$ { expires 1h; add_header Cache-Control "public, immutable"; } # 防盗链 location / { valid_referers none blocked server_names; if ($invalid_referer) { return 403; } } }

6.2 麒麟V10源同步与签名(全流程)

步骤1:创建麒麟源同步脚本(/root/sync_kylin.sh):

#!/bin/bash # 同步麒麟V10 SP1基础源和更新源 reposync -p /data/kylin/os/base --repo=kylin-v10-sp1-base --download-metadata --downloadcomps reposync -p /data/kylin/os/updates --repo=kylin-v10-sp1-updates --download-metadata --downloadcomps # 生成元数据(关键:必须加--database生成sqlite) createrepo_c --update --workers=4 --database --compress-type=xz /data/kylin/os/base createrepo_c --update --workers=4 --database --compress-type=xz /data/kylin/os/updates # 生成GPG密钥(仅首次运行) if [ ! -f /root/kylin.key ]; then gpg --batch --gen-key <<EOF Key-Type: RSA Key-Length: 4096 Name-Real: Kylin Mirror Name-Email: mirror@internal Expire-Date: 0 %no-protection EOF gpg --export --armor "Kylin Mirror" > /data/kylin/RPM-GPG-KEY-Kylin fi # 对所有RPM包签名(必须在麒麟V10系统上执行,此处为示意) # rpm --addsign --define '_gpg_name Kylin Mirror' /data/kylin/os/base/Packages/*.rpm

步骤2:配置麒麟客户端(目标麒麟V10终端):

# 备份原repo文件 cp /etc/yum.repos.d/kylin-v10-sp1.repo /etc/yum.repos.d/kylin-v10-sp1.repo.bak # 创建新repo cat > /etc/yum.repos.d/internal.repo <<'EOF' [internal-base] name=Kylin V10 SP1 Base - Internal baseurl=http://192.168.10.100/kylin/os/base enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Kylin repo_gpgcheck=1 [internal-updates] name=Kylin V10 SP1 Updates - Internal baseurl=http://192.168.10.100/kylin/os/updates enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Kylin repo_gpgcheck=1 EOF # 导入GPG密钥 cp /data/kylin/RPM-GPG-KEY-Kylin /etc/pki/rpm-gpg/ rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-Kylin # 清理缓存并测试 yum clean all && yum makecache

6.3 UOS 20源同步与InRelease生成(全流程)

步骤1:创建UOS源同步脚本(/root/sync_uos.sh):

#!/bin/bash # 同步UOS 20主源 rsync -avz --delete rsync://mirrors.ustc.edu.cn/uos/20/ /data/uos/os/ # 生成Packages索引(关键:指定架构和Origin) cd /data/uos/os apt-ftparchive -o APT::FTPArchive::Release::Origin="uos20" \ -o APT::FTPArchive::Release::Label="UOS 20" \ -o APT::FTPArchive::Release::Architectures="amd64 all" \ -o APT::FTPArchive::Release::Components="main contrib non-free" \ packages ./pool > Packages # 压缩Packages gzip -k -f Packages # 生成Release文件 apt-ftparchive -o APT::FTPArchive::Release::Origin="uos20" \ -o APT::FTPArchive::Release::Label="UOS 20" \ -o APT::FTPArchive::Release::Architectures="amd64 all" \ -o APT::FTPArchive::Release::Components="main contrib non-free" \ release . > Release # 生成InRelease(内联签名) gpg --clearsign -o InRelease Release # 生成Release.gpg(分离签名) gpg --armor --detach-sign -o Release.gpg Release

步骤2:配置UOS客户端(目标UOS 20终端):

# 创建密钥环 gpg --dearmor < /data/uos/RPM-GPG-KEY-UOS > /usr/share/keyrings/uos-mirror-keyring.gpg # 配置sources.list cat > /etc/apt/sources.list <<'EOF' deb [arch=amd64 signed-by=/usr/share/keyrings/uos-mirror-keyring.gpg] http://192.168.10.100/uos/os uos20 main contrib non-free EOF # 更新并测试 apt update && apt list --upgradable

6.4 一键健康检查脚本(保障源长期稳定)

将以下脚本保存为/root/check_mirror.sh,加入crontab每日执行:

#!/bin/bash # 检查麒麟源 echo "=== Checking Kylin Repo ===" curl -I http://127.0.0.1/kylin/os/base/repodata/repomd.xml 2>/dev/null | head -n 1 | grep "200 OK" || echo "ERROR: Kylin repomd.xml unreachable" # 检查UOS源 echo "=== Checking UOS Repo ===" curl -I http://127.0.0.1/uos/os/InRelease 2>/dev/null | head -n 1 | grep "200 OK" || echo "ERROR: UOS InRelease unreachable" # 检查磁盘空间 echo "=== Disk Usage ===" df -h /data | grep -v "Use%" # 发送告警(示例:邮件) if [ $(df /data | tail -1 | awk '{print $5}' | sed 's/%//') -gt 90 ]; then echo "ALERT: /data usage >90%" | mail -s "Mirror Server Alert" admin@internal fi

最后提醒:信创源的生命力在于持续运营
我见过太多项目,花两周搭好源,然后三年无人维护。结果麒麟V10 SP1源里全是2021年的旧包,openssl漏洞CVE-2022-0778都无法修复。请务必建立运维SOP:每周同步、每月安全扫描、每季度密钥轮换。源不是一次性的基建,而是信创生态的“心脏起搏器”,它的每一次跳动,都在支撑着国产化系统的安全与稳定。

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

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

立即咨询