c77b82d835
- Disable Redis RDB snapshots (--save "") — Redis is cache-only for Nextcloud; snapshot writes failed due to directory permissions and blocked all Redis writes, causing background jobs to crash-loop at 400%+ CPU - Add passlib as a required local dependency (needed for password_hash filter in Ansible templates) - Make dl role SFTP setup conditional on dl_sftp_authorized_keys being set in the vault - Fix check-vault.sh stdin conflict (pipe + heredoc) by passing vault content via environment variable - Remove dl_sftp_authorized_keys from required_scalars (it is optional)
99 lines
3.1 KiB
Bash
Executable File
99 lines
3.1 KiB
Bash
Executable File
#!/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",
|
|
]
|
|
|
|
# Required non-empty lists (must contain at least one entry)
|
|
required_lists = [
|
|
"traefik_dashboard_users",
|
|
"registry_users",
|
|
"dovecot_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
|