# 自动安装 Pigsty 仓库公钥

> 尽最大努力准备 Pigsty 公钥，复用已有文件；失败时告警并降级，元数据显式指定密钥时保持严格要求。

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

> **决策日期：** 2026-10-04<br>
> **状态：** Released（已发布），随 [v1.9.0](/zh/release/pig-1.9.0/) 交付。<br>
> **当前参考：** [`pig repo`](/zh/repo/)<br>
> **范围：** 配置仓库时安装已有的内嵌 Pigsty 公钥。

## 决策 {#decision}

当所选模块包含当前平台可用的 `pigsty-infra` 或 `pigsty-pgsql` 仓库时，先准备内嵌
Pigsty 公钥，再修改仓库定义。该逻辑放在共享配置流程中，使 `repo add`、`repo set`
以及复用此流程的调用方保持一致。已有公钥文件直接复用，自动安装尽最大努力；
所选仓库元数据显式引用签名密钥时，安装失败才必须终止。

## 背景 {#context}

[基线实现](https://github.com/pgsty/pig/blob/1c6f52401accc8ad6d7e8ab51248895632d7629b/cli/repo/add.go)
只在调用方指定 `GPGCheck` 时安装公钥，原生 `sty boot` 使用了这一选项。
因此，普通 `repo add/set` 没有安装 PIG 已经内嵌的公钥。

两个 Pigsty 仓库都使用此公钥，而它们分布在 `pigsty`、`infra`、`pgsql` 和默认的
`all` 选择中。只检查命令参数是否包含字面量 `pigsty` 会遗漏正常用法。

APT 不会自动发现 `/etc/apt/keyrings` 中的文件，仓库必须通过 `signed-by` 引用准备好的
公钥，否则即使文件写入成功，刷新缓存时仍会出现 `NO_PUBKEY` 警告。

降级时删除 `signed-by`，可能与保留的同源 APT 定义冲突，因此成功和失败时都保持默认引用一致。
显式元数据指定的是它实际需要的密钥，准备一个无关的默认文件不能满足要求。

## 备选方案 {#alternatives}

- **运行时下载默认公钥。** 现有内嵌发布输入已经提供公钥，支持离线使用，无须增加网络或外部工具依赖。
- **每次仓库操作都安装公钥。** 无关仓库不应触发 Pigsty 公钥写入，应先解析模块成员与平台可用性。
- **自动安装公钥失败就阻止仓库操作。** 可选的公钥准备不应阻止已有兼容模式工作，因此拒绝这种处理。
  显式签名密钥元数据仍是严格边界。
- **同时开启所有签名校验。** 发布者身份、RPM 包签名和 APT 元数据认证是不同的契约。
  自动安装公钥不能证明所有软件包或镜像都具备对应签名。

## 契约 {#contract}

- 安装前校验请求的模块。被拒绝的仓库替换请求不会安装公钥或移动仓库文件。
- 检查展开后模块中的两个 Pigsty 仓库标识符，包括自定义模块；不凭 URL 主机名选择签名身份。
- EL 写入 `/etc/pki/rpm-gpg/RPM-GPG-KEY-pigsty`，Debian/Ubuntu 写入
  `/etc/apt/keyrings/pigsty.asc`；仅在需要安装时写入内嵌公钥，权限为 `0644`。
- 先检查公钥普通文件是否存在，包括能解析到普通文件的符号链接；存在时复用，不修改内容或权限。
  存在性检查失败时仍尝试安装。
- 父目录缺失时使用已有 sudo 回退机制创建，并通过原子文件写入逻辑安装。
  检查目标时遇到权限不足，仍应进入提权安装并再次检查目标。目标被目录或悬空符号链接占用属于安装错误。
- 未显式指定密钥时，安装失败只告警并继续；仅所选 Pigsty 定义降级为 APT `trusted=yes`，
  或 RPM `gpgcheck=0` 与 `repo_gpgcheck=0`。成功和失败时均补齐默认的 `gpgkey` 或 `signed-by`
  引用，不通过改写已保留的仓库文件实现降级。
- 显式密钥准备针对实际引用。APT 的 `signed-by` 接受已有普通密钥文件的绝对路径，多个路径以
  逗号分隔；显式引用默认路径时可安装内嵌公钥。EL 按需准备显式引用的默认公钥文件，再把
  `gpgkey` 中的路径或不含变量的 URL 交给原生 `rpm --import`。
- 显式密钥准备失败会在备份、写入仓库、刷新缓存之前终止。保留其引用，绝不降级该定义。
  有效自定义密钥不依赖默认路径；其他所选仓库的默认密钥失败只使对应的默认定义降级。
- 普通操作成功准备公钥后保留签名校验设置。`sty boot` 在公钥准备成功时请求启用校验，
  默认公钥失败时采用同样的降级规则。
- 降级警告同时写入 stderr 和 `repo add/set` 的 `data.warnings`，`sty boot` 也保留在自身告警中。
  默认安装不导入 RPM 数据库，EL 显式密钥配置会导入；两种情况都不修改 APT 全局信任库。
- 仓库回滚恢复仓库定义，已经安装或导入的公钥保留。

## 影响 {#impact}

添加 Pigsty 仓库时会一并尝试准备公钥文件，离线主机也适用。默认公钥失败不再阻止仓库配置。
降级会关闭这些 Pigsty 定义的签名校验，并明确报告。存在性检查成功后保留管理员已有文件，
不校验其内容；第三方仓库的信任策略保持不变。
密钥不可用时，APT 可能告警并沿用此前认证过的索引，因此仓库配置成功不能证明每个索引都刚刚下载。
当前默认行为和路径以[仓库参考页](/zh/repo/)为准。

## 验证与演进 {#verification}

[自动公钥准备实现](https://github.com/pgsty/pig/blob/56f73537ac15ad5eb2520aa18ac33dab2bb862a0/cli/repo/key.go)
与[仓库回归测试](https://github.com/pgsty/pig/blob/56f73537ac15ad5eb2520aa18ac33dab2bb862a0/cli/repo/add_key_test.go)
已记录在源代码提交 `56f7353` 中。本地回归测试覆盖 EL、Debian、Ubuntu
及两种支持的架构，并检查默认选择、组合模块、已有公钥复用、存在性检查被拒、自定义模块、
不可用仓库、默认公钥降级，以及显式密钥安装失败时不修改仓库。已有仓库替换测试继续验证回滚行为。

本地 EL9、EL10、Debian 12 和 Ubuntu 24 ARM64 虚拟机中的实际 CLI 测试使用独立挂载命名空间，验证普通用户通过
sudo 创建缺失的密钥目录、内嵌公钥字节与权限一致、重复执行、组合模块、可解析的结构化输出，
以及默认公钥失败时告警并继续、显式密钥失败时在替换仓库之前停止。测试使用真实 DNF/APT，
使用隔离缓存，并查询生成索引中的可用包。APT 还验证成功安装后的密钥引用
消除了 `NO_PUBKEY` 警告，以及显式配置 `signed-by` 与 `trusted=no` 时能完成元数据认证。
这些检查不安装软件包，也不验证 RPM 包签名。虚拟机原有仓库定义、公钥与缓存保持不变。

后续回归补充了保留旧 APT 源与已认证缓存、自定义密钥缺失时阻止添加和替换、默认路径不可用时
有效自定义密钥仍能工作，以及显式与默认密钥混合配置的场景。权限测试禁止普通用户遍历密钥目录，
验证提权安装和隐藏符号链接拒绝行为。RPM 导入测试使用虚拟机 RPM 数据库的隔离副本。

## 当前状态 {#status}

该实现已进入经过验证的 [v1.9.0 标签](https://github.com/pgsty/pig/tree/v1.9.0)
与[正式发布制品](https://github.com/pgsty/pig/releases/tag/v1.9.0)。上述实现与本地验证的证据范围保持不变。
软件仓库发布、公开文档部署与既有系统升级仍是独立交付阶段。
