runsisi's

technical notes

制作并使用 apt 源

2019-02-02 runsisi#apt

在 CentOS 下,rpm 包自带 gpg 签名,只用在 rpmbuild 的阶段考虑 gpg 签名的问题,可以使用 createrepo 工具对已有的 rpm 包非常简单的制作 yum 源,createrepo 完全不需要考虑 gpg 签名的问题。但在 Debian/Ubuntu 下,一般不对 deb 包进行 gpg 签名,而是对 apt 源(repository)的元数据进行签名,因此对制作 apt 源的工具有一定的要求,比如 dpkg-scanpackages 就不支持 gpg 签名。下面以 aptly 为例,介绍如何制作带 gpg 签名的 apt 源。

aptly 的工作流程大致如下:

  1. 创建空白 repo;
  2. 为刚才创建的空白 repo 添加 deb 包;
  3. 发布 repo,即创建 apt 源;

创建 apt 源

创建空白 repo,名字为 ceph:

~$ aptly repo create -architectures amd64 -comment 'Ceph distributed file system' -component main -distribution trusty ceph

Local repo [ceph]: Ceph distributed file system successfully added.
You can run 'aptly repo add ceph ...' to add packages to repository.

为名字为 ceph 的 repo 添加 xxx/ 目录下的所有 deb 包:

~$ aptly repo add ceph xxx/
Loading packages...
[+] ceph-base_12.2.9-1.126.g8653d9c-trusty_amd64 added
...

列出所有的 repo,我们能看到刚才创建的 repo 里添加了 37 个 deb 包:

~$ aptly repo list
List of local repos:
 * [ceph]: Ceph distributed file system (packages: 37)

刚才说过 apt 源的 gpg 签名是对元数据进行签名,aptly 需要我们指定合适的 gpg 参数才能进行签名,这里我们简单点,直接导出 GNUPGHOME 环境变量,aptly 就能使用默认的私钥进行签名:

~$ export GNUPGHOME=~/build/gpg

创建 apt 源:

~$ aptly publish repo -label 'ceph' -origin 'ceph.com' ceph ceph-12.2.9
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...
...

查看已创建的 apt 源,显然这是一个标准的 apt 源目录结构,可以把 ~/.aptly/public 下刚才创建的 apt 源整个目录拷贝至 web 服务器目录对外提供 apt 源服务:

~$ aptly publish list
Published repositories:
  * ceph-12.2.9/trusty (origin: ceph.com, label: ceph) [amd64] publishes {main: [ceph]: Ceph distributed file system}
~$ tree ~/.aptly/public/
/home/runsisi/.aptly/public/
└── ceph-12.2.9
    ├── dists
    │   └── trusty
    │       ├── Contents-amd64.gz
    │       ├── InRelease
    │       ├── main
    │       │   ├── binary-amd64
    │       │   │   ├── Packages
    │       │   │   ├── Packages.bz2
    │       │   │   ├── Packages.gz
    │       │   │   └── Release
    │       │   └── Contents-amd64.gz
    │       ├── Release
...

最后可以删除刚才创建的 apt 源:

~$ aptly publish drop trusty ceph-12.2.9
Removing /home/runsisi/.aptly/public/ceph-12.2.9/dists...
Removing /home/runsisi/.aptly/public/ceph-12.2.9/pool...

Published repository has been removed successfully.

客户端配置

创建 apt 源之后,还需要在客户端进行配置才能使用。

首先添加这个源(我把刚才那个源的目录拷贝到了我的 nginx 服务器 ceph/ 目录下,并重命名为 ceph-ubuntu):

~$ sudo vi /etc/apt/sources.list
...
deb [arch=amd64] http://example.com/release/ceph/ceph-ubuntu/ trusty main

还一个重要的操作是添加 gpg 公钥,不要忘了我们已经通过 aptly 为 apt 源的元数据进行了签名,如果客户端不导入对应的公钥,那签名就没有任何意义了:

~$ curl -o- http://example.com/release/XXX-RELEASE-GPG-KEY | sudo apt-key add -
...
OK

不导入 gpg 公钥的话 apt update 的时候会报如下的错误:

Fetched 4835 kB in 8s (553 kB/s)
Reading package lists... Done
W: GPG error: http://example.com trusty InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY C08D7AE1759D8517

查看刚才通过 apt-key add 添加的 gpg 公钥(注意到包含了上面的错误提示里的 keyid):

~$ apt-key list
/etc/apt/trusted.gpg
--------------------
...

pub   2048R/759D8517 2018-01-06
uid                  xxx <xxx@example.com>
sub   2048R/1B971CBF 2018-01-06

至此,客户端就可以正常的进行 apt update && apt install ceph 了。

非 gpg 签名的 apt 源制作与使用

使用 dpkg-scanpackages 制作的 apt 源不支持 gpg 签名:

~$ dpkg-scanpackages xxx-debs/ | gzip > xxx-debs/Packages.gz
dpkg-scanpackages: info: Wrote 62 entries to output Packages file.

然后在客户端配置源:

~$ sudo vi /etc/apt/sources.list
...
deb [arch=amd64] file:///home/runsisi/ xxx-debs/

注意 xxx-debs 后面的 / 非常重要(与是否是本地路径还是 http url 路径无关,都必须加上 /),否则会认为是非法的地址。

然后使用 apt-get 进行 deb 包的安装:

~$ sudo apt-get update
Reading package lists... Done
W: The repository 'file:/home/runsisi xxx-debs/ Release' does not have a Release file.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.

注意 apt (Ubuntu 16.04+)不支持这种非标准格式的 repo 且要求 repo 的元数据有 gpg 签名,可以使用 apt-get 临时进行规避。

故障解决

1. weak digest algorithm

如果使用 apt update 的过程中出现如下的警告:

W: http://<server>/dists/trusty/InRelease: Signature by key <keyid> uses weak digest algorithm (SHA1)

查看 Release.gpg 文件:

~$ gpg --list-packets dists/trusty/Release.gpg
:signature packet: algo 1, keyid C08D7AE1759D8517
        version 4, created 1544758617, md5len 0, sigclass 0x00
        digest algo 2, begin of digest fa 97
        hashed subpkt 2 len 4 (sig created 2018-12-14)
        subpkt 16 len 8 (issuer key ID C08D7AE1759D8517)
        data: [2046 bits]

并对照如下的 RFC 文档,可知 algo 2SHA1 算法。

Hash Algorithms

https://tools.ietf.org/html/rfc4880#section-9.4

显然,这是 gpg 签名所使用的算法问题,可以参考如下两个链接:

add support for setting gpg.conf settings

https://github.com/ros-infrastructure/buildfarm_deployment/issues/130

How to fix apt: Signature by key uses weak digest algorithm (SHA1)?

https://askubuntu.com/questions/760796/how-to-fix-apt-signature-by-key-uses-weak-digest-algorithm-sha1

通过在 GNUPGHOME 下增加相应的配置文件即可解决该问题:

~$ vi gpg.conf
cert-digest-algo SHA256
digest-algo SHA256
~$ gpg --list-packets dists/trusty/Release.gpg
:signature packet: algo 1, keyid C08D7AE1759D8517
        version 4, created 1545009257, md5len 0, sigclass 0x00
        digest algo 8, begin of digest a3 ee
        hashed subpkt 2 len 4 (sig created 2018-12-17)
        subpkt 16 len 8 (issuer key ID C08D7AE1759D8517)
        data: [2047 bits]

其中 algo 8 对应 SHA256

2. Inappropriate ioctl

在容器环境中,使用 reprepro 制作 apt 源,可能会出现如下的错误:

gpgme gave error GPGME:32870:  Inappropriate ioctl for device
There have been errors!

和如下链接的现象一样:

Often fails when including package into repo

https://github.com/desc/puppet-reprepro/issues/59

链接中提供的解决方案(mount devpts)在这边并不需要,因为已经 mount 上了,但提供了思路,这个容器是通过 docker run -idt ubuntu:14.04 的方式创建的,但进入容器是通过 docker exec 的方式,尝试改成 docker attach 方式之后问题解决。

参考资料

HOWTO: Create debian repositories with reprepro

https://blog.packagecloud.io/eng/2017/03/23/create-debian-repository-reprepro/

HOWTO: GPG sign and verify deb packages and APT repositories

https://blog.packagecloud.io/eng/2014/10/28/howto-gpg-sign-verify-deb-packages-apt-repositories/

DebianRepository Setup

https://wiki.debian.org/DebianRepository/Setup

Debian Repository Format

https://wiki.debian.org/DebianRepository/Format

reprepro manual

https://www.red-bean.com/doc/reprepro/manual.html

aptly - Debian repository management tool

https://github.com/aptly-dev/aptly