Initial commit: VaultMesh Skills collection
Collection of operational skills for VaultMesh infrastructure including: - backup-sovereign: Backup and recovery operations - btc-anchor: Bitcoin anchoring - cloudflare-tunnel-manager: Cloudflare tunnel management - container-registry: Container registry operations - disaster-recovery: Disaster recovery procedures - dns-sovereign: DNS management - eth-anchor: Ethereum anchoring - gitea-bootstrap: Gitea setup and configuration - hetzner-bootstrap: Hetzner server provisioning - merkle-forest: Merkle tree operations - node-hardening: Node security hardening - operator-bootstrap: Operator initialization - proof-verifier: Cryptographic proof verification - rfc3161-anchor: RFC3161 timestamping - secrets-vault: Secrets management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
130
secrets-vault/scripts/11_apply.sh
Normal file
130
secrets-vault/scripts/11_apply.sh
Normal file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${VAULT_ROOT:=~/infrastructure/vault}"
|
||||
: "${AGE_KEY_DIR:=~/.config/sops/age}"
|
||||
: "${AGE_KEYS_FILE:=~/.config/sops/age/keys.txt}"
|
||||
: "${RECIPIENTS_FILE:=$SKILL_ROOT/outputs/recipients.txt}"
|
||||
: "${BACKUP_DIR:=$SKILL_ROOT/outputs/backups}"
|
||||
|
||||
backup_file() {
|
||||
local f="$1"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
if [[ -f "$f" ]]; then
|
||||
ts="$(date -Iseconds | tr ':' '-')"
|
||||
cp -p "$f" "$BACKUP_DIR/$(basename "$f").${ts}.bak"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
|
||||
mkdir -p "$SKILL_ROOT/outputs" "$BACKUP_DIR"
|
||||
vr="$(expand_path "$VAULT_ROOT")"
|
||||
kd="$(expand_path "$AGE_KEY_DIR")"
|
||||
kf="$(expand_path "$AGE_KEYS_FILE")"
|
||||
|
||||
mkdir -p "$vr/secrets"
|
||||
mkdir -p "$kd"
|
||||
|
||||
# 1) ensure age identity exists
|
||||
if [[ ! -f "$kf" ]]; then
|
||||
log_info "Generating age identity: $kf"
|
||||
age-keygen -o "$kf" >/dev/null
|
||||
chmod 600 "$kf"
|
||||
else
|
||||
log_info "Using existing age identity: $kf"
|
||||
chmod 600 "$kf" || true
|
||||
fi
|
||||
|
||||
# extract recipient (public key)
|
||||
recipient="$(grep -E '^# public key: ' "$kf" | head -n1 | sed 's/^# public key: //')"
|
||||
[[ -n "$recipient" ]] || die "Could not parse public key from $kf"
|
||||
|
||||
echo "$recipient" > "$RECIPIENTS_FILE"
|
||||
|
||||
# 2) write .sops.yaml policy
|
||||
policy="$vr/.sops.yaml"
|
||||
backup_file "$policy"
|
||||
cat > "$policy" <<EOF
|
||||
creation_rules:
|
||||
- path_regex: secrets/.*\\.enc\\.(yaml|yml|json|env)$
|
||||
encrypted_regex: '^(data|stringData|secrets|values)$'
|
||||
age: ["$recipient"]
|
||||
EOF
|
||||
|
||||
# 3) scaffold encrypted templates
|
||||
# plaintext templates are generated into a temp file then immediately encrypted and removed.
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
# cloudflare
|
||||
cat > "$tmpdir/cloudflare.yaml" <<EOF
|
||||
data:
|
||||
account_id: "REPLACE_ME"
|
||||
tunnel_token: "REPLACE_ME"
|
||||
api_token: "REPLACE_ME"
|
||||
EOF
|
||||
sops --encrypt --age "$recipient" "$tmpdir/cloudflare.yaml" > "$vr/secrets/cloudflare.enc.yaml"
|
||||
|
||||
# gitea
|
||||
cat > "$tmpdir/gitea.yaml" <<EOF
|
||||
data:
|
||||
admin_username: "REPLACE_ME"
|
||||
admin_password: "REPLACE_ME"
|
||||
admin_email: "REPLACE_ME"
|
||||
secret_key: "REPLACE_ME"
|
||||
EOF
|
||||
sops --encrypt --age "$recipient" "$tmpdir/gitea.yaml" > "$vr/secrets/gitea.enc.yaml"
|
||||
|
||||
# registry
|
||||
cat > "$tmpdir/registry.yaml" <<EOF
|
||||
data:
|
||||
htpasswd: "REPLACE_ME"
|
||||
tls_cert_pem: "REPLACE_ME"
|
||||
tls_key_pem: "REPLACE_ME"
|
||||
EOF
|
||||
sops --encrypt --age "$recipient" "$tmpdir/registry.yaml" > "$vr/secrets/registry.enc.yaml"
|
||||
|
||||
# k8s
|
||||
cat > "$tmpdir/k8s.yaml" <<EOF
|
||||
data:
|
||||
kubeconfig: "REPLACE_ME"
|
||||
cluster_token: "REPLACE_ME"
|
||||
EOF
|
||||
sops --encrypt --age "$recipient" "$tmpdir/k8s.yaml" > "$vr/secrets/k8s.enc.yaml"
|
||||
|
||||
# 4) vault README
|
||||
readme="$vr/README.md"
|
||||
backup_file "$readme"
|
||||
cat > "$readme" <<EOF
|
||||
# Vault (age + sops)
|
||||
|
||||
This folder stores **encrypted secrets** for GitOps.
|
||||
|
||||
## Edit a secret
|
||||
\`\`\`bash
|
||||
sops secrets/cloudflare.enc.yaml
|
||||
\`\`\`
|
||||
|
||||
## Decrypt to stdout (careful)
|
||||
\`\`\`bash
|
||||
sops -d secrets/cloudflare.enc.yaml
|
||||
\`\`\`
|
||||
|
||||
## Policy
|
||||
See \`.sops.yaml\`. Only files matching \`secrets/*.enc.(yaml|json|env)\` are covered.
|
||||
|
||||
## Key location
|
||||
- age identity: \`$kf\` (0600)
|
||||
- public recipient: stored in \`outputs/recipients.txt\` by the skill
|
||||
EOF
|
||||
|
||||
echo "$vr" > "$SKILL_ROOT/outputs/vault_root.txt"
|
||||
log_info "Vault forged at: $vr"
|
||||
log_info "Recipient: $recipient"
|
||||
}
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user