Skip to content

docs(secrets): RFC — keyless secret backend (KMS recipients + OIDC)#344

Closed
Cre-eD wants to merge 3 commits into
mainfrom
docs/keyless-secrets-rfc
Closed

docs(secrets): RFC — keyless secret backend (KMS recipients + OIDC)#344
Cre-eD wants to merge 3 commits into
mainfrom
docs/keyless-secrets-rfc

Conversation

@Cre-eD

@Cre-eD Cre-eD commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

What

Adds a design RFC: keyless secret backend — pluggable recipient types whose private
key never leaves a remote authority (cloud KMS / Vault), plus a keyless CI mode via
OIDC federation, so the store private key (SIMPLE_CONTAINER_CONFIG) no longer has to be
materialized on CI runners.

Docs-only (docs/design/keyless-secrets/README.md). No code changes.

Why

The envelope is already multi-recipient, but the only recipient type is a raw private
key — which therefore must be placed on every machine that decrypts, including CI. That
single value is long-lived, all-powerful, un-rotated in practice, un-audited, and
non-revocable after a leak. This RFC makes the key custody pluggable and federated while
keeping the local-key path as the default.

Highlights

  • Cloud-agnostic + backward-compatible: local recipient stays default; AWS/GCP/
    Azure/Vault optional; migration is additive, per-repo, reversible.
  • v2 envelope prerequisite: single AEAD DEK + per-recipient key wrapping + AAD
    binding (so KMS calls are O(1) per deploy, and ciphertext/DEK can't be transplanted).
  • OIDC authorization model: per-stack roles; pin caller repo id + job_workflow_ref;
    ref vs environment subjects are mutually exclusive; production behind protected
    Environments; never pull_request_target with PR-head checkout.
  • Format versioning with a fail-closed guard rolled out before any v2 write.
  • Revocation reality: git history means removing a recipient requires rotating the
    underlying secret values, not just dropping a wrapper.
  • Phased migration with a canary gate and a zero-usage soak before decommission.

This is an RFC for discussion; it folds in an adversarial multi-reviewer pass against the
current pkg/api/secrets implementation.

Proposes pluggable recipient types whose private key never leaves a remote
authority (cloud KMS / Vault) plus a keyless CI mode via OIDC federation, so
the store private key (SIMPLE_CONTAINER_CONFIG) no longer has to be
materialized on CI runners. Cloud-agnostic and backward-compatible: the local
recipient stays the default; migration is additive, per-repo, and reversible.

Covers the v2 envelope prerequisite (single AEAD DEK + per-recipient wrapping
+ AAD binding), the OIDC trust/authorization model, format versioning with a
fail-closed guard, revocation reality (value rotation), operational concerns,
and a phased migration plan.

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Semgrep Scan Results

Repository: api | Commit: 1fcc63d

Check Status Details
⚠️ Semgrep Warning 1 warning(s), 1 total

Scanned at 2026-06-27 20:03 UTC

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Security Scan Results

Repository: api | Commit: 1fcc63d

Check Status Details
✅ Secret Scan Pass No secrets detected
✅ Dependencies (Trivy) Pass 0 total (no critical/high)
✅ Dependencies (Grype) Pass 0 total (no critical/high)
📦 SBOM Generated 523 components (CycloneDX)

Scanned at 2026-06-27 20:03 UTC

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

📊 Statement coverage

Measured on the documented included set (see docs/TESTING.md → Coverage scope). Observe-only — no regression gate is enforced yet.

Scope This PR main baseline Δ
Included set (Gold-tier denominator) 90.3% 90.3% +0.0 pp
Full set (whole repo, transparency) 27.9% 27.9% +0.0 pp

Baseline: main @ 842404b

…OPS, file-per-scope

Folds the multi-model design review outcome into the RFC: adopt SOPS for the
inline-value crypto/format, one readable file per scope (not a multiplexed
single file — avoids the partial-MAC paradox), per-scope isolated keys, the
non-negotiables (hard-fail on missing required secret, MAC+AAD, CODEOWNERS-gated
recipients, plaintext lint, rotate-not-just-remove, OIDC->KMS provider keys),
the strict-backcompat constraint (separate files + fail-closed version reader
first), and a minimal v1. Resolves the format/native-vs-SOPS open questions.

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
@Cre-eD

Cre-eD commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Folded into #346 — the RFC doc now ships in the same PR as its Phase-1 implementation (the version guard), so it's one review/merge instead of two.

@Cre-eD Cre-eD closed this Jun 28, 2026
Cre-eD added a commit that referenced this pull request Jun 28, 2026
Consolidates the keyless-secrets **design** and its **Phase-1
implementation** into one PR (supersedes #344, which was doc-only).

## 1. RFC — keyless secret backend
(`docs/design/keyless-secrets/README.md`)
Design for getting the master key out of GitHub: KMS-held recipients +
OIDC, no behaviour change. Records the decision: **inline values +
per-scope keys, via SOPS, file-per-scope** (`.sc/secrets.<scope>.yaml`)
— avoids the multiplexed "partial-MAC paradox", gives per-scope key
isolation.

## 2. Phase-1 — fail-closed store version guard (code + tests)
Prerequisite for any future on-disk format change (incl. the SOPS
migration above):

- Adds `version` to the `secrets.yaml` schema
(`EncryptedSecretFiles.Version`); absent/`0` = the original format (full
back-compat).
- `CurrentSecretsFileVersion = 0` — the highest schema version this
build understands.
- The reader **refuses** any store whose `version` exceeds what the
build supports, instead of silently dropping the fields it can't model
(which would corrupt the store on the next write).
- Tests: rejects a newer version, accepts current/absent.

**Why it ships first:** this reader must roll out fleet-wide *before*
any higher-versioned store is ever written, or older binaries clobber
it. It is a no-op for every existing (version-0) store today.

Branch is synced with `main`, so history includes the already-merged
#345 (X25519) — net new here = the guard + RFC + tests.

Validated via a preview build (see comments).

---------

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Co-authored-by: Ilya <smecsia@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants