Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
122 lines
3.6 KiB
Bash
Executable File
122 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Create a new age-encrypted secret with auto-determined recipients
|
|
# Usage: ./create-secret.sh <path> [content]
|
|
# path: relative to secrets/ (e.g., "usda-dash/my-secret.age" or "global/shared.age")
|
|
# content: stdin if not provided
|
|
|
|
SECRETS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
if [ $# -lt 1 ]; then
|
|
echo "Usage: $0 <path> [content]" >&2
|
|
echo "Examples:" >&2
|
|
echo " $0 usda-dash/database-url.age <<< 'postgresql://...'" >&2
|
|
echo " $0 global/api-key.age < secret-file.txt" >&2
|
|
echo " echo 'secret' | $0 nix-builder/token.age" >&2
|
|
exit 1
|
|
fi
|
|
|
|
SECRET_PATH="$1"
|
|
shift
|
|
|
|
# Extract directory from path (e.g., "usda-dash/file.age" -> "usda-dash")
|
|
SECRET_DIR="$(dirname "$SECRET_PATH")"
|
|
SECRET_FILE="$(basename "$SECRET_PATH")"
|
|
|
|
# Ensure .age extension
|
|
if [[ ! "$SECRET_FILE" =~ \.age$ ]]; then
|
|
echo "Error: Secret file must have .age extension" >&2
|
|
exit 1
|
|
fi
|
|
|
|
TARGET_FILE="$SECRETS_DIR/$SECRET_PATH"
|
|
|
|
# Ensure target directory exists
|
|
mkdir -p "$(dirname "$TARGET_FILE")"
|
|
|
|
# Collect recipient keys
|
|
RECIPIENTS=()
|
|
|
|
if [ "$SECRET_DIR" = "global" ]; then
|
|
echo "Creating global secret (encrypted for all hosts + admins)..." >&2
|
|
|
|
# Add all host keys
|
|
for host_dir in "$SECRETS_DIR"/*/; do
|
|
host_name="$(basename "$host_dir")"
|
|
# Skip non-host directories
|
|
if [ "$host_name" = "admins" ] || [ "$host_name" = "global" ]; then
|
|
continue
|
|
fi
|
|
|
|
# Add all .age.pub files from this host
|
|
while IFS= read -r -d '' key_file; do
|
|
RECIPIENTS+=("$key_file")
|
|
done < <(find "$host_dir" -maxdepth 1 -name "*.age.pub" -print0)
|
|
done
|
|
|
|
# Add global keys
|
|
while IFS= read -r -d '' key_file; do
|
|
RECIPIENTS+=("$key_file")
|
|
done < <(find "$SECRETS_DIR/global" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
|
|
|
|
else
|
|
echo "Creating host-specific secret for $SECRET_DIR..." >&2
|
|
|
|
# Check if host directory exists
|
|
if [ ! -d "$SECRETS_DIR/$SECRET_DIR" ]; then
|
|
echo "Error: Host directory $SECRET_DIR does not exist" >&2
|
|
echo "Create it first: mkdir -p secrets/$SECRET_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Add this host's keys
|
|
while IFS= read -r -d '' key_file; do
|
|
RECIPIENTS+=("$key_file")
|
|
done < <(find "$SECRETS_DIR/$SECRET_DIR" -maxdepth 1 -name "*.age.pub" -print0)
|
|
|
|
# Add global keys (so global hosts can also decrypt)
|
|
while IFS= read -r -d '' key_file; do
|
|
RECIPIENTS+=("$key_file")
|
|
done < <(find "$SECRETS_DIR/global" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
|
|
fi
|
|
|
|
# Add admin keys (for editing from workstations)
|
|
if [ -d "$SECRETS_DIR/admins" ]; then
|
|
while IFS= read -r -d '' key_file; do
|
|
RECIPIENTS+=("$key_file")
|
|
done < <(find "$SECRETS_DIR/admins" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
|
|
fi
|
|
|
|
# Check if we have any recipients
|
|
if [ ${#RECIPIENTS[@]} -eq 0 ]; then
|
|
echo "Error: No recipient keys found!" >&2
|
|
echo "Run ./update-age-keys.sh first to generate .age.pub files" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Found ${#RECIPIENTS[@]} recipient key(s):" >&2
|
|
for key in "${RECIPIENTS[@]}"; do
|
|
echo " - $(basename "$key")" >&2
|
|
done
|
|
|
|
# Create recipient list file (temporary)
|
|
RECIPIENT_LIST=$(mktemp)
|
|
trap "rm -f $RECIPIENT_LIST" EXIT
|
|
|
|
for key in "${RECIPIENTS[@]}"; do
|
|
cat "$key" >> "$RECIPIENT_LIST"
|
|
done
|
|
|
|
# Encrypt the secret
|
|
if [ $# -gt 0 ]; then
|
|
# Content provided as argument
|
|
echo "$@" | age -R "$RECIPIENT_LIST" -o "$TARGET_FILE"
|
|
else
|
|
# Content from stdin
|
|
age -R "$RECIPIENT_LIST" -o "$TARGET_FILE"
|
|
fi
|
|
|
|
echo "✓ Created $TARGET_FILE" >&2
|
|
echo " Edit with: ragenix -e secrets/$SECRET_PATH" >&2
|