#!/usr/bin/env bash # Decrypts ansible/group_vars/vault.yml and checks all required secrets are present # and non-empty, and validates optional secrets when they are present. # Exits non-zero if any check fails. # Usage: bash scripts/check-vault.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" VAULT_FILE="${REPO_ROOT}/ansible/group_vars/all/vault.yml" if [ ! -f "${VAULT_FILE}" ]; then echo "ERROR: vault file not found at ${VAULT_FILE}" >&2 echo " Create it with: ansible-vault create ansible/group_vars/all/vault.yml" >&2 exit 1 fi if ! command -v ansible-vault &>/dev/null; then echo "ERROR: ansible-vault not found — install with: pip install ansible" >&2 exit 1 fi echo "==> Decrypting vault (you will be prompted for the vault password)" VAULT_CONTENT=$(ansible-vault view "${VAULT_FILE}") # Pass vault content via environment variable — avoids a pipe+heredoc stdin conflict # where python3 - consumes stdin for the script, leaving sys.stdin.read() empty. export VAULT_CONTENT python3 - <<'PYEOF' import os, sys, yaml data = yaml.safe_load(os.environ['VAULT_CONTENT']) # Required non-empty scalar strings required_scalars = [ "gitea_db_password", "gitea_secret_key", "gitea_internal_token", "keycloak_db_password", "keycloak_admin_password", "nextcloud_db_password", "nextcloud_admin_password", "roundcube_db_password", "roundcube_des_key", "vaultwarden_admin_token", "anthropic_api_key", ] # Required non-empty lists (must contain at least one entry) required_lists = [ "traefik_dashboard_users", "registry_users", "dovecot_users", "clay_users", ] # Optional scalars: validated only when present — (key, validator_fn, hint) SSH_KEY_PREFIXES = ("ssh-rsa", "ssh-ed25519", "ssh-ecdsa", "ecdsa-sha2-", "sk-ssh-") def is_nonempty(val): return bool(str(val).strip()) def is_ssh_pubkey(val): return any(str(val).strip().startswith(p) for p in SSH_KEY_PREFIXES) optional_scalars = [ ("vaultwarden_sso_client_secret", is_nonempty, "must be a non-empty string"), ("dl_sftp_authorized_keys", is_ssh_pubkey, "must be a valid SSH public key (ssh-ed25519 / ssh-rsa / ecdsa-sha2-*)"), ] errors = [] for key in required_scalars: val = data.get(key, "") if not val or str(val).strip() in ("", '""', "''"): errors.append(f"{key}: missing or empty (required)") for key in required_lists: val = data.get(key) if not isinstance(val, list) or len(val) == 0: errors.append(f"{key}: missing or empty list (required)") for key, validator, hint in optional_scalars: if key in data: val = data[key] if not val or not validator(str(val).strip()): errors.append(f"{key}: present but invalid — {hint}") if errors: print("", file=sys.stderr) print("ERROR: vault.yml has the following issues:", file=sys.stderr) for msg in errors: print(f" - {msg}", file=sys.stderr) print("", file=sys.stderr) print("Edit the vault with: ansible-vault edit ansible/group_vars/all/vault.yml", file=sys.stderr) sys.exit(1) print(" OK — all secrets are present and valid") PYEOF