在 CentOS 下,rpm 包自带 gpg 签名,只用在 rpmbuild 的阶段考虑 gpg 签名的问题,可以使用 createrepo 工具对已有的 rpm 包非常简单的制作 yum 源,createrepo 完全不需要考虑 gpg 签名的问题。但在 Debian/Ubuntu 下,一般不对 deb 包进行 gpg 签名,而是对 apt 源(repository)的元数据进行签名,因此对制作 apt 源的工具有一定的要求,比如 dpkg-scanpackages 就不支持 gpg 签名。下面以 aptly 为例,介绍如何制作带 gpg 签名的 apt 源。
aptly 的工作流程大致如下:
- 创建空白 repo;
- 为刚才创建的空白 repo 添加 deb 包;
- 发布 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 2
是 SHA1
算法。
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)?
通过在 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
最后修改于 2019-02-02