Configuration¶
gapsign reads its configuration from $GAPSIGN_CONFIG, else
~/.config/gapsign/config.yaml. Secrets are referenced, never inlined:
signing keys, KMS credentials, and registry credentials live under
~/.config/gapsign/ (mode 0600) or a KMS/env, and only their paths appear in
the config. Unknown keys are a hard error (strict decoding), so a typo can't
silently empty a control.
Full example¶
# Signer backend (signing side):
# cosign-key — key-based (file); airgap default
# cosign-x509 — certificate mode; chains to the corporate CA
backend: cosign-key
# Logical signing identity, referenced by policy rules (rule.signer). Required
# for `gapsign ingest` so the destination repo can be authorized.
signer_name: ci-kms
# Non-secret backend options. Secret material is referenced, not inlined.
backend_opts:
key: ~/.config/gapsign/ci-kms.key
# cosign-x509: key + cert + chain
# cert: ~/.config/gapsign/leaf.crt
# chain: ~/.config/gapsign/corp-ca.pem
# Default OCI registry host (informational).
registry: registry.internal.example.com
# The single source of truth for enforcement (see below).
policy: ~/.config/gapsign/policy.yaml
# Registry hosts `gapsign ingest` may pull from. REQUIRED for ingest (an empty
# list fails closed) — bounds the SSRF surface of an operator-supplied source.
# Host-boundary matched: "docker.io.evil.com" never matches "docker.io".
sources:
- docker.io
- quay.io
# Optional ingest footprint caps (defaults: 2 GiB / 256 layers).
# max_ingest_bytes: 2147483648
# max_ingest_layers: 256
# Trust bundle for `gapsign policy render` — maps each signer to its verify-side
# material. All values are public (safe to commit via GitOps). A signer missing
# or invalid here fails the render, never fail-open.
trust:
# Key mode: a PKIX public key.
ci-kms:
public_key: ~/.config/gapsign/ci-kms.pub
# X.509 mode: signature cert must chain to these CA roots and match identity + issuer.
mirror-x509:
ca_roots: ~/.config/gapsign/corp-ca.pem
identity: "gapsign-ci@corp.example"
issuer: "https://corp.example/oidc"
Policy file¶
policies:
- match: "registry.internal/*" # image-reference glob: exact, "*", or "prefix/*"
signer: ci-kms # a signer named in `trust:`
namespaces: ["prod", "payments"] # scope; empty = all namespaces
require: signed # signed (block) | warn (audit-only)
policies is ordered — the first matching rule wins. An image matching no rule is
default-denied on a protected namespace.
Secrets¶
| Material | Where | Committed? |
|---|---|---|
| Signing private key | ~/.config/gapsign/*.key (0600) or KMS |
never |
| Registry / KMS credentials | ~/.config/, docker keychain |
never |
| Public keys, CA roots | referenced by path; embedded into the rendered policy | yes (public) |
The security residuals ledger (docs/SECURITY_RESIDUALS.md) is local-only and
gitignored; it is never committed or deployed.