DeveloperWiki:软件仓库数据库签名
外观
[RFC] 软件仓库数据库签名(以及 ISO/其他产物)
由于担心在向软件仓库添加软件包的服务器上放置 GPG 私钥的安全风险,我们目前的仓库数据库(DB)尚未签名。然而,我们希望自动为我们的数据库、ISO 和其他产物进行签名,因此必须在安全性与便捷性之间做出一定的折中。
关于这个话题已经进行了多次讨论
为软件仓库数据库签名其实很简单
repo-add -v -s foo.db pacman-3.0.0-1-x86_64.pkg.tar.gz lrwxrwxrwx 1 jelle users 13 Aug 15 20:56 foo.db -> foo.db.tar.gz lrwxrwxrwx 1 jelle users 17 Aug 15 20:56 foo.db.sig -> foo.db.tar.gz.sig -rw-r--r-- 1 jelle users 504 Aug 15 20:56 foo.db.tar.gz -rw-r--r-- 1 jelle users 503 Aug 15 20:56 foo.db.tar.gz.old -rw-r--r-- 1 jelle users 310 Aug 15 20:56 foo.db.tar.gz.sig lrwxrwxrwx 1 jelle users 16 Aug 15 20:56 foo.files -> foo.files.tar.gz lrwxrwxrwx 1 jelle users 20 Aug 15 20:56 foo.files.sig -> foo.files.tar.gz.sig -rw-r--r-- 1 jelle users 782 Aug 15 20:56 foo.files.tar.gz -rw-r--r-- 1 jelle users 782 Aug 15 20:56 foo.files.tar.gz.old -rw-r--r-- 1 jelle users 310 Aug 15 20:56 foo.files.tar.gz.sig
repo-add 按照以下方式对仓库进行签名
gpg --detach-sign --use-agent --no-armor
无论是否为有意设计,repo-add 可以在不添加软件包的情况下直接对数据库进行签名。
repo-add foo.db.tar.gz -s
提议的解决方案
通过“安全”飞地进行签名(方案 1)
创建一个专门的签名主机,用于接收文件并返回签名。
该主机应添加一些限制
- 签名频率限制(例如 ISO 每月仅签名一次)
- 安全地记录已签名的产物
- 请求必须来自特定 IP(应仅限 repos.archlinux.org)
- 身份验证(SSH?)
- 使用独立主机、虚拟机还是容器?
- 最小访问列表
详见 Bluewind 的提议。
实现方案: https://git.server-speed.net/users/flo/alass/[死链 2025-03-15—域名无法解析]
通过“安全”飞地进行签名(方案 2)
不再向“安全”飞地发送任意文件,而是由飞地主动拉取待签名文件并返回一个新的已签名文件。
工作流程
- 触发数据库更改操作,锁定仓库。
- 创建一个新的数据库文件并将其放入临时位置。
- 向安全飞地发送通知(或更准确地说是被飞地获取),告知有新的数据库文件需要签名。
- 安全飞地获取待签名文件。
- 对其进行签名,并(可选,待定)记录该操作。
- 将签名上传回数据库服务器。
- 数据库服务器将数据库及其签名移至正式位置,释放仓库锁并重置通知通道。
全局实现细节
- 此处无特殊/新内容。
- 此处需要将新生成的数据库放在特定位置。
- 在此阶段,数据库更改操作通知签名需求主要有两种可能性
- 不做任何操作:飞地在临时位置监视数据库,当看到文件(而不是例如 404 错误)时,即知道有工作要做。
- 向特定的文件写入内容:当没有新数据库需要签名时,该文件内容可为 0,否则为 1,甚至可以是实际数据库文件的校验和,以避免传输过程中的损坏问题。
- 如果采用了上述第二种情况,只需使用同样类型的安全连接来获取数据库。
- 这里有很多需要考虑的地方。尤其是 *安全* 记录很难实现。下文将对此展开。
- 这里有多种可能的上传机制,见后文。
- 这要求数据库更改操作必须监视签名的返回情况。
具体细节
- 关于飞地从数据库服务器获取文件:我能想到的最安全方式是通过极强加固的 HTTPS 连接。这意味着服务器和客户端都需要使用由自定义 CA(可驻留在飞地中)颁发的证书进行身份验证,仅使用 TLS 1.3,ECDHE 搭配 Ed448 和 Poly1305-Chacha20 或 AES-GCM。我们需要在监视通知文件的同时保持连接开启,以避免在监视频率下重复建立 TLS 会话。
- 将签名上传回服务器:我们可以使用与上述相同的设置进行 HTTP(S) 上传,但不确定这是否非常重要(除非我们签名了一个错误的文件,否则签名本身本来就是公开文件……)。这里需要对攻击场景进行仔细思考。
- 对于上述两点,我们可以在两端强制执行 IP 地址验证以增强安全性。仓库服务器的实际入口点也可以是一个 TOR 隐藏服务,但不确定我们是否需要(我想可能会有副作用)。
- 现在,让我们深入研究日志记录和威胁模型。这可能是最困难的部分。
- 如果我们不考虑飞地可能被攻破的情况,那么只需要签名脚本将签名同时记录到远程上传点和本地文件(例如在网络故障时)。
- 如果我们考虑飞地可能被攻破但没有 root 权限,我们可以让密钥仅 root 可访问或存储在 HSM(硬件安全模块)中,但让(仅 root 可写)的签名+记录脚本可由普通用户使用。这样,攻击者将无法在不记录日志的情况下签名文件。
- 如果我们考虑飞地已被 root 攻破,那么拥有 HSM 是唯一能让我们在面对全面崩溃时获得一点保护的手段(这要求攻击者必须在飞地中维持存在,从而防止长期攻击),但基本上这已经宣告失败了。
请注意,大多数具体细节同样适用于方案 1。
相对于方案 1 的优势
- 飞地至少少开一个端口(“提交”端口),这意味着如果我们仅允许物理访问该机器且不允许 SSH(我们可以将 SSH 隐藏在 TOR 中),端口数可以降至零。
- 取决于实现细节,针对任意文件签名的防御可能会更强健。
相对于方案 1 的缺陷
- 两端都需要监视器(等待待签名文件,等待签名上传) vs 几乎不需要(虽然你仍然需要等待签名返回,但那是你“握手”过程的一部分)。
- 复杂度更高,但安全性提升有限(在某种程度上,获得一点安全性提升需要付出巨大努力)。
https://www.mail-archive.com/arch-dev-public@archlinux.org/msg25637.html
优势与缺陷对比
- 两种方案都有一个问题,即在一段时间内仓库数据库是没有签名的。一种解决方案是使用临时数据库,然后将其移动覆盖。
其他发行版的解决方案
Debian
OpenSuSe 构建服务
OBS (OpenSuSe Build Service) 拥有一个签名守护进程,负责接收数据并返回签名。
Fedora
开放问题
- 应该使用哪个密钥为仓库数据库签名?
- 使用 SSH 进行远程签名的仓库数据库签名可行吗?
- 公钥的分发。放入 keyring 中?
- 使用硬件令牌?