#!/usr/bin/env bash # Manage per-user age keys and sops recipients. # - Creates a new age keypair under creds/age/.key # - Updates .sops.yaml recipients list for creds/secrets.enc.env # - Can delete a user (removes key file and recipient entry) # # Usage: # ./scripts/manage-age-key.sh add # ./scripts/manage-age-key.sh remove # ./scripts/manage-age-key.sh list # # Notes: # - Keep private keys out of git; creds/age/ is git-ignored. # - After add/remove, re-encrypt secrets: sops --encrypt --in-place creds/secrets.enc.env set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" AGE_DIR="$ROOT_DIR/creds/age" SOPS_CONFIG="$ROOT_DIR/.sops.yaml" usage() { sed -n '1,12p' "${BASH_SOURCE[0]}" exit 1 } list_keys() { if [[ -d "$AGE_DIR" ]]; then find "$AGE_DIR" -maxdepth 1 -type f -name '*.key' -printf '%f\n' fi } ensure_age_dir() { mkdir -p "$AGE_DIR" } add_user() { local user="$1" ensure_age_dir local key_file="$AGE_DIR/${user}.key" if [[ -f "$key_file" ]]; then echo "Key already exists: $key_file" exit 1 fi echo "Generating age key for ${user} -> ${key_file}" age-keygen -o "$key_file" >/dev/null local pub pub=$(grep '^# public key:' "$key_file" | awk '{print $4}') echo "Public key: $pub" if [[ ! -f "$SOPS_CONFIG" ]]; then echo "sops config not found at $SOPS_CONFIG; skipping recipient update." echo "Add this recipient manually: $pub" exit 0 fi if grep -q "$pub" "$SOPS_CONFIG"; then echo "Recipient already present in $SOPS_CONFIG" else echo "Adding recipient to $SOPS_CONFIG" # Append under the first age key list. tmp=$(mktemp) awk -v pub="$pub" ' /^ *- age:/ { in_age=1; print; next } in_age && /^ *- age1/ { print " - " pub; in_age=0 } { print } ' "$SOPS_CONFIG" >"$tmp" mv "$tmp" "$SOPS_CONFIG" fi echo "Done. Re-encrypt secrets after updating recipients:" echo " sops --encrypt --in-place creds/secrets.enc.env" } remove_user() { local user="$1" local key_file="$AGE_DIR/${user}.key" if [[ -f "$key_file" ]]; then rm -f "$key_file" echo "Removed key $key_file" else echo "No key file for $user at $key_file" fi if [[ -f "$SOPS_CONFIG" ]]; then local pub pub=$(grep '^# public key:' "$key_file" 2>/dev/null | awk '{print $4}') if [[ -n "${pub:-}" ]]; then tmp=$(mktemp) awk -v pub="$pub" '!($0 ~ pub)' "$SOPS_CONFIG" >"$tmp" mv "$tmp" "$SOPS_CONFIG" echo "Removed recipient from $SOPS_CONFIG (if present)." fi fi echo "Re-encrypt secrets to drop removed recipients if needed:" echo " sops --encrypt --in-place creds/secrets.enc.env" } cmd="${1:-}" case "$cmd" in add) [[ $# -eq 2 ]] || usage add_user "$2" ;; remove|rm|delete) [[ $# -eq 2 ]] || usage remove_user "$2" ;; list|ls) list_keys ;; *) usage ;; esac