Skip to content

Releases: OpScaleHub/keyfold

v0.10.0

Choose a tag to compare

@AliRezaTaleghani AliRezaTaleghani released this 03 Sep 06:46

v0.10.0 — 2026-09-03

Whole-file encryption binds the repository (#79)

  • A ciphertext blob sealed in one repo now fails authentication if dropped into
    another that merely shares the key. init generates a random repo_id,
    commits it in .repo-enc.yml, and whole-file encryption folds it into the
    AEAD's additional-authenticated-data (a v2 envelope, which also authenticates
    the envelope header). Repos created before this keep sealing/reading v1
    (path-only AAD) unchanged — no migration is forced; opt in with a fresh init
    or by adding repo_id and running rotate-keys. Don't change repo_id
    once set
    — existing v2 blobs would stop verifying.
  • This does not add freshness: rolling a tracked file back to an earlier
    ciphertext for the same path still authenticates (no counter on the CLI path).
    New threat-model.md T10-CLI row.
  • Documented the AES-GCM 96-bit-nonce ceiling that keeps it off the default path
    (crypto.Default is always XChaCha20-Poly1305 for new seals).

CI/CD & supply-chain hardening (#78)

  • Every third-party GitHub Action is pinned to a commit SHA; .github/dependabot.yml
    keeps the pins, Go modules, and Docker base images current (the Kubernetes
    client stack is its own group — see #86).
  • release.yml runs with a default-deny token (permissions: {}), and every
    released binary now carries a signed SLSA build-provenance attestation
    (gh attestation verify …); container images are signed keyless with
    cosign and carry provenance + SBOM attestations. An SPDX SBOM of the
    module graph is attached to each release. See SECURITY.md → "Verifying a
    release".
  • Dockerfile base images pinned by digest. CI gained advisory govulncheck +
    Trivy scans, runs the race detector on Linux, and fails if the GPG test suite
    is silently skipped there.

Controller hardening (Beta → GA, #77)

  • Reconcile no longer self-triggers. The reconciler wrote status.lastSyncTime
    on every pass; that write returned through its own watch and re-enqueued the
    object, so a steady-state GitSecret reconciled forever. Status is now written
    only when a field other than lastSyncTime changes.
  • Webhook now requires replicaCount: 1. The serving cert is per-pod and the
    ValidatingWebhookConfiguration.caBundle holds one CA; the chart refuses
    webhook.enabled with more than one replica, and the CA injector is
    leader-gated. Reconcile HA via leader election is unchanged.
  • Tighter controller RBAC. Dropped unused verbs (secrets: delete,
    gitsecrets: update/patch); scoped validatingwebhookconfigurations
    update/patch to the controller's own config by name. New watchNamespaces
    value confines the cache and Secret RBAC to a fixed namespace list
    (--watch-namespaces).
  • New UPGRADING.md: the v1alpha1 "additive only" compatibility
    policy.

Deprecations

  • git-secret-server (the ESO webhook bridge) is now explicitly deprecated.
    It is superseded by the native GitSecret CRD + git-secret-controller. The
    binary, image, and Helm chart are still published so existing External Secrets
    Operator users are not broken, and it receives security fixes only — no new
    features. New deployments should use the GitSecret CRD. The chart is marked
    deprecated: true.

Docs

  • Landing page: corrected the hook-skip note — only SECRETIZE_SKIP_HOOKS=1
    skips hooks; the ambient CI variable deliberately does not (it previously
    implied CI=1 would). Rewrote the GitSecret section to stand on its own —
    no competitor named.
  • disaster-recovery.md / recipient-lifecycle.md: clarified that CRD
    recipients remove performs a rewrap (same content key, encryptedData
    untouched), not a content-key rotation — a removed recipient who cached the
    content key can still decrypt values that have not since changed. The CLI
    git secret removeuser still forces a full rotate-keys and is unchanged.
  • threat-model.md T11 (recipient substitution) reworded "Handled" →
    "Partial" — sealer.VerifyRecipients is a count check, not per-fingerprint
    authentication.
  • overview.md: on unseal failure the controller leaves the last-written
    target Secret in place (fail-safe) — documented, with how to force removal.

Verifying this release

gh attestation verify ./git-secret-linux-amd64 --repo OpScaleHub/git-secret
cosign verify \
  --certificate-identity-regexp '^https://github.com/OpScaleHub/git-secret/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/opscalehub/git-secret-controller:v0.10.0

See SECURITY.md → Verifying a release.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 28 Aug 17:47
v0.9: git-secret-seal ui (public-key-only sealing web form); pre-free…

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 20:58
v0.8: security foundation — threat model, disaster recovery, recipien…

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 18 Aug 03:46
v0.7: native GitSecret CRD + controller; ship the controller image an…

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 22:06
v0.7: native GitSecret CRD + controller; ship the controller image an…

v0.6.3

Choose a tag to compare

@github-actions github-actions released this 17 Aug 19:22
v0.6: git-secret-server ESO webhook bridge (later superseded); contai…

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 17 Aug 19:11
v0.6: git-secret-server ESO webhook bridge (later superseded); contai…

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 17 Aug 17:28
v0.6: git-secret-server ESO webhook bridge (later superseded); contai…

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 17:17
v0.6: git-secret-server ESO webhook bridge (later superseded); contai…

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 20:27

Security-focused release covering 25 issues from a self-run audit, plus a redesign of the core verification/enforcement model that most of them shared a root cause with, plus ArgoCD/GitOps documentation for the gpg key backend.

⚠️ Upgrade notes / behavior changes

  • verify now requires the key to be available. It authenticates every candidate ciphertext instead of trusting a magic-byte prefix, so it needs to actually decrypt. If your CI ran verify without key access before, it will now fail with exit code 2 instead of a false "OK" — this is intentional (see #1, #2).
  • Ambient CI=1 no longer bypasses hooks. Only the explicit SECRETIZE_SKIP_HOOKS=1 does now. If your CI relied on the ambient CI var to skip encryption/push-protection, set SECRETIZE_SKIP_HOOKS=1 explicitly (#21).
  • kubectl-secret ciphertext is now bound to the full object identity (apiVersion/kind/metadata.name/namespace), not just the file and key. Values encrypted with prior versions were sealed under the old, narrower AAD and will need to be re-encrypted with encrypt-value under this version (#23).
  • kubectl secret encrypt-value reads the plaintext from stdin by default now (echo -n VALUE | kubectl secret encrypt-value -f FILE -k KEY). Scripts passing it as a positional argument need --allow-argv (#5).
  • k8s_secret_paths manifests are now enforced by verify/pre-commit. A stringData value that isn't ciphertext and isn't explicitly allowlisted via the new k8s_plaintext_keys config field will now be flagged — add it to the allowlist if it's intentionally plaintext (#15).

Verification & enforcement redesign

The core design change: verify/hooks used to check encryption status via a magic-byte prefix, only at HEAD, using working-tree config — a checkpoint with several independent ways around it. verify is now revision-pinned and authenticated; pre-push walks every commit in the pushed range via git's ref-update protocol, not just HEAD.

  • #1 — verify accepted fake RENC-prefixed plaintext as encrypted
  • #2 — verify used mutable index/worktree config, could miss plaintext at HEAD
  • #3 — pre-push verified only HEAD, letting earlier leaked commits reach remotes
  • #4 — a committed raw file-backend key wasn't detected by verify
  • #6 — git path parsing wasn't NUL-safe, letting quoted filenames bypass protection
  • #15 — k8s_secret_paths were ignored by hooks and verify
  • #16 — a global config could silently disable repo-local encryption policy via exclude
  • #20 — root-anchored patterns (/secrets/**) validated but failed open
  • #21 — ambient CI disabled both encryption and push-protection hooks

Path, symlink, and key-lifecycle safety

  • #7 — key_source and explicit paths could escape the repository root
  • #8 — GPG recipients weren't validated as full fingerprints
  • #9 — unlock wrote plaintext secrets with world-readable 0644 mode
  • #10 — removeuser saved the recipient removal before key rotation succeeded
  • #11 — global GPG recipients could be silently included in the committed key.gpg
  • #12 — adduser could grant a new GPG recipient even when the config save failed
  • #18 — matched symlinks could capture local files into encrypted commits
  • #19 — a failed rotate-keys could leave an unignored raw staging key

kubectl-secret per-value crypto

  • #5 — encrypt-value exposed plaintext through argv
  • #23 — Kubernetes value ciphertext wasn't bound to the Secret identity
  • #24 — YAML aliases could copy decrypted stringData into other fields
  • #25 — manifest-state verification gaps (undetected plaintext, argv leak, ArgoCD selfHeal race) — all three sub-items fixed

Release/supply-chain hardening

  • #13 — ArgoCD CMP docs now carry ArgoCD's own warning about manifest-generation secret injection
  • #14 — release binaries are now checksummed (.sha256 assets below); the ArgoCD install snippet verifies before chmod +x
  • #17 — the release workflow no longer interpolates the pushed tag name directly into shell (was shell-injectable)
  • #22 — the ArgoCD CMP key-copy step no longer follows a repo-controlled symlink

Docs

New "Recommended: use the gpg backend instead of file" section for the ArgoCD integration — granting/revoking ArgoCD's access becomes the same adduser/removeuser commands used for teammates, and no raw key material ever needs to be copied into the repo checkout.


Every fix has a regression test. Full commit history: v0.4.1...v0.5.0