feat: self-generate and store the terraform registry signing key
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

Rather than requiring an operator to create a GPG key and a K8s secret, the
registry now provisions itself: on first start artifactapi generates a signing
keypair and persists it in a new signing_keys table, so all replicas share one
key and there is nothing to set up. TF_SIGNING_KEY_PATH still overrides with a
bring-your-own key when set.

- signing_keys table + GetSigningKey / InsertSigningKeyIfAbsent (ON CONFLICT DO
  NOTHING so a replica race converges on one key)
- tfsign.Generate, LoadArmored, and LoadOrCreate(store, purpose)
- server prefers a configured key file, else LoadOrCreate against the DB
- tests: generate/load round-trip, load-or-create generates once then reuses,
  DB insert idempotency
This commit is contained in:
2026-07-03 18:46:09 +10:00
parent edb6c7c0f7
commit 97cdb9c6b5
7 changed files with 228 additions and 15 deletions
+11 -3
View File
@@ -70,11 +70,19 @@ func New(cfg *config.Config, version string) (*Server, error) {
virtEngine := virtual.NewEngine(db, engine)
collector := gc.New(db, s3, 1*time.Hour)
// A failure to load the signing key must not take the server down: the
// terraform registry simply stays disabled until a valid key is present.
signer, err := tfsign.Load(cfg.TFSigningKeyPath, cfg.TFSigningKeyPassphrase)
// The terraform registry signs with a GPG key. A configured file wins (BYO
// key); otherwise artifactapi generates one on first start and persists it in
// the database so every replica shares it. A failure here must not take the
// server down — the registry just stays disabled.
var signer *tfsign.Signer
if cfg.TFSigningKeyPath != "" {
signer, err = tfsign.Load(cfg.TFSigningKeyPath, cfg.TFSigningKeyPassphrase)
} else {
signer, err = tfsign.LoadOrCreate(context.Background(), db, "terraform-provider")
}
if err != nil {
slog.Warn("terraform provider registry disabled", "error", err)
signer = nil
}
tfRegistry := tfregistry.NewHandler(db, signer, cfg.TFProviderProtocols)
if tfRegistry.Enabled() {