From f79e542149d7b035a6fd8817ad03402b2f7fe21a Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Wed, 27 May 2026 16:31:34 +0100 Subject: [PATCH] 1c/W2a: mount cc-ci-secrets as submodule at secrets/; cert+key now sops-decrypted to /var/lib/ci-certs/live - secrets/ is now the private cc-ci-secrets repo (submodule). defaultSopsFile path unchanged. - secrets.nix: add wildcard_cert/wildcard_key sops secrets -> path=/var/lib/ci-certs/live/*. - proxy.nix: cert is sops-from-git, not an operator file drop (reframed; FATAL guard kept as decrypt-path check). Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitmodules | 3 +++ modules/proxy.nix | 10 +++++++--- modules/secrets.nix | 17 +++++++++++++++-- secrets | 1 + secrets/secrets.yaml | 35 ----------------------------------- 5 files changed, 26 insertions(+), 40 deletions(-) create mode 100644 .gitmodules create mode 160000 secrets delete mode 100644 secrets/secrets.yaml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1111335 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "secrets"] + path = secrets + url = https://git.autonomic.zone/recipe-maintainers/cc-ci-secrets.git diff --git a/modules/proxy.nix b/modules/proxy.nix index 1d6f543..e280dc4 100644 --- a/modules/proxy.nix +++ b/modules/proxy.nix @@ -1,6 +1,8 @@ # Reverse proxy = the canonical Co-op Cloud `traefik` recipe, deployed via abra in -# wildcard / file-provider mode (operator's pre-issued cert as ssl_cert/ssl_key swarm secrets, +# wildcard / file-provider mode (wildcard cert as ssl_cert/ssl_key swarm secrets, # LETS_ENCRYPT_ENV empty => NO ACME, no DNS token). See DECISIONS.md "Proxy: real coop-cloud/traefik". +# Phase-1c: the cert at CERT_DIR is sops-decrypted from git (cc-ci-secrets) at activation +# (modules/secrets.nix wildcard_cert/wildcard_key), NOT an out-of-band operator file drop. # # Declared as an idempotent-RECONCILE systemd oneshot (like swarm-init): it inspects current # state and converges every activation/boot, self-healing drift (redeploys if the stack is gone, @@ -16,9 +18,11 @@ let CERT_DIR="/var/lib/ci-certs/live" ENV_FILE="$HOME/.abra/servers/default/$PROXY_DOMAIN.env" - # Fail visibly (failed unit) if the operator cert is missing — do NOT silently skip. + # Fail visibly (failed unit) if the cert is missing — do NOT silently skip. It is + # sops-decrypted from git (cc-ci-secrets) at activation; a miss here means the sops decrypt + # path is broken (e.g. age identity not present), which must surface, not be papered over. if [ ! -r "$CERT_DIR/fullchain.pem" ] || [ ! -r "$CERT_DIR/privkey.pem" ]; then - echo "FATAL: wildcard cert missing at $CERT_DIR (operator precondition)" >&2 + echo "FATAL: wildcard cert missing at $CERT_DIR (sops decrypt from cc-ci-secrets failed?)" >&2 exit 1 fi diff --git a/modules/secrets.nix b/modules/secrets.nix index 31236e4..0ebc24c 100644 --- a/modules/secrets.nix +++ b/modules/secrets.nix @@ -1,7 +1,8 @@ # sops-nix wiring (D6 infra secrets). cc-ci decrypts secrets at activation using its own # ed25519 SSH host key as the age identity (no separate key file to manage on the box). -# Encrypted material lives in ../secrets/*.yaml, committed and readable only by recipients -# listed in /.sops.yaml (host key + off-box master recovery key). +# Encrypted material lives in ../secrets/secrets.yaml — Phase-1c moved this into the private +# `cc-ci-secrets` repo, mounted here as a git SUBMODULE at ../secrets/ (so the path is unchanged). +# Readable only by the recipients in secrets/.sops.yaml (host key + off-box master recovery key). { config, ... }: { sops = { @@ -27,6 +28,18 @@ secrets.bridge_drone_token = { }; secrets.bridge_gitea_token = { }; + # Phase-1c C2: the wildcard TLS cert+key are now sops secrets (in cc-ci-secrets), decrypted at + # activation to /var/lib/ci-certs/live/{fullchain.pem,privkey.pem} — the exact path the traefik + # reconcile (modules/proxy.nix) already reads. Replaces the prior operator-drops-a-cert-file step. + secrets.wildcard_cert = { + path = "/var/lib/ci-certs/live/fullchain.pem"; + mode = "0444"; # leaf+intermediate chain — not secret + }; + secrets.wildcard_key = { + path = "/var/lib/ci-certs/live/privkey.pem"; + mode = "0400"; # private key — root only + }; + # EnvironmentFile for the host exec runner: DRONE_RPC_SECRET rendered from the sops secret. templates."drone-runner.env".content = '' DRONE_RPC_SECRET=${config.sops.placeholder.drone_rpc_secret} diff --git a/secrets b/secrets new file mode 160000 index 0000000..2312f1c --- /dev/null +++ b/secrets @@ -0,0 +1 @@ +Subproject commit 2312f1cc67a9569c2254181ba7d3cc0cd91c9244 diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml deleted file mode 100644 index c55a651..0000000 --- a/secrets/secrets.yaml +++ /dev/null @@ -1,35 +0,0 @@ -test_secret: ENC[AES256_GCM,data:VOxNiRyeSQQPKeF2PUK9AtezhzX+Hdm9ji5ZYm+gNd2NJ+wwXc67En8=,iv:Bn1oQeBN98E2/To1KRAw3wsLUF0/HsQFBm8s28L5aqo=,tag:KPS5Y+25Elf3alSF2H6npw==,type:str] -drone_rpc_secret: ENC[AES256_GCM,data:pE+6nTpFaclRAQDBQZfki5WiMTOC6NkhBMcAkEfma9QjfOyBx9BbfyCyAB3a0ICo8NZj6kMHyw78GbrowU6RAg==,iv:3YgoLsXEQh6bOVMyVpSGopZFTP/Kxi20QdajNX9heVI=,tag:SNhRwTVzkygxZ10bN1yHlQ==,type:str] -drone_gitea_client_secret: ENC[AES256_GCM,data:kBYptCmAdFmAuZNa1moK3o5faYrKFDr5KEjDHzfZOKyrz47awRX9LwNejyWlej1ybE8rvv075Yc=,iv:fESd6OYoHKjoZS4YBajon0dibt0BuHD2f/WqjbljmiY=,tag:f7skoA5fizTXjYGI3u6Uig==,type:str] -bridge_drone_token: ENC[AES256_GCM,data:5n7x6S9a/OIoq2AyPX4iKNDmoQsk+WT8za2/4qVhZFQ=,iv:r0fAs1cAj/YEOq6AGPsjJnsApYM8bMimdJ6e5zzKIw0=,tag:YMyt5FNR9yd0Mj/i+FmmFQ==,type:str] -bridge_gitea_token: ENC[AES256_GCM,data:zyGrnq36o2RfIBEGsCGFpOg/wJucNDviEUdaJjJkOXJkAdV0+CYZhw==,iv:y5G+cBu/Aaghn1ORcFT0wkj5ZJZ3UjzREmiES/dPosE=,tag:5KKANpOgf3+0rvspNCjYrg==,type:str] -bridge_webhook_hmac: ENC[AES256_GCM,data:0qD6BtZSoQx6pKUf8sz2Zcp656TKrzjNvb5zBtoGnLalg4lX7Nm715xtoaqdgAnXOEeJ5e8LfDgLIhwtxYHJxQ==,iv:hvXNzF1dXviGZae0hMhqn/pBwbv/LosHPl/BL6V3ZvU=,tag:B8V4HKqs/+gigQfTmBBCkw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1h90utdztfc23kx8ewrtrtk80mnddvrf8pg4ppej55rwwwupzhfvqhmp3qa - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoRzN4Q0Eramhkdk8xbmNn - NGRwclpaT3YvR24yemJxQjF1N001cVI3MXdjCk9wUE5QTE1UNmxTa1BWalk1V1hS - M1o2elpOVHNGcWdyWVI1RHdUZi96M0UKLS0tIGpaRDNNa1ZuRWkybitPTlM5ajVK - VjNldWh1Q3lTc2lCNlE4aGNmaVNOT1kKMhsp/z5LbEyAezDHTodL2vS3L/wlNOc0 - xiDLBYX1AJSiT8DOBvSMSZFj+ygsNL8GBYABjC0Ioar1PIK/KI00oA== - -----END AGE ENCRYPTED FILE----- - - recipient: age1cmk26t9e30ls8594s8txgmf2exenydmntfxqpcd3qdqm3ru2lpnqpdkdz9 - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwTk9uQ3JSeEFidEF6U2FU - Mjk1bVYvNTdYaDJjL0g1ZGlZdXkyR3VUREJrClV5S283elRFbXZkUXNiQkoyYWwr - ZUkxV1UyRE1xcW5DSTNVbEk1dXEyWmcKLS0tIFo5SUdhaVVqYUpZUGQxVGQvR2s0 - a2RKRWVaTGhNb0d3TFlnL2NtejZOaEUK2dQaAzlYk4Z7aBej77cO4Ug9Afkka6wg - G1SumwxX0wMocpgz4WhDUPkBC66uWlaR3u1AWzwpzRseuwAZ94gAxA== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-05-26T22:15:49Z" - mac: ENC[AES256_GCM,data:ulV/ounh9Qt6+ZEK5R6eqLi++VwLjGajgpCdl9q3CjNvNinXL19TMIrmezellsVt9njsVqgsKbYkwTAhYN0t7MHcS2ObNe6Gw0EeZBsABTu8fHuyopW4wxFC3I6O4TaEJm4ALOxuqCUFtZoRs5Cnrez4MhzVzZjFoqlVtYj3FCE=,iv:Y6TJTUaeU2rRKP9gd9vByqM2TfX0eZw15Gbz40RzY7M=,tag:f9e8LPbCymrYeL0kDCKzvA==,type:str] - pgp: [] - unencrypted_suffix: _unencrypted - version: 3.9.4