部署 搭建 APT 镜像 MeteorCat 2025-12-01 2025-12-05 这里以 debian 为例子, 一般能够看到为了国内源从而配置修改相关的 sources.list 等文件, 目前有以下集中源配置:
sources.list: 直接源配置
DEB822: 新的安全配置源
比如下面的的源格式:
1 2 3 4 5 6 7 8 9 # sources.list 经典的远程源配置 deb http://mirrors.ustc.edu.cn/debian trixie main contrib non-free non-free-firmware # DEB822 格式远程源配置, 追加 gpg 签名做安全验证, 'SignWith: no' 代表不启用签名验证 Types: deb URIs: http://mirrors.ustc.edu.cn/debian Suites: trixie trixie-updates Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
如果要自己做内网二进制打包镜像, 可以采用 sources.list 配置, 因为本身内网的包是管理员确定已提交的可控安装包.
注意: 提交暴露给内网的包在 debian 系都是 的 *.deb 结尾打出的压缩包
我们先通过 sources.list 格式来解读我们的镜像源需要什么配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 以 'deb http://mirrors.ustc.edu.cn/debian trixie main contrib non-free non-free-firmware' 为例子 # deb|deb-src: deb 表示二进制包仓库, 也就是直接拉取 deb 包安装; deb-src 代表源码包拉取, 不会做编译处理 # http://mirrors.ustc.edu.cn/debian: 代表远程镜像地址 # trixie: 代表目前系统系统代号, 可以通过 cat /etc/os-release|grep VERSION= 查看 # main contrib non-free non-free-firmware: 这里代表的索引目录, 也就远程带有以下目录(以 /pool 为根目录) # - http://远程镜像/pool/main: 代表 debian 负责官方维护的包, 比如编译工具和开发工具链等 # - http://远程镜像/pool/contrib: 代表 debian 半官方支持, 比如依赖的第三方含有非开源的非自由工具包 # - http://远程镜像/pool/non-free: 代表闭源的非自由软件包, 具有闭源/禁止修改/分发/商业授权等限制 # - http://远程镜像/pool/non-free-firmware: 硬件设备的闭源固件(Firmware), 定制驱动的非自由组件包 # =========================================================================================== # 按照样例来看, 最后需要搭建的远程目录结构如下 # http://mirrors.ustc.edu.cn/debian/ ├─ dists/ # 包索引目录(APT 更新时下载这里的索引) │ └─ trixie/ # 对应发行版代号(trixie=Debian 13) │ ├─ main/ # main 组件的索引 │ │ ├─ binary-amd64/ # amd64 架构的索引文件(Packages、Packages.gz) │ │ ├─ source/ # 源码包索引(Sources) │ │ └─ ... │ ├─ contrib/ # contrib 组件的索引(结构同 main) │ ├─ non-free/ # non-free 组件的索引(结构同 main) │ └─ non-free-firmware/ # non-free-firmware 组件的索引(结构同 main) └─ pool/ # 实际 .deb 包存储目录(对应组件分类) ├─ main/ # main 组件的包 │ ├─ a/ # 按软件名首字母划分(如 apache2) │ ├─ b/ # 如 bash、nginx(n 开头) │ └─ ... ├─ contrib/ # contrib 组件的包(结构同 main) ├─ non-free/ # non-free 组件的包(结构同 main) └─ non-free-firmware/ # non-free-firmware 组件的包(结构同 main)
只需要按照这部分目录构建就可以自定义生成主要的架构, 如果想要安装过程自动进行打包就需要手动来构建 {包}.deb 包, 这里按照流程生成所需要镜像源的本地目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 一般采用 nginx + reprepro 做轻量级的包编译即可 # reprepro 是 Debian 系专用的 APT 仓库管理工具, 能自动生成索引, 管理包的增删和处理发行版/组件映射 sudo apt install nginx nginx-extras reprepro dpkg-dev # 设定 /data/mirror/debian 为本地包的访问路径 # 这里就需要创建 conf,dists,pool 这三个目录 sudo mkdir -p /data/mirror/debian/{conf,dists,pool} # 按组件和发行版划分目录(对应 trixie + main/contrib 等组件) # 注意这里声明这部分二进制包是采用 amd64 架构, 如果采用 arm 就需要额外处理 sudo mkdir -p /data/mirror/debian/dists/trixie/{main,contrib,non-free,non-free-firmware}/binary-amd64 sudo mkdir -p /data/mirror/debian/pool/{main,contrib,non-free,non-free-firmware} # 需要创建 distributions 文件用于描述目前的仓库信息 sudo touch /data/mirror/debian/conf/distributions # 赋予 nginx 访问权限 sudo chown -R www-data:www-data /data/mirror sudo chmod -R 755 /data/mirror
具体创建目录说明:
conf: 存放 reprepro 的配置文件(核心目录)
dists: 存放 APT 索引文件(Packages/Release 等, 由 reprepro 自动生成)
pool: 存放实际的 .deb 包(按组件分类)
这里需要声明该镜像库目前的信息(/data/mirror/debian/conf/distributions):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 # 以 www-data 权限生成源仓库信息 # 注意: 我这里因为采用 Ubuntu 24.04.3 LTS (Noble Numbat) # Ubuntu24.04 是 debian13 的下游, 而 noble 的软件库可以和 trixie 的软件复用 # 因为我演示是在 Ubuntu 设备上处理, 而我日常服务器都是 debian 系统, 所以为了方便这里声明为 ubuntu sudo -u www-data bash -c 'cat > /data/mirror/debian/conf/distributions << EOF # 发行版代号(需与系统一致, 如 debian13 - trixie | debian12 - bookworm | ubuntu22.04 - jammy | ubuntu24.04 - noble) Codename: noble # 仓库描述 Description: MeteorCat APT Repository # 支持的组件(与 pool 目录对应) Components: main contrib non-free non-free-firmware # 支持的架构(amd64 为主, 可加 arm64 armhf i386 等架构, 但是必须确保架构的包存在) Architectures: amd64 # 索引文件压缩格式 UDebComponents: main EOF' # 生成完成之后就是通过 nginx 配置加载 sudo bash -c 'cat > /etc/nginx/conf.d/apt-debian.conf << EOF server { listen 80; # 端口按需求修改 # 可选:填写你的服务器域名(如apt.meteorcat.local),内网用IP可留空为_ server_name _; # 指向你的仓库根目录 root /data/mirror; ## 开启目录列表 # autoindex on; #autoindex_exact_size off; # autoindex_localtime on; # 不过这里做了美化采用 fancyindex location ^~ / { fancyindex on; # 启用fancyindex fancyindex_exact_size off; # 人性化文件大小 fancyindex_localtime on; # 本地时间 fancyindex_name_length 255; # 显示完整文件名 # 忽略 .log 结尾|.tmp 结尾 | 隐藏文件(以.开头)| test 目录 fancyindex_ignore "\.log$|\.tmp$|^\..+$|^test$"; } # 日志配置(便于排查访问问题) access_log /var/log/nginx/apt-debian-access.log; error_log /var/log/nginx/apt-debian-error.log; # 规则1:禁止访问所有.log/.tmp后缀的文件(任意目录下) location ~* \.(log|tmp)$ { deny all; return 404; # 伪装成资源不存在,比403更安全 } # 规则2:禁止访问根目录下的.test/.*隐藏目录/文件(任意目录下) location ~* /(\..+|test) { deny all; return 404; } } EOF'
启动 nginx 就可以访问到镜像地址目标即可, 之后就是添加安装包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 重启 Nginx sudo systemctl restart nginx # 这里以官方的 htop 工具为例子, 确认先卸载掉本地的包并清理缓存 sudo apt purge htop sudo apt autoremove sudo apt autoclean # 我们假借中科大的包下载过来当作我们自己镜像包内部的 htop 软件 cd /data/mirror/debian/ # 必须进入源镜像目录 # 我们先把远程下载到包放在临时目录 # 注意临时目录的文件需要删除下避免空间不足 sudo -u www-data wget -O /tmp/htop_3.4.1-5_amd64.deb https://mirrors.ustc.edu.cn/debian/pool/main/h/htop/htop_3.4.1-5_amd64.deb # 执行 reprepro 索引, 这里的配置是有规则的 # reprepro includedeb: 代表引入包 # noble: 这里是 distributions 文件声明的 Codename 配置 sudo -u www-data reprepro includedeb noble /tmp/htop_3.4.1-5_amd64.deb # 如果想删除包和其索引, 可以通过以下指令 # 注意: 也是必须要进入到源镜像目录 sudo -u www-data reprepro list noble # 查看导入的的包列表 sudo -u www-data reprepro remove noble htop # 注意: 删除包只需要关键字, 比如 htop
之后其他服务器配置镜像源直接声明下 nginx 地址配置即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 # 这里直接映射部署的 nginx 地址 sudo echo "deb http://127.0.0.1/debian noble main contrib non-free non-free-firmware" | sudo tee /etc/apt/sources.list.d/apt-debian.list # 更新源 sudo apt update # 这里会报错: 仓库 “http://127.0.0.1/debian noble Release” 没有数字签名 # 如果是对外开放的服务一定要做好签名验证 # 而如果是本地认证绝对不会出问题的包, 可以跳过这部分认证, 这需要将镜像源配置修改如下 sudo echo "deb [trusted=yes] http://127.0.0.1/debian noble main contrib non-free non-free-firmware" | sudo tee /etc/apt/sources.list.d/apt-debian.list # 不过还是建议对包做签名 # 最后就是安装 htop, 如果本地已经安装可以执行 sudo apt upgrade 自动升级 sudo apt install htop
后续就是其他细节, 比如生成 GPG 签名(强烈建议)和数据缓存和自动更新镜像源, 不过内网使用一般搞个 https 证书安装到服务器设置 [trusted=yes] 基本上可以满足这部分日常需求.
deb 打包 这里提供源码打包样例, 采用将 jenkins 打包安装到系统安装, 然后放置在自己镜像源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 先进拉取官方包 cd /tmp && wget https://get.jenkins.io/war-stable/2.528.1/jenkins.war jenkins.war # 创建对应包, 标记为 amd64 包 mkdir -p /tmp/jenkins_2.528.1_amd64 && cd /tmp/jenkins_2.528.1_amd64 # 创建 DEB 包信息目录 mkdir -p DEBIAN && cd DEBIAN touch control # 设置信息文件 # postinst(安装后脚本) | prerm(卸载前脚本) # 注意权限必须要 <= 775, 否则会出现包安装失败 touch postinst && chmod 755 postinst touch prerm && chmod 755 prerm # 编辑 control 文件 sudo vim /tmp/jenkins_2.528.1_amd64/DEBIAN/control
首先编写 control 的软件包信息:
1 2 3 4 5 6 7 8 Package: jenkins Version: 2.528.1 Description: The leading open source automation server, Jenkins provides hundreds of plugins to support building, deploying and automating any project. Section: net Priority: standard Architecture: amd64 Maintainer: jenkins.io Pre-Depends: fontconfig, openjdk-21-jre, git, subversion
之后创建映射的安装目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 创建源码目录 mkdir -p /tmp/jenkins_2.528.1_amd64/usr/local/src/jenkins # 将之前下载的 war 包放置于 src 目录 cp -rf /tmp/jenkins.war /tmp/jenkins_2.528.1_amd64/usr/local/src/jenkins/jenkins.war chmod 755 /tmp/jenkins_2.528.1_amd64/usr/local/src/jenkins/jenkins.war # 源码包直接放在在以下目录 # ls -l /tmp/jenkins_2.528.1_amd64/usr/local/src/jenkins/jenkins.war# 之后就编写安装|卸载的脚本 vim postinst # 安装脚本 vim prerm # 卸载脚本 # 其实这里应该创建 /tmp/jenkins_2.528.1_amd64/etc/systemd/system/jenkins.service 服务启动相关 # 但是为了节约时间这里不做扩展
postinst 安装脚本本身就是运行脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 # !/bin/sh set -e # 出错立即退出 # 这里最好检测下 java -version 确认二进制存在或者版本存在 java -version # 创建 jenkins 用户和组 if ! id -u jenkins >/dev/null 2>&1; then useradd -r -m -d /var/lib/jenkins -s /bin/false jenkins fi # 授权目录 chown -R jenkins:jenkins /usr/local/src/jenkins
prerm 卸载脚本也是个运行脚本:
1 2 3 4 5 6 7 8 9 10 11 12 # !/bin/sh set -e # 停止并禁用服务 if systemctl is-active --quiet jenkins.service; then systemctl stop jenkins.service fi systemctl disable jenkins.service # 卸载时删除用户(可选,保留数据则注释) userdel -r jenkins
现在已经可以进行打包处理:
1 2 3 4 5 # 回到包的根目录并且启动打包程序 cd /tmp && dpkg -b /tmp/jenkins_2.528.1_amd64 jenkins_2.528.1_amd64.deb # 打包完成可以看到输出 ls -l /tmp/jenkins_2.528.1_amd64.deb
至此 deb 已经打包完成, 这里先不要放置在镜像源, 测试下安装本地的时候是否会进行编译处理:
1 2 3 4 5 6 7 8 9 10 11 # 测试安装, 会进入编译流程 # 这里就会提示 openjdk-21-jre 未安装, 这就是强依赖的应用 sudo dpkg -i /tmp/jenkins_2.528.1_amd64.deb # 测试没问题可以卸载安装包 sudo dpkg -r jenkins # 之后直接提交到自己镜像源网站 cd /data/mirror/debian/ # 进入镜像源目录 sudo -u www-data reprepro includedeb noble /tmp/jenkins_2.528.1_amd64.deb sudo -u www-data reprepro list noble # 查看是否提交包完成
这里就是怎么手动打出 deb 包并且提交的流程.
安全签名 之前虽然能够采用 [trusted=yes] 跳过安全认证, 但是为了安全最好还是手动生成数字签名来提供服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 # 安装 GPG 工具 sudo apt update && sudo apt install -y libconfig-file-perl gnupg dpkg-sig # 注意: 很奇葩的一点就是 dpkg-sig 这个包可能 Ubuntu 环境下 apt 找不到 # 需要去上游额外的 debian 源下载安装: wget -O /tmp/dpkg-sig_0.13.1+nmu4_all.deb https://mirrors.ustc.edu.cn/debian/pool/main/d/dpkg-sig/dpkg-sig_0.13.1%2Bnmu4_all.deb sudo dpkg -i /tmp/dpkg-sig_0.13.1+nmu4_all.deb # 安装 dpkg-sig # 查看具体版本信息 gpg --version # 先手动生成 GPG 密钥 # 注意: 生成的密钥信息会默认保存 ~/.gnupg/openpgp-revocs.d 之中 # 所以如果要自动化生成的话, 最好确认授权是否存在 HOME 目录保证生成 # 这里也是异常高发地, 会涉及到挂载的服务用户权限问题, 后面会说明 gpg --full-generate-key # 请选择您要使用的密钥类型: 选择 {(4) RSA(仅用于签名)|RSA(sign only) } 选项 # 选择 RSA 密钥的长度: 4096 # 你要有效时限: 0 - 永不过期 # 确认信息进行下一步, 后续就用仓库具体信息 # 需要输出真实名称: MeteorCat(我这里随便自定义名称) # 需要输入邮箱地址: [email protected] (输入自己邮箱) # 注释说明: 这个随便, 留空也行 # 输入 o 生成, 其中会提示你需要密码保护, 如果不想要额外设置密码也没事, 直接继续下一步即可 # 生成之后按照流程一般是导出公钥到镜像源的源目录下暴露给 apt 源配置 # 导出公钥需要采用之前输入邮箱地址指定, 比如上面说的 [email protected] gpg --armor --export [email protected] > /tmp/meteorcat-debian-mirror.gpg.key # 之后把公钥放到镜像源目录下 # 后续如果其他服务需要配置源就需要执行以下命令来导入公钥 # wget -O - http(s)://{源镜像站}/debian/meteorcat-debian-mirror.gpg.key | apt-key add - sudo -u www-data cp /tmp/meteorcat-debian-mirror.gpg.key /data/mirror/debian/meteorcat-debian-mirror.gpg.key # ------------------------------------------------------------------------- # 这里就是假设生成的签名数据 # gpg: 吊销证书已被存储为‘ /home/meteorcat/.gnupg/openpgp-revocs.d/906B7B5387FA4D2C445795D737B153163F339E56.rev’ # 公钥和私钥已经生成并被签名。 # # 906B7B5387FA4D2C445795D737B153163F339E56 # uid MeteorCat <[email protected] > # sub rsa4096 2025-12-01 [E] # ------------------------------------------------------------------------- # pub 下面的 906B7B5387FA4D2C445795D737B153163F339E56 就是生成的 KEY ID, 也就是我们后续需要的签名标识 # 我们需要的是这个标识的后8位 # 修改 /data/mirror/debian/conf/distributions 文件 # 需要追加配置签名 SignWith 字段 # 同时需要 GPG 生成的 KEY: 906B7B5387FA4D2C445795D737B153163F339E56 # 那么我们需要追加个 'SignWith: yes' 配置 sudo vim /data/mirror/debian/conf/distributions # 最后生成的仓库元数据内容如下(首行#不需要加上): # # Codename: noble # # Description: MeteorCat APT Repository # # Components: main contrib non-free non-free-firmware # # Architectures: amd64 # # UDebComponents: main # # SignWith: yes # 准备就是对包进行签名处理 # 这是远程下载的 deb 包 or 自己打包出来的 deb, 以之前说过的下载的上级 deb 包为例子, 我们重新再走一遍流程 wget -O /tmp/htop_3.4.1-5_amd64.deb https://mirrors.ustc.edu.cn/debian/pool/main/h/htop/htop_3.4.1-5_amd64.deb # 对deb 包签名写入 # 注意: 这里打包的是刚下载下来的 deb 包, 而不是已经放在镜像源的 deb 包 dpkg-sig --sign builder -k 3F339E56 /tmp/htop_3.4.1-5_amd64.deb # 输出 Signed deb /tmp/htop_3.4.1-5_amd64.deb 就代表签名结束 # 之后就是把签名后的包提交镜像源 # 这里则是利用新的 -b/--basedir 参数指定镜像源目录, 如果你之前对 htop 提交过一次源镜像, 以下命令会执行报错: sudo -u www-data reprepro -b /data/mirror/debian remove noble htop # 清理原来的包内容 sudo -u www-data reprepro --basedir /data/mirror/debian includedeb noble /tmp/htop_3.4.1-5_amd64.deb # 需要注意这里会常常出错提示 Already existing files can only be included again # 这是因为默认的包都会被文件哈希做对比, 你重新引入的 deb 包和之前的老 deb 文件哈希不一致 # 按照源配置的原理: 同样版本的包被发布出去, 就不能再被修改, 只能被版本号更新之后同步! # 所以要求源配置提交一定要小心! 一旦执行 includedeb 之后同名同版本的包就不被允许覆盖而是必须升级版本来提交! # 所以我们这里就需要重新在上游找个源包签名提交才能方便测试, 这里用 tree 这个小应用来测试 wget -O /tmp/tree_2.2.1-4_amd64.deb https://mirrors.ustc.edu.cn/debian/pool/main/t/tree/tree_2.2.1-4_amd64.deb dpkg-sig --sign builder -k 906B7B5387FA4D2C445795D737B153163F339E56 /tmp/tree_2.2.1-4_amd64.deb # 最后推送到镜像源目录 sudo -u www-data reprepro --basedir /data/mirror/debian includedeb noble /tmp/tree_2.2.1-4_amd64.deb # 这里会报错异常, 但是包已经成功导入: # ebian includedeb noble /tmp/tree_2.2.1-4_amd64.deb # Exporting indices... # gpgme gave error GPGME:1: General error # ERROR: Could not finish exporting 'noble' ! # This means that from outside your repository will still look like before (and # should still work if this old state worked), but the changes intended with this # call will not be visible until you call export directly (via reprepro export ) # Changes will also get visible when something else changes the same file and # thus creates a new export of that file, but even changes to other parts of the # same distribution will not! # There have been errors! # 这里能看到包已经导入, 但是实际上是因为签名异常 sudo -u www-data reprepro --basedir /data/mirror/debian ls tree # 这里涉及到其实就是权限问题, 因为生成的 gpg 默认只生成加载当前的登陆用户 # 我们看到之前处理的都是 www-data 系统用户操作, 而我们一直都是直接运行生成, # 私钥公钥都是放置在 '/home/{目前操作管理员用户}/.gnupg/openpgp-revocs.d/' # www-data 是没办法跨权限目录去加载和签名这些私钥公钥 # 所以这里应该做的是重新构建个系统账号去处理这些专门操作, 而且必须要有以下条件: # 1. HOME 目录文件夹存在 # 2. 追加 www-data 用户组
因为这边一般是内网处理, 所以都是干脆 www-data 挂载暴露, 后续这里的操作和搭建一致只是需要创建系统管理账号
注意: gpg 工具集版本变动很频繁, 有的参数和接口随着版本不同而变动得很频繁, 这里部署方式可能不是很准确.
国内的镜像站则是直接同步外包资源到本地, 不会自己重新签名所以签名这些信息依旧还是官方的