Salt Phase 2b: GPG-encrypted pillar + Terraform integration (#3) #4

Merged
forgejo_admin merged 3 commits from 3-salt-phase-2b-gpg-encryption-secret-migr into main 2026-02-27 20:30:44 +00:00

Summary

  • Migrate platform secrets from plaintext ~/secrets/ to GPG-encrypted Salt pillar
  • Terraform now reads secrets via make tofu-secrets (renders secrets.auto.tfvars from pillar)
  • k3s.tfvars eliminated — replaced by auto-rendered file + variable defaults
  • Age keypair generated and stored in encrypted pillar for future SOPS use

Changes

  • salt/pillar/secrets/platform.sls: GPG-encrypted Tailscale, Grafana, Forgejo, Woodpecker, Harbor, MinIO secrets
  • salt/pillar/secrets/services.sls: GPG-encrypted ArgoCD, Harbor CI, Forgejo ArgoCD token
  • salt/pillar/secrets/forgejo.sls: GPG-encrypted Forgejo/Woodpecker CI credentials
  • salt/pillar/secrets/sops.sls: GPG-encrypted age private key + plaintext public key
  • salt/pillar/secrets_registry.sls: Plaintext metadata — origin, rotation schedule, backup locations
  • salt/pillar/top.sls: Assign all pillar data to archbox minion
  • Makefile: New tofu-secrets target, tofu-plan/tofu-apply auto-depend on it, removed -var-file=k3s.tfvars
  • .gitignore: Allow salt/pillar/secrets/ (GPG-encrypted content, safe to commit)

OpenTofu notes: tofu plan output = "No changes. Your infrastructure matches the configuration." tofu fmt not applicable (no .tf changes).

Test Plan

  • salt-call pillar.items decrypts all 20 secrets correctly
  • make tofu-secrets renders 9 Terraform variables from encrypted pillar
  • make tofu-plan = "No changes" with zero warnings
  • GPG encrypt/decrypt round-trip verified
  • Salt master restart verified healthy after GPG renderer config

Review Checklist

  • No plaintext secrets committed — all values are GPG-encrypted PGP armor blocks
  • No unnecessary file changes
  • Commit messages are descriptive
  • .gitignore exception is scoped to salt/pillar/secrets/ only
  • issue-pal-e-platform-salt-phase-2b-gpg-secrets — the issue this PR addresses
  • plan-2026-02-26-salt-host-management — Phase 2b
  • lesson-salt-gpg-agent-config — GPG agent configuration lesson discovered during this work
## Summary - Migrate platform secrets from plaintext `~/secrets/` to GPG-encrypted Salt pillar - Terraform now reads secrets via `make tofu-secrets` (renders `secrets.auto.tfvars` from pillar) - `k3s.tfvars` eliminated — replaced by auto-rendered file + variable defaults - Age keypair generated and stored in encrypted pillar for future SOPS use ## Changes - `salt/pillar/secrets/platform.sls`: GPG-encrypted Tailscale, Grafana, Forgejo, Woodpecker, Harbor, MinIO secrets - `salt/pillar/secrets/services.sls`: GPG-encrypted ArgoCD, Harbor CI, Forgejo ArgoCD token - `salt/pillar/secrets/forgejo.sls`: GPG-encrypted Forgejo/Woodpecker CI credentials - `salt/pillar/secrets/sops.sls`: GPG-encrypted age private key + plaintext public key - `salt/pillar/secrets_registry.sls`: Plaintext metadata — origin, rotation schedule, backup locations - `salt/pillar/top.sls`: Assign all pillar data to archbox minion - `Makefile`: New `tofu-secrets` target, `tofu-plan`/`tofu-apply` auto-depend on it, removed `-var-file=k3s.tfvars` - `.gitignore`: Allow `salt/pillar/secrets/` (GPG-encrypted content, safe to commit) **OpenTofu notes:** `tofu plan` output = "No changes. Your infrastructure matches the configuration." `tofu fmt` not applicable (no .tf changes). ## Test Plan - [x] `salt-call pillar.items` decrypts all 20 secrets correctly - [x] `make tofu-secrets` renders 9 Terraform variables from encrypted pillar - [x] `make tofu-plan` = "No changes" with zero warnings - [x] GPG encrypt/decrypt round-trip verified - [x] Salt master restart verified healthy after GPG renderer config ## Review Checklist - [x] No plaintext secrets committed — all values are GPG-encrypted PGP armor blocks - [x] No unnecessary file changes - [x] Commit messages are descriptive - [x] `.gitignore` exception is scoped to `salt/pillar/secrets/` only ## Related Notes - `issue-pal-e-platform-salt-phase-2b-gpg-secrets` — the issue this PR addresses - `plan-2026-02-26-salt-host-management` — Phase 2b - `lesson-salt-gpg-agent-config` — GPG agent configuration lesson discovered during this work
Migrate platform secrets from plaintext ~/secrets/ to GPG-encrypted
Salt pillar. Terraform now reads secrets via `make tofu-secrets` which
renders secrets.auto.tfvars from pillar, replacing manual k3s.tfvars.

- GPG keypair (RSA 4096, salt@pal-e.local) encrypts all pillar secrets
- 4 encrypted pillar files: platform, services, forgejo, sops (age key)
- Secret registry with rotation metadata and origin tracking
- Makefile tofu-secrets target renders secrets.auto.tfvars from pillar
- tofu-plan/tofu-apply auto-depend on tofu-secrets
- k3s.tfvars eliminated (kubeconfig_path defaulted in variables.tf)
- .gitignore updated: allow salt/pillar/secrets/ (GPG-encrypted, safe)
- minio_root_password discovered missing from secrets.env, added to pillar

Closes #3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add services.harbor_admin_password registry entry referencing
  platform entry as canonical (nit 1)
- Escape backslashes, double quotes, and newlines in Python tfvars
  renderer (nit 2)
- Add sudo pre-check to tofu-secrets for fast failure (nit 3)
- Remove value field from sops.age_public_key registry entry;
  update notes to reference sops.sls (nit 4)
- Replace list comprehension with for loop; extract Python script
  into Makefile define block for readability (nit 5)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

Review nits addressed (ff72890)

All 5 nits from the review have been fixed in a single commit:

  1. Registry gap -- Added services.harbor_admin_password entry referencing platform.harbor_admin_password as canonical, matching the existing YAML style.

  2. Python escape special chars -- Values rendered into secrets.auto.tfvars now escape backslashes, double quotes, and newlines via replace() chain before printing.

  3. sudo pre-check -- sudo -n true check added as first line of tofu-secrets recipe. Fails fast with a clear error message instead of silently hanging.

  4. Remove value field from age_public_key -- Removed the actual key value from the registry entry. Updated notes to: "Plaintext in sops.sls pillar. Shared freely for SOPS encryption."

  5. Replace list comprehension with for loop -- Extracted the inline Python into a Makefile define block (PILLAR_TO_TFVARS) for readability. Uses a proper for loop. Allowed variable names are now passed as sys.argv[1:] instead of inline Make variable expansion, which is cleaner.

## Review nits addressed (ff72890) All 5 nits from the review have been fixed in a single commit: 1. **Registry gap** -- Added `services.harbor_admin_password` entry referencing `platform.harbor_admin_password` as canonical, matching the existing YAML style. 2. **Python escape special chars** -- Values rendered into `secrets.auto.tfvars` now escape backslashes, double quotes, and newlines via `replace()` chain before printing. 3. **sudo pre-check** -- `sudo -n true` check added as first line of `tofu-secrets` recipe. Fails fast with a clear error message instead of silently hanging. 4. **Remove `value` field from `age_public_key`** -- Removed the actual key value from the registry entry. Updated `notes` to: "Plaintext in sops.sls pillar. Shared freely for SOPS encryption." 5. **Replace list comprehension with for loop** -- Extracted the inline Python into a Makefile `define` block (`PILLAR_TO_TFVARS`) for readability. Uses a proper `for` loop. Allowed variable names are now passed as `sys.argv[1:]` instead of inline Make variable expansion, which is cleaner.
- secrets_registry.sls: Replace escaped double quotes with single quotes
  in gpg.identity and backup_locations fields (cleaner YAML)
- Makefile: Remove 2>/dev/null from salt-call in tofu-secrets target
  so GPG decrypt failures and other real errors surface instead of
  being silently swallowed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

QA Review Round 2 — Nit fixes applied

Nit 1: Escaped quotes in secrets_registry.sls
Replaced escaped double quotes (\"...\") with single quotes ('...') in gpg.identity and gpg.backup_locations. Cleaner YAML, same result.

Nit 2: PR body secret count
Correction: test plan item should read "22 secrets" not "20". Actual count: 10 platform + 5 services + 5 forgejo + 2 sops = 22.

Nit 4: stderr suppression in Makefile
Removed 2>/dev/null from the sudo /opt/salt/salt-call line in the tofu-secrets target. Salt's stderr warnings won't corrupt the JSON output (stdout and stderr are separate pipes), and now real errors like GPG decrypt failures or minion-not-running will surface instead of being silently swallowed.

Commit: afb09a9

## QA Review Round 2 — Nit fixes applied **Nit 1: Escaped quotes in `secrets_registry.sls`** Replaced escaped double quotes (`\"...\"`) with single quotes (`'...'`) in `gpg.identity` and `gpg.backup_locations`. Cleaner YAML, same result. **Nit 2: PR body secret count** Correction: test plan item should read "22 secrets" not "20". Actual count: 10 platform + 5 services + 5 forgejo + 2 sops = 22. **Nit 4: stderr suppression in Makefile** Removed `2>/dev/null` from the `sudo /opt/salt/salt-call` line in the `tofu-secrets` target. Salt's stderr warnings won't corrupt the JSON output (stdout and stderr are separate pipes), and now real errors like GPG decrypt failures or minion-not-running will surface instead of being silently swallowed. Commit: `afb09a9`
forgejo_admin deleted branch 3-salt-phase-2b-gpg-encryption-secret-migr 2026-02-27 20:30:44 +00:00
Sign in to join this conversation.
No description provided.