私有 Feed 负责“谁能下载哪些 BitzOrcas.* 包和版本”。它不替代 Runtime License,也不能把一个客户的下载凭据复用于另一个客户。
先分清四类身份
| 身份 | 权限 | 凭据位置 |
|---|---|---|
| Feed 管理员 | 建立源、不可变策略、备份、审计 | Feed 管理面 |
| 发布流水线 | 只向 staging/发布源写入候选包 | GitHub commercial-release 环境 |
| GA 流水线 | 只读恢复并核对正式 Feed | GitHub commercial-release 环境 |
| 客户/开发者 | 只读指定产品、版本通道与包前缀 | Credential Provider 或个人 Secret Store |
发布 API key 与 Consumer token 必须是两份独立凭据。任何浏览器前端、Consumer 仓库、模板或 NuGet.Config 都不能持有发布凭据。
1. 建立 Feed
Feed 产品可替换,但必须具备以下能力:
- HTTPS endpoint,地址不含 userinfo、query 或 fragment;
- 已发布的
PackageId + Version不可覆盖; - 最好支持 staging/promote;若不支持,必须有“同 identity 仅在字节完全相同时续传”的恢复协议;
- Token 可按客户、产品、
BitzOrcas.*包前缀和版本通道最小授权; - 读写凭据分离,可撤销、可轮换、有审计;
- 包、元数据和权限策略有备份与恢复演练。
2. 配置发布仓库
在 GitHub Repository/Environment 中配置:
| 类型 | 名称 | 用途 |
|---|---|---|
| Variable | BITZORCAS_COMMERCIAL_FEED_URL | 无凭据 HTTPS Feed 地址 |
| Secret | BITZORCAS_COMMERCIAL_PUSH_API_KEY | 发布流水线独占写凭据 |
| Secret | BITZORCAS_COMMERCIAL_FEED_CREDENTIALS | GA 使用的 NuGet 标准只读凭据 |
| Secret | BITZORCAS_NUGET_SIGNING_CERTIFICATE_BASE64 | 签名环境使用的 Base64 PFX |
| Secret | BITZORCAS_NUGET_SIGNING_CERTIFICATE_PASSWORD | 上述 PFX 的独立密码 |
| Variable | BITZORCAS_NUGET_TIMESTAMP_URL | 受信 RFC 3161 时间戳 URL(HTTP 或 HTTPS) |
| Variable | BITZORCAS_TRUSTED_SIGNER_FINGERPRINTS | 允许的包签名证书指纹 |
| Variable | BITZORCAS_TRUSTED_RELEASE_WORKFLOW | 受信候选构建工作流 |
| Variable | BITZORCAS_TRUSTED_ATTESTATION_WORKFLOW | 受信证明工作流 |
| Variable | BITZORCAS_TRUSTED_ATTESTATION_WORKFLOW_DIGEST | 固定工作流提交 |
| Variable | BITZORCAS_VULNERABILITY_THRESHOLD | 漏洞门禁阈值 |
| Variable | BITZORCAS_THIRD_PARTY_LICENSE_POLICY | 第三方许可证策略标识 |
| Variable | BITZORCAS_APPROVED_LICENSE_EXPRESSIONS | 明确批准的许可证表达式 |
BITZORCAS_COMMERCIAL_FEED_CREDENTIALS 的值必须符合:
Username=<只读账号>;Password=<短期只读 token>;ValidAuthenticationTypes=Basic签名证书两项只放入受保护的 commercial-release-builder 环境;Feed 写入和 GA 只读凭据放入 commercial-release 环境。不要把以上值写入 workflow YAML。两个环境都应启用审批、最小管理员范围和 Secret 访问审计。
3. 初始化 Consumer
在产品仓库根目录执行:
export BITZORCAS_COMMERCIAL_FEED_URL="https://packages.example.com/nuget/v3/index.json"
scripts/feed/init-consumer.sh /absolute/path/to/ConsumerSolution脚本只会在目标目录没有 NuGet.Config 时创建无凭据配置;已有文件只验证,不覆盖。生成结果包含:
<clear />,防止继承开发机未知源;BitzOrcasCommercial,值从%BITZORCAS_COMMERCIAL_FEED_URL%解析;BitzOrcas.*只映射到商业 Feed;- 公共包映射到
nuget.org; - 不含
packageSourceCredentials。
先做本地结构诊断:
scripts/feed/doctor.sh config /absolute/path/to/ConsumerSolution/NuGet.Config预期只输出配置状态,不输出 URL、账号或 Token。
4. 注入只读凭据并恢复
优先使用组织提供的 NuGet Credential Provider。没有 Provider 时,可以让包装脚本从终端安全读取短期 Token:
# 包装脚本只把短期凭据注入当前 restore 子进程。export BITZORCAS_COMMERCIAL_FEED_URL="https://packages.example.com/nuget/v3/index.json"
scripts/feed/with-credentials.sh -- \ dotnet restore /absolute/path/to/ConsumerSolution/Consumer.slnx \ --configfile /absolute/path/to/ConsumerSolution/NuGet.ConfigCI 应从 Secret Store 向单个 restore/install 进程注入标准变量:
NuGetPackageSourceCredentials_BitzOrcasCommercial不要使用 dotnet nuget add source --store-password-in-clear-text,也不要把 Token 放进命令参数、shell profile、镜像层或构建日志。
5. 做在线空缓存探针
结构验证只能证明配置正确,不能证明认证、权益和包签名链。选择一个客户确实有权下载的精确包和版本:
# 用真实有权下载的精确包版本验证认证、权益和签名链。scripts/feed/doctor.sh consumer \ /absolute/path/to/ConsumerSolution/NuGet.Config \ --online BitzOrcas.Profile.MiniApi 1.2.3探针会隔离 NUGET_PACKAGES、HTTP cache、plugin cache 和 DOTNET_CLI_HOME,从空缓存恢复精确 nupkg,再执行:
dotnet nuget verify <package> --all成功条件是恢复和完整签名验证都通过。命中开发机旧缓存不算成功。
6. 发布一个版本
发布顺序固定:
- 在受保护
main手工运行 Build Commercial Release Candidate,输入唯一release_train; - 记录成功 run id、artifact name、完整 40 位 Git commit;
- 用这三个值运行 Publish Commercial Candidate,只推送已签名、已证明的原始 nupkg;
- 用完全相同的三个值运行 Commercial GA;
- GA 通过后,才把版本加入客户可用通道和升级说明。
不要在发布 Job 中重新 pack,也不要手工修改候选包后重传。同一版本内容变化必须提升版本号并重新走完整候选链。
7. 向客户分发访问权
为每个客户或自动化主体单独签发只读、短期、可撤销凭据,并记录:
- Customer、Product 和负责人;
- 允许的包前缀与版本通道;
- 签发、到期和最近使用时间;
- 分发渠道与 Secret Store 位置;
- 轮换责任人和撤销工单。
通过企业 Secret Manager、Credential Provider 或一次性安全交付通道分发。不要通过邮件、聊天记录、Wiki 或工单正文发送 Token。客户仓库只保留无密钥 NuGet.Config。
8. 轮换与撤销
正常轮换:
- 签发新只读 Token;
- 在一台空缓存 Consumer 上完成在线探针;
- 更新 CI/开发者 Secret Store;
- 观察旧 Token 不再使用;
- 撤销旧 Token 并记录证据。
泄漏处置:
- 立即撤销相关 Token;
- 查询下载、IP、PackageId 和版本审计;
- 检查同一 Secret 是否被复用于发布或其他客户;
- 签发权限更小的新 Token;
- 若包完整性可疑,冻结通道并按恶意包事件处理。
Feed 故障不会使已经部署的 Runtime License 自动失效;已部署实例继续按本地签名 License 和离线租约运行。新 restore、升级和新环境部署则会受影响。
诊断速查
| 现象 | 首先检查 |
|---|---|
NU1101 | Package Source Mapping、客户权益、精确版本是否已发布 |
401/403 | Token 是否过期、是否只读、源名称是否为 BitzOrcasCommercial |
| 本机成功、CI 失败 | 四类缓存是否隔离,CI 是否注入标准变量 |
NU300x | 包签名、证书链、可信时间戳和受信指纹 |
| 同版本哈希不同 | 立即停止使用;检查覆盖策略和部分发布恢复 |
| restore 很慢 | 区分 Feed 网络、Credential Provider、首次空缓存和依赖数量 |
验收清单
- Consumer 配置无凭据且 Source Mapping 精确;
- 发布与只读凭据物理分离;
- 一个真实客户身份通过空缓存恢复与
dotnet nuget verify --all; - 已发布版本不可覆盖;
- 候选构建、发布和 GA 绑定同一 artifact identity 与 commit;
- Token 有负责人、到期、轮换和撤销记录;
- Feed 有备份、恢复和部分发布处置方案。