docs(pdftract-3wrx): add release signing strategy note
Resolves OQ-10: document v1.0.0 stance on binary signing. - Linux: GPG-signed (implemented) - macOS: Deferred to v1.1+ ($99/yr Apple Developer Program) - Windows: Deferred to v1.1+ ($200-400/yr Authenticode cert) - All platforms: SLSA Level 2 attestation (already committed) Closes: pdftract-3wrx Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
6ffeccc26e
commit
eb025f7b1a
1 changed files with 258 additions and 0 deletions
258
docs/notes/release-signing.md
Normal file
258
docs/notes/release-signing.md
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
# Release Binary Signing Strategy
|
||||
|
||||
**Status:** RESOLVED (OQ-10)
|
||||
**Date:** 2026-05-24
|
||||
**Bead:** pdftract-3wrx
|
||||
|
||||
## Open Question OQ-10
|
||||
|
||||
> What is the v1.0.0 stance on signed binaries — code-signed macOS releases, Authenticode-signed Windows binaries, GPG-signed Linux releases? Each adds CI complexity.
|
||||
|
||||
## Resolution Decision
|
||||
|
||||
**v1.0.0 Stance:** GPG-sign Linux artifacts only. Defer macOS and Windows code-signing to v1.1+.
|
||||
|
||||
| Platform | Code Signing | v1.0.0 | Rationale |
|
||||
|----------|--------------|--------|-----------|
|
||||
| Linux | GPG signature | ✅ Yes | Low cost; strong trust chain; distro compatibility |
|
||||
| macOS | Developer ID + notarization | ❌ Defer | $99/yr Apple Developer Program cost |
|
||||
| Windows | Authenticode | ❌ Defer | $200-400/yr cert cost; cross-platform CI complexity |
|
||||
|
||||
## Background: Code-Signing vs Attestation
|
||||
|
||||
Code-signing and SLSA provenance answer **different questions**:
|
||||
|
||||
- **Code-signing** (macOS Developer ID, Windows Authenticode): *Who built this?* Binds a binary to an identity certificate. End-of-chain trust anchors are Apple/Microsoft root CAs.
|
||||
- **SLSA provenance** (`provenance.intoto.jsonl`): *How was this built?* Cryptographic attestation of the build process (materials, inputs, builder identity). Trust anchor is the build infrastructure's signing key.
|
||||
|
||||
Both are complementary. SLSA Level 2 (already committed per plan line 895) provides supply-chain transparency. Code-signing provides platform-native trust integration (Gatekeeper, SmartScreen).
|
||||
|
||||
## SLSA Level 2 Attestation (Already Implemented)
|
||||
|
||||
Per plan line 895, every GitHub Release includes `provenance.intoto.jsonl` generated by the Argo runner on `iad-ci`. This attestation contains:
|
||||
|
||||
- **Builder identity:** Argo Workflow execution on `iad-ci`
|
||||
- **Materials:** Git commit SHA, dependency crate versions (from `Cargo.lock`)
|
||||
- **Recipe:** Build command invocation
|
||||
- **Metadata:** Timestamp, workflow template
|
||||
|
||||
The attestation is signed by the CI fleet's root key. Consumers verify using `slsa-verifier`:
|
||||
|
||||
```bash
|
||||
slsa-verifier verify-artifact \
|
||||
--provenance-path provenance.intoto.jsonl \
|
||||
--source-uri github.com/jedarden/pdftract \
|
||||
--source-tag v1.0.0
|
||||
```
|
||||
|
||||
This provides supply-chain integrity **without** platform-specific code-signing.
|
||||
|
||||
## Platform-Specific Analysis
|
||||
|
||||
### Linux: GPG Signing
|
||||
|
||||
**Status:** ✅ **Implemented in v1.0.0**
|
||||
|
||||
**Implementation:**
|
||||
- Release tarball (`pdftract-$VERSION-x86_64-unknown-linux-gnu.tar.gz`) and cargo registry crate are signed with a maintainer-controlled GPG key
|
||||
- Public key is checked into the repo at `docs/signing/pubkey.asc`
|
||||
- Signature files (`*.tar.gz.asc`) are uploaded alongside artifacts on GitHub Releases
|
||||
- `Cargo.toml` includes `metadata.signing` key for cargo verification
|
||||
|
||||
**Rationale for v1.0.0:**
|
||||
- **Zero cost:** GPG is free; key generation is local
|
||||
- **Strong trust:** GPG web-of-trust is well-understood by Linux users
|
||||
- **Distro compatibility:** Many Linux distributions require GPG signatures for third-party packages
|
||||
- **Low CI complexity:** `gpg --detach-sign` is a single command; key lives in OpenBao as a Kubernetes Secret
|
||||
|
||||
**Key Management:**
|
||||
- **Private key:** Stored in OpenBao, synced to Kubernetes Secret `signing-gpg-private` in the `iad-ci` namespace
|
||||
- **Passphrase:** Separate OpenBao entry, injected as env var `GPG_PASSPHRASE` during signing step
|
||||
- **Public key:** Checked into repo at `docs/signing/pubkey.asc` (NOT secret)
|
||||
- **Rotation:** Annually, or immediately on suspected compromise. Rotate = generate new key, update `pubkey.asc`, re-sign latest release, publish new fingerprint.
|
||||
|
||||
**CI Integration:**
|
||||
```yaml
|
||||
# Argo WorkflowTemplate: pdftract-release
|
||||
- name: sign-linux-artifacts
|
||||
container:
|
||||
image: debian:bookworm-slim
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "$GPG_PRIVATE_KEY" | gpg --import --batch --passphrase "$GPG_PASSPHRASE"
|
||||
for artifact in pdftract-*.tar.gz; do
|
||||
gpg --detach-sign --armor --passphrase "$GPG_PASSPHRASE" "$artifact"
|
||||
done
|
||||
env:
|
||||
- name: GPG_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: signing-gpg-private
|
||||
key: key
|
||||
- name: GPG_PASSPHRASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: signing-gpg-passphrase
|
||||
key: passphrase
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
# Download and verify release
|
||||
wget https://github.com/jedarden/pdftract/releases/download/v1.0.0/pdftract-1.0.0-x86_64-unknown-linux-gnu.tar.gz{,.asc}
|
||||
gpg --verify pdftract-1.0.0-x86_64-unknown-linux-gnu.tar.gz.asc
|
||||
gpg --import docs/signing/pubkey.asc # First time only
|
||||
```
|
||||
|
||||
### macOS: Code Signing + Notarization
|
||||
|
||||
**Status:** ❌ **Deferred to v1.1+**
|
||||
|
||||
**What would be required:**
|
||||
1. **Apple Developer Program account** ($99/year)
|
||||
2. **Developer ID Application certificate** (issued by Apple)
|
||||
3. **Notarization** (post-sign submission to Apple's notarization service)
|
||||
4. **Stapling** (attach notarization ticket to binary)
|
||||
|
||||
**Implementation cost:**
|
||||
- **Financial:** $99/year recurring (Apple Developer Program)
|
||||
- **Tooling:** `codesign` CLI (built into Xcode), `xcrun notarytool submit` (requires Apple ID auth)
|
||||
- **CI complexity:**
|
||||
- Certificates stored as `.p12` in OpenBao
|
||||
- Notarization requires Apple ID app-specific password (separate secret)
|
||||
- Two-step signing+notarization workflow
|
||||
|
||||
**Consequences of NOT signing (current state):**
|
||||
- **Gatekeeper quarantine:** macOS quarantines unsigned downloads; users see "cannot be opened because the developer cannot be verified"
|
||||
- **User friction:** Users must right-click → Open, or use `xattr -d com.apple.quarantine pdftract`
|
||||
- **No SmartScreen integration:** macOS doesn't block execution, but warns on first run
|
||||
|
||||
**Why defer to v1.1+:**
|
||||
- **Funding question:** $99/year is non-trivial for a v1.0.0 release with uncertain adoption
|
||||
- **ADR-009 constraint:** `iad-ci` is Linux-only; cannot run `codesign` natively. Would need macOS runner or cross-compilation with remote signing
|
||||
- **Alternative:** Community-sponsored Apple Developer Program account (e.g., "pdftract is signed by Ardenone LLC under their developer account")
|
||||
|
||||
**v1.1+ trigger:**
|
||||
- Sustained macOS user base (>10% of downloads)
|
||||
- Funding allocation for Apple Developer Program
|
||||
- OR community contribution: "I will sign pdftract binaries under my Apple Developer account"
|
||||
|
||||
### Windows: Authenticode Signing
|
||||
|
||||
**Status:** ❌ **Deferred to v1.1+**
|
||||
|
||||
**What would be required:**
|
||||
1. **Code signing certificate** from a Windows-trusted CA (DigiCert, Sectigo, GlobalSign)
|
||||
2. **Certificate cost:** $200-400/year (standard), ~$1000/year (EV certificate)
|
||||
3. **Signing tool:** `signtool` (Windows SDK) or `osslsigncode` (cross-platform)
|
||||
|
||||
**Implementation cost:**
|
||||
- **Financial:** $200-400/year minimum
|
||||
- **Tooling:**
|
||||
- **Native:** `signtool sign /f cert.pfx /p password ...` (requires Windows)
|
||||
- **Cross-platform:** `osslsigncode` (Linux-compatible, but less battle-tested)
|
||||
- **CI complexity:** `iad-ci` is Linux-only per ADR-009. `signtool` doesn't run on Linux. Options:
|
||||
1. **Cross-compile + remote signing:** Build on Linux, transfer binary to Windows VM for signing (complex)
|
||||
2. **osslsigncode:** Open-source alternative, but unclear if Windows SmartScreen trusts it equally
|
||||
3. **Azure Trusted Signing:** Microsoft's cloud signing service (requires Azure AD tenancy, per-certificate cost)
|
||||
|
||||
**Consequences of NOT signing (current state):**
|
||||
- **SmartScreen warning:** Users see "Windows protected your PC" warning on first run
|
||||
- **User friction:** Users must click "More info" → "Run anyway"
|
||||
- **No Microsoft Store compatibility:** Cannot publish to Microsoft Store without signing
|
||||
|
||||
**Why defer to v1.1+:**
|
||||
- **Funding question:** $200-400/year is non-trivial
|
||||
- **CI-platform mismatch:** ADR-009 mandates Linux-only CI; Authenticode signing is fundamentally Windows-centric
|
||||
- **osslsigncode uncertainty:** Unclear if cross-platform signing produces identical SmartScreen behavior
|
||||
|
||||
**v1.1+ trigger:**
|
||||
- Sustained Windows user base (>10% of downloads)
|
||||
- Funding allocation for certificate
|
||||
- OR community contribution: "I will sign pdftract binaries under my certificate"
|
||||
|
||||
## Failure Modes and Mitigations
|
||||
|
||||
### macOS Notarization Rejection
|
||||
|
||||
**Failure:** `xcrun notarytool` returns `Status: Invalid`
|
||||
|
||||
**Mitigation:**
|
||||
- Do not ship unsigned binary with failed notarization
|
||||
- CI step aborts release; diagnostic logged to Argo Workflow
|
||||
- Maintainer must investigate (malware fingerprint, entitlement issue, Apple ID problem)
|
||||
|
||||
### SmartScreen False Positive (Windows)
|
||||
|
||||
**Failure:** Unsigned binary triggers SmartScreen "unrecognized app" warning
|
||||
|
||||
**Mitigation (v1.0.0, unsigned):**
|
||||
- Document in README: "Click 'More info' → 'Run anyway'"
|
||||
- Encourage users to verify GPG signature on Linux subsystem or WSL
|
||||
- Track SmartScreen false-positive rate via user feedback
|
||||
|
||||
**Mitigation (v1.1+, signed):**
|
||||
- Timestamped Authenticode signature prevents post-signage tampering
|
||||
- Certificate reputation improves over time (fewer warnings as adoption grows)
|
||||
|
||||
### GPG Key Compromise
|
||||
|
||||
**Failure:** Maintainer's GPG private key is exfiltrated from OpenBao
|
||||
|
||||
**Mitigation:**
|
||||
- **Immediate:** Rotate compromised key (generate new key, revoke old on keyserver)
|
||||
- **Re-sign:** Re-sign latest release with new key
|
||||
- **Communicate:** Publish security advisory with new fingerprint
|
||||
- **Postmortem:** Audit OpenBao access logs; implement stricter RBAC
|
||||
|
||||
**Prevention:**
|
||||
- OpenBao's transit encryption (key never leaves Vault in plaintext)
|
||||
- Kubernetes Secret with strict RBAC (only `pdftract-release` workflow can read)
|
||||
- Quarterly key rotation (planned, not implemented)
|
||||
|
||||
## Version Policy
|
||||
|
||||
### v1.0.0 (Current Release)
|
||||
|
||||
- **Linux:** GPG-signed (`*.tar.gz.asc`)
|
||||
- **macOS:** Unsigned (quarantine warning expected)
|
||||
- **Windows:** Unsigned (SmartScreen warning expected)
|
||||
- **All platforms:** SLSA Level 2 attestation (`provenance.intoto.jsonl`)
|
||||
|
||||
### v1.1.0 (Future Release, TBD)
|
||||
|
||||
- **Linux:** GPG-signed (no change)
|
||||
- **macOS:** Developer ID signed + notarized (funding dependent)
|
||||
- **Windows:** Authenticode-signed (funding dependent)
|
||||
- **All platforms:** SLSA Level 2 attestation (no change)
|
||||
|
||||
## Release Checklist (Per KU-12)
|
||||
|
||||
The `docs/operations/manual-platform-smoke.md` runbook (KU-12) includes signature verification:
|
||||
|
||||
1. **Download release artifacts** from GitHub Releases
|
||||
2. **Verify GPG signature** (Linux): `gpg --verify *.asc`
|
||||
3. **Verify SLSA provenance** (all platforms): `slsa-verifier verify-artifact`
|
||||
4. **Run smoke test** (binary execution, extraction sanity check)
|
||||
5. **Check platform-specific behavior**:
|
||||
- macOS: Does Gatekeeper quarantine the binary? (Expected v1.0.0, unexpected v1.1+)
|
||||
- Windows: Does SmartScreen warn? (Expected v1.0.0, unexpected v1.1+)
|
||||
- Linux: Does GPG verify successfully? (Expected always)
|
||||
|
||||
## References
|
||||
|
||||
- Plan Open Question OQ-10 (line 521)
|
||||
- Plan Supply Chain Considerations (line 895): SLSA Level 2 commitment
|
||||
- Plan ADR-009 (line 495): Argo Workflows on `iad-ci` (Linux-only CI)
|
||||
- Plan Known Unknown KU-12 (line 608): Manual quarterly smoke test
|
||||
- Apple Developer Documentation: [Code Signing](https://developer.apple.com/support/code-signing/)
|
||||
- Microsoft Docs: [Authenticode](https://docs.microsoft.com/en-us/windows/win32/seccrypto/cryptography-tools)
|
||||
- SLSA: [SLSA Level 2](https://slsa.dev/spec/v0.1/levels#level-2)
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Change |
|
||||
|------|--------|
|
||||
| 2026-05-24 | Initial resolution; v1.0.0 stance (GPG Linux only) |
|
||||
Loading…
Add table
Reference in a new issue