使用 reprepro 制作 apt 源
使用 apt 分发 deb 软件包。
基本工作流程:
-
构建机器 执行 debuild 构建 deb 包;
-
构建机器 执行 dput 上传包到 APT 服务器(通过 Nginx WebDAV 上传文件,dput 也支持 ftp、scp 等方式);
-
APT 服务器 执行 reprepro 添加 deb 包。
-
通过 HTTP POST 请求 APT 服务器 通过 CGI 执行 Shell。
系统环境:Debian 12。
Ubuntu 22.04 dput 版本太低。 |
安装
sudo apt install apache2-utils gnupg fcgiwrap nginx reprepro
# docker 中无 systemd 需要安装 spawn-fcgi
-
apache2-utils
密码文件工具; -
gnupg
GPG 签名; -
fcgiwrap
通过 HTTP 请求执行 Shell; -
nginx
通过 WebDAV 上传,通过 CGI 执行 Shell,提供静态 apt 数据访问; -
reprepro
生成 apt 数据。
reprepro
创建目录
-
构建的 deb 包等添加到对应的 incoming 目录;
-
reprepro 从 incoming 读取添加到 apt 元数据中。
# debian/ubuntu apt 源数据
mkdir -p /var/www/packages/{debian,ubuntu}/conf
touch /var/www/packages/{debian,ubuntu}/conf/{distributions,uploaders,options,incoming}
# debian 12 (bookworm) / 11 (bullseye) 新到来的 deb 包
mkdir -p /var/www/packages/incoming/debian/{bookworm,bullseye}
mkdir -p /var/www/packages/incoming/debian/{bookworm-temp,bullseye-temp}
# ubuntu 22.04 (jammy) / 20.04 (focal) 新到来的 deb 包
mkdir -p /var/www/packages/incoming/ubuntu/{jammy,focal}
mkdir -p /var/www/packages/incoming/ubuntu/{jammy-temp,focal-temp}
目录权限
目录权限问题:
-
Nginx WebDAV 使用 www-data 用户在 incoming 目录下创建文件;
-
reprepro 命令需要读取、删除 incoming 目录下的文件;
-
reprepro 需要读取 GPG 私钥,通过私钥对 deb 数据进行签名。
需要统一这三者的权限为 www-data
:
sudo chown www-data:www-data -R /var/www/packages
GPG 密钥对
用户 www-data 无法直接登录,可通过当前用户生成 GPG 密钥,再导入到 www-data 用户。
生成 GPG 密钥用于签名包:
# 生成 GPG 密钥
gpg --full-generate-key
# 列出 GPG
gpg --list-secret-key --with-subkey-fingerprint
-
为避免签名脚本处理密码输入,私钥不要添加密钥,输入密码时直接回车即可。
导入私钥到 www-data 用户:
sudo mkdir --mode=700 /var/www/.gnupg
sudo chown www-data:www-data /var/www/.gnupg
# 从当前用户导出私钥,并通过管道导入给用户 www-data
gpg --export-secret-key --armor E81E8376705C4ECEE542E26E002555760B157806 \
| sudo -u www-data gpg --import --yes --batch --import-options restore
# 检查
sudo -u www-data gpg --list-secret-keys
导出 GPG 公钥,提供给客户端下载:
sudo -u www-data gpg --armor --output /var/www/packages/public.gpg --export E81E8376705C4ECEE542E26E002555760B157806
-
问题: gpg: agent_genkey failed: 权限不够
检查 tty 权限是否为当前用户:
ls -l $(tty)
可以考虑使用其他用户生成再导入。
配置
Debian
Debian 相关配置位于目录 /var/www/packages/debian
。
配置发行版 debian 12 和 debian 11
Origin: packages.notfound.cn
Label: notfound
Codename: bookworm
Architectures: amd64 arm64
Components: main
Description: Notfound apt repository
SignWith: E81E8376705C4ECEE542E26E002555760B157806
Uploaders: uploaders
Origin: packages.notfound.cn
Label: notfound
Codename: bullseye
Architectures: amd64 arm64
Components: main
Description: Notfound apt repository
SignWith: E81E8376705C4ECEE542E26E002555760B157806
Uploaders: uploaders
-
Codename
通过命令lsb_release --short --codename
可以查看:-
bookworm
: Debian 12 -
bullseye
: Debian 11
-
-
Architectures
体系结构,命令dpkg-architecture -L
可列出所有值; -
SignWith
GPG KEY ID,用于 GPG 签名。 -
Uploaders
: 可选,uploaders 文件,配置上传者规则。
配置 uploaders(可选)
指定上传者通过 GPG 签名的内容允许被添加到 APT。
添加 conf/uploaders
:
allow * by key 0B157806
allow * by key 359C1291
-
允许指定的 GPG KEY ID,这里需要填写短格式 KEY ID,同时需要将 GPG 公钥导入到服务器。
相关 GPG 命令:
# 查看短格式 KEY ID
gpg --list-key --keyid-format short
# 导出公钥
gpg --export --armor --output public.gpg 07CE1788C0D07551532C8C871A6B2334359C1291
# 导入公钥
gpg --import public.gpg
# 或者相同机器下二合一
gpg --export --armor 07CE1788C0D07551532C8C871A6B2334359C1291 \
| sudo -u www-data gpg --import --yes --batch
配置 reprepro 参数
verbose
basedir /var/www/packages/debian
ask-passphrase
-
verbose
显示详情; -
basedir
Debian 包目录; -
ask-passphrase
需要输入 gpg 密码。
配置 incoming
Name: bookworm
IncomingDir: /var/www/packages/incoming/debian/bookworm
TempDir: /var/www/packages/incoming/debian/bookworm-temp
Allow: bookworm
Default: bookworm
Permit: unused_files
Cleanup: unused_files on_deny on_error
Name: bullseye
IncomingDir: /var/www/packages/incoming/debian/bullseye
TempDir: /var/www/packages/incoming/debian/bullseye-temp
Allow: bullseye
Default: bullseye
Permit: unused_files
Cleanup: unused_files on_deny on_error
-
Name
规则集名称,执行reprepro
命令时使用; -
IncomingDir
用来扫描.changes
文件的目录; -
TempDir
处理过程中的临时目录; -
Allow
允许的发行版本; -
Default
未通过Allow
参数时的默认发行版; -
Permit
允许的出现未使用的文件(unused_files); -
Cleanup
未使用(unused_files)、拒绝处理(on_deny)、处理出错(on_error)时文件都会被清理。
Ubuntu
和 Debian 类似。
添加 deb 包
操作前切换用户:
sudo su - www-data --shell /bin/bash
方法一
通过 includedeb
直接添加:
reprepro --basedir /var/www/packages/debian includedeb bookworm ~/bookworm/debhello_0.0-1_amd64.deb
方法二
将 .changes 以及 .changes
中指定的相关文件放到 incoming 目录,执行:
reprepro --basedir /var/www/packages/debian processincoming bookworm
-
规则集名称为 bookworm
这些文件可以在 debuild 后通过 dput
上传,见后文。
reprepro 命令
# 列出
reprepro list bookworm
# 移除
reprepro remove bookworm debhello
# 删除所有不在发行版中的包数据库
reprepro clearvanished
Nginx
Nginx 的功能:
-
提供 apt 源数据;
-
配置 WebDAV 功能,以支持 dput 上传 deb 包;
-
配置 FastCGI 功能,以支持 http 方式触发 reprepro 处理 deb 包。
配置
Basic 认证
sudo mkdir /etc/nginx/htpasswd/
sudo htpasswd /etc/nginx/htpasswd/packages packages
sudo htpasswd /etc/nginx/htpasswd/packages jenkins
-
为用户 packages/jenkins 生成密码数据
WebDAV 配置
WebDAV Nginx 相关配置:
limit_except GET HEAD {
auth_basic "packages.notfound.cn";
auth_basic_user_file /etc/nginx/htpasswd/packages;
}
client_body_temp_path /var/www/packages/client_temp;
create_full_put_path off;
dav_access user:rw group:rw all:r;
dav_methods PUT DELETE MKCOL COPY MOVE;
-
limit_except
非 GET 和 HEAD 都需要认证; -
create_full_put_path
禁止创建新的目录,因此需要提前创建目录。
FastCGI
FastCGI 执行脚本文件:
#!/bin/bash
function error_400() {
echo 'Status: 400 Bad Request'
echo 'Content-Type: text/plain'
echo ''
echo "$1"
exit 1
}
if [ "$REQUEST_METHOD" != 'POST' ]; then
error_400 'only support POST'
fi
IFS='/'; set -- $REQUEST_URI
os=$3
distribution=$4
case "$os/$distribution" in
debian/bookworm)
;;
debian/bullseye)
;;
ubuntu/jammy)
;;
ubuntu/focal)
;;
*)
error_400 "unsupport $os/$distribution"
;;
esac
message=$(reprepro --basedir /var/www/packages/$os processincoming $distribution 2>&1)
if [ $? -ne 0 ]; then
echo 'Status: 500 Internal Server header'
echo 'Content-Type: text/plain'
echo ''
echo $message
exit 1
fi
echo 'Status: 200 OK'
echo 'Content-Type: text/plain'
echo ''
echo 'success'
echo "$message"
exit 0
# vim: set tabstop=4 shiftwidth=4 expandtab
添加执行权限:
sudo a+x /usr/local/bin/reprepro.cgi
添加 FastCGI Nginx 配置:
auth_basic "packages.notfound.cn";
auth_basic_user_file /etc/nginx/htpasswd/packages;
gzip off;
include fastcgi_params;
fastcgi_pass unix:/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME /usr/local/bin/reprepro.cgi;
packages 配置
域名 packages.notfound.cn 配置,通过 include 指令整合上文中的配置文件:
server {
listen 80;
# listen 443 ssl;
server_name packages.notfound.cn;
# ssl_certificate /etc/nginx/cert.d/notfound.cn.crt;
# ssl_certificate_key /etc/nginx/cert.d/notfound.cn.key;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
root /var/www/packages;
access_log /var/log/nginx/packages.access.log;
error_log /var/log/nginx/packages.error.log;
client_max_body_size 100M;
location / {
autoindex on;
}
location ~ /(.*)/(conf|db) {
deny all;
}
# debian: bookworm, bullseye
location ~ ^/incoming/debian/(bookworm|bullseye)/cgi-bin/reprepro.cgi$ {
include packages_fastcgi_params;
}
location ~ ^/incoming/debian/(bookworm|bullseye)/ {
autoindex on;
include packages_dav_params;
}
# ubuntu: jammy, focal
location ~ ^/incoming/ubuntu/(jammy|focal)/cgi-bin/reprepro.cgi$ {
include packages_fastcgi_params;
}
location ~ ^/incoming/ubuntu/(jammy|focal)/ {
autoindex on;
include packages_dav_params;
}
}
-
root 目录为 packages 目录;
-
开启了目录浏览功能;
-
禁止访问
conf
和db
; -
通过
packages_dav_params
配置 Nginx WebDAV,只允许访问指定的目录; -
通过
packages_fastcgi_params
配置 Nginx FastCGI,只允许访问指定的目录; -
client_max_body_size
最大上传文件。
目录 /var/www/packages/incoming/
用于上传 deb 相关文件,需要提前创建。
dput
通过 dput 可上传打包的文件。
dput 支持 ftp、http(s)、scp、sftp、rsync 和 local 方式上传文件。
配置
Debian 12 环境。
# vim: set tabstop=4 shiftwidth=4 expandtab
##################### Debian 12 ####################
# mkdir -p $HOME/.config/dput
# curl -o $HOME/.config/dput/dput.cf -fsSL http://packages.notfound.cn/incoming/dput.cf.txt
[DEFAULT]
fqdn = packages.notfound.cn
login = jenkins
method = http
default_host_main = bookworm
allow_unsigned_uploads = true
[bookworm]
incoming = /incoming/debian/bookworm
[bullseye]
incoming = /incoming/debian/bullseye
[jammy]
incoming = /incoming/ubuntu/jammy
[focal]
incoming = /incoming/ubuntu/focal
-
default_host_main
默认配置; -
fqdn
服务器; -
login
登录用户名; -
method
支持ftp
、http(s)
、scp
、sftp
、rsync
和local
; -
incoming
上传的目标目录; -
allow_unsigned_uploads
可选,允许上传无 GPG 签名的文件。
可以在 Nginx 服务端保存一份,方便部署时使用,可以考虑保存到:/var/www/packages/incoming/dput.cf.txt
。
参考: man dput.cf
使用
假设已经构建 deb 包。
# 使用默认 host
dput debhello_0.0-1_amd64.changes
# 或者指定 host
dput bookworm debhello_0.0-1_amd64.changes
查看 host 列表:
dput --host-list
客户端
添加 GPG Key:
sudo mkdir -p /etc/apt/keyrings
sudo curl -sSL http://packages.notfound.cn/public.gpg -o /etc/apt/keyrings/notfound.asc
添加 apt 源:
# debian 12
deb [signed-by=/etc/apt/keyrings/notfound.asc] http://packages.notfound.cn/debian bookworm main
# deiban 11
deb [signed-by=/etc/apt/keyrings/notfound.asc] http://packages.notfound.cn/debian bullseye main
安装 debhello:
sudo apt update
sudo apt install debhello
优先级
如果其他 APT 源已经存在相同的包,可以通过 apt preferences 设置优先级,如,添加文件 /etc/apt/preferences.d/notfound :
Package: hello
Pin: release l=notfound,c=main
Pin-Priority: 900
-
Package
包名 -
Pin
-
l
为label
-
c
为Component
-
Pin-Priority
优先级,数字越大优先级越高
查询包的优先级:
apt-cache policy hello
参考:
-
man apt_preferences
-
https://debian-handbook.info/browse/stable/sect.apt-get.html#sect.apt.priorities
包命名约定
deb 包名称相同但内容不同时,无法重复添加,即使是不同的发行版:
$ reprepro includedeb bullseye deb/bullseye/debhello_0.0-1_amd64.deb
deb/bullseye/debhello_0.0-1_amd64.deb: component guessed as 'main'
ERROR: 'deb/bullseye/debhello_0.0-1_amd64.deb' cannot be included as 'pool/main/d/debhello/debhello_0.0-1_amd64.deb'.
Already existing files can only be included again, if they are the same, but:
md5 expected: 937114b8826ea3441f2eb3a196db1a8d, got: 169429e1b925b065b866e714ffd10a09
sha1 expected: 1824644849af1b8cca7234a2406d0052163ae27d, got: bedd3f062023aef802e0ae153b2be31e351d8a9d
sha256 expected: 38749fd54428945ec9a93b01ea92c6e153b8592b7ebf786a322d6e7408817a8a, got: fcdc9cfc23f1ca8b5082e0d957ee225bc1219405ddbfc1aa2873088ca5076f89
size expected: 14392, got: 14512
There have been errors!
如果相同的源码需要打包到不同发行版 Codename
,需要修改 debian/changelog
中的版本信息改变 deb 包名称。
Debian/Ubuntu 命名约定
通过变更日志查看现有的包命名规则:
apt changelog openjdk-17-jdk
apt changelog curl
结果:
# Debian 12 查看官方包命名:
openjdk-17 (17.0.11+9-1~deb12u1) bookworm-security; urgency=medium
curl (7.88.1-10+deb12u5) bookworm-security; urgency=high
# Debian 11 查看官方包命名:
openjdk-17 (17.0.11+9-1~deb11u1) bullseye-security; urgency=medium
curl (7.74.0-1.3+deb11u11) bullseye-security; urgency=high
# Ubuntu 22.04
openjdk-17 (17.0.10+7-1~22.04.1) jammy-security; urgency=high
curl (7.81.0-1ubuntu1.16) jammy-security; urgency=medium
# ubuntu 20.04
openjdk-17 (17.0.10+7-1~20.04.1) focal-security; urgency=high
curl (7.68.0-1ubuntu2.22) focal-security; urgency=medium
看上去并没有一个强制标识 codename 的统一规范。
可以参考 Naming Convention for Debian Packages 使用规则:
<package_name> (<upstream_version>-<debian_revision>+<dist_codename>)
# 如
debhello (0.0-1+bookworm)
debhello (0.0-1+bullseye)
-
package_name
包名 -
upstream_version
上游软件包版本 -
debian_revision
Debian 修订版本 -
dist_codename
发行版 codename
修改 debian/changelog
后重新打包。