Phase 1 (author-only): Flux + SOPS bootstrap + interface wiring (T-1.4)
bootstrap/sops (age-keygen refusing in-repo, .sops.yaml, install-age-key.sh glue installing the sops-age Secret) + bootstrap/flux (bootstrap.sh: flux install -> apply -k clusters/dev; decryption/url patches). PM wiring: place .sops.yaml at repo root (SOPS walks to root); inline the 3 flux patches into clusters/dev (avoids kustomize ../ load-restriction). clusters/dev builds with decryption enabled; full CI gate PASS. No key material; nothing executed. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
1 parent
0e901f135c
commit
84a2d762f0
12 files changed
+573
-4
No files matched your search
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# age-keygen.sh — generate the SOPS age keypair for the platform.
|
||||
#
|
||||
# Phase 1, T-1.4 (ADR-0007 SOPS+age, R-SEC-01). AUTHORED, NOT RUN.
|
||||
# =============================================================================
|
||||
# *** THE OPERATOR RUNS THIS, ONCE, AT BOOTSTRAP. AGENTS MUST NOT RUN IT. ***
|
||||
# =============================================================================
|
||||
#
|
||||
# What it does:
|
||||
# - Generates an age keypair to a path OUTSIDE this git repo (default:
|
||||
# ~/.config/sops/age/keys.txt — SOPS's conventional location, honoured by the
|
||||
# SOPS_AGE_KEY_FILE default lookup).
|
||||
# - Prints ONLY the PUBLIC recipient (age1...) to stdout. The public recipient
|
||||
# is safe to paste into bootstrap/sops/.sops.yaml. The PRIVATE key stays in
|
||||
# the file and is NEVER printed, NEVER committed (see .gitignore: age.key,
|
||||
# *.key). Spec docs/07 + AGENTS.md §5: no secret material in logs/PR/output.
|
||||
#
|
||||
# Hard guarantees:
|
||||
# - REFUSES to write anywhere inside this repository (private key must never
|
||||
# land where git could track it).
|
||||
# - REFUSES to overwrite an existing key file (rotation is deliberate — see
|
||||
# age-key-secret.md "Rotation").
|
||||
#
|
||||
# Next step after running this: install the private key into the cluster as the
|
||||
# `sops-age` Secret — see age-key-secret.md. Then paste the printed recipient
|
||||
# into .sops.yaml.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# --- resolve target path (override with AGE_KEY_FILE) -------------------------
|
||||
KEY_FILE="${AGE_KEY_FILE:-${HOME}/.config/sops/age/keys.txt}"
|
||||
|
||||
# --- refuse to write inside the repo -----------------------------------------
|
||||
# Repo root = git toplevel if available, else two levels up from this script
|
||||
# (bootstrap/sops/ -> repo root).
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel 2>/dev/null)"; then
|
||||
:
|
||||
else
|
||||
repo_root="$(cd "${script_dir}/../.." && pwd)"
|
||||
fi
|
||||
|
||||
abspath() { # portable realpath for a possibly-not-yet-existing path
|
||||
local p="$1"
|
||||
local d b
|
||||
d="$(dirname "$p")"
|
||||
b="$(basename "$p")"
|
||||
mkdir -p "$d"
|
||||
printf '%s/%s\n' "$(cd "$d" && pwd)" "$b"
|
||||
}
|
||||
key_abs="$(abspath "${KEY_FILE}")"
|
||||
|
||||
case "${key_abs}/" in
|
||||
"${repo_root}/"*)
|
||||
echo "ERROR: refusing to write the age private key inside the repo:" >&2
|
||||
echo " ${key_abs}" >&2
|
||||
echo " repo root: ${repo_root}" >&2
|
||||
echo " Set AGE_KEY_FILE to a path OUTSIDE the repo (default is fine)." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- refuse to clobber an existing key ---------------------------------------
|
||||
if [[ -e "${key_abs}" ]]; then
|
||||
echo "ERROR: ${key_abs} already exists — refusing to overwrite." >&2
|
||||
echo " Rotating? Move the old key aside deliberately (see age-key-secret.md)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v age-keygen >/dev/null 2>&1 || {
|
||||
echo "ERROR: age-keygen not found. Use the pinned dev container" >&2
|
||||
echo " (infra/devcontainer, AGE_VERSION in its versions.env)." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- generate ----------------------------------------------------------------
|
||||
umask 077 # 0600 key file
|
||||
age-keygen -o "${key_abs}" >/dev/null
|
||||
|
||||
echo "age private key written (0600) to: ${key_abs}"
|
||||
echo "This file is OUTSIDE the repo and must stay that way — do NOT commit it."
|
||||
echo
|
||||
echo "Public recipient (safe to share — paste into bootstrap/sops/.sops.yaml):"
|
||||
age-keygen -y "${key_abs}" # prints ONLY the public recipient (age1...)
|
||||
Reference in new issue
Block a user