disinto/vault/policies/validate.sh

225 lines
8 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# vault/policies/validate.sh — Validate Vault policy HCL files
#
# Usage: vault/policies/validate.sh [--check-exists]
#
# This script provides CI validation for Vault policy files:
# 1. `vault policy fmt -check` — ensures consistent formatting (non-destructive)
# 2. `vault policy validate` — syntax + semantic validation (requires Vault dev mode)
# 3. Optional: check that referenced policies exist in roles.yaml
#
# Exit codes:
# 0 — all checks pass
# 1 — formatting or validation error
# 2 — policy reference validation error (roles.yaml check)
#
# Environment:
# VAULT_ADDR — Vault server URL (defaults to http://127.0.0.1:8200 for dev mode)
# VAULT_TOKEN — Dev mode token (defaults to "root" for CI)
#
# CI usage:
# vault/policies/validate.sh
# vault/policies/validate.sh --check-exists # when roles.yaml exists
#
# Notes:
# - fmt -check is non-destructive; it only reports diff
# - validate requires a running Vault instance (dev mode is sufficient for CI)
# - Exit 2 is tolerated for advisory warnings (TLS-disabled listeners)
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROLES_YAML="${SCRIPT_DIR}/../roles.yaml"
VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:8200}"
VAULT_TOKEN="${VAULT_TOKEN:-root}"
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Validate Vault policy HCL files.
Options:
--check-exists Also validate that roles.yaml references exist policies
--help Show this help message
Environment:
VAULT_ADDR Vault server URL (default: http://127.0.0.1:8200)
VAULT_TOKEN Dev mode token (default: root)
Exit codes:
0 All checks pass
1 Formatting or validation error
2 Policy reference validation error (roles.yaml check)
EOF
exit 0
}
# ─────────────────────────────────────────────────────────────────────────────
# Check if Vault is available (dev mode is sufficient for CI)
# ─────────────────────────────────────────────────────────────────────────────
check_vault_available() {
local rc=0
# Try to fetch version; exit 2 is advisory (TLS warning), which is OK for CI
if ! vault status > /dev/null 2>&1; then
rc=$?
case "$rc" in
0|2) return 0 ;; # OK for CI
*) echo "vault/policies/validate.sh: Vault not available (exit $rc)" >&2; return 1 ;;
esac
fi
return 0
}
# ─────────────────────────────────────────────────────────────────────────────
# Step 1: vault policy fmt -check
# ─────────────────────────────────────────────────────────────────────────────
fmt_check() {
local failed=0
local hcl_files
hcl_files=$(find "$SCRIPT_DIR" -maxdepth 1 -name '*.hcl' -type f 2>/dev/null || true)
if [ -z "$hcl_files" ]; then
echo "vault/policies/validate.sh: no .hcl files found in $SCRIPT_DIR" >&2
return 0
fi
for f in $hcl_files; do
echo "fmt-check: $f"
if ! vault policy fmt -check "$f" > /dev/null 2>&1; then
echo " ERROR: file not formatted correctly" >&2
vault policy fmt -check "$f" 2>&1 | head -20 >&2 || true
failed=1
fi
done
return $failed
}
# ─────────────────────────────────────────────────────────────────────────────
# Step 2: vault policy validate (syntax + semantic)
# ─────────────────────────────────────────────────────────────────────────────
validate_syntax() {
local failed=0
local hcl_files
hcl_files=$(find "$SCRIPT_DIR" -maxdepth 1 -name '*.hcl' -type f 2>/dev/null || true)
if [ -z "$hcl_files" ]; then
return 0
fi
# Check Vault is available first
if ! check_vault_available; then
echo "vault/policies/validate.sh: skipping validation (Vault unavailable)" >&2
return 0
fi
for f in $hcl_files; do
echo "validate: $f"
local rc=0
if ! vault policy validate "$f" > /dev/null 2>&1; then
rc=$?
case "$rc" in
0) ;; # Should not happen, but be safe
1|2)
echo " ERROR: validation failed (exit $rc)" >&2
vault policy validate "$f" 2>&1 | head -20 >&2 || true
failed=1
;;
*)
echo " ERROR: unexpected exit code $rc" >&2
failed=1
;;
esac
fi
done
return $failed
}
# ─────────────────────────────────────────────────────────────────────────────
# Step 3: Check that roles.yaml references exist
# ─────────────────────────────────────────────────────────────────────────────
check_policy_references() {
if [ ! -f "$ROLES_YAML" ]; then
echo "vault/policies/validate.sh: roles.yaml not found, skipping reference check" >&2
return 0
fi
local failed=0
local policy_names
# Get list of policy names (basenames without .hcl)
policy_names=$(find "$SCRIPT_DIR" -maxdepth 1 -name '*.hcl' -type f -exec basename {} .hcl \; | sort)
# Extract policy names from roles.yaml using yq or grep+sed
local referenced_policies
if command -v yq > /dev/null 2>&1; then
# yq is available, use it
referenced_policies=$(yq -r '.roles[].policies[]?' "$ROLES_YAML" 2>/dev/null | sort -u || true)
else
# Fallback: grep for 'policies:' lines and extract values
referenced_policies=$(grep -E '^\s*policies:' "$ROLES_YAML" 2>/dev/null | \
sed -E 's/.*policies:\s*\[(.*)\].*/\1/' | \
tr ',' '\n' | \
sed 's/^[[:space:]]*"//;s/"[[:space:]]*$//' | \
sort -u || true)
fi
if [ -z "$referenced_policies" ]; then
echo "vault/policies/validate.sh: no policies referenced in roles.yaml" >&2
return 0
fi
for policy in $referenced_policies; do
if ! echo "$policy_names" | grep -q "^${policy}$"; then
echo "vault/policies/validate.sh: ERROR: policy '$policy' referenced in roles.yaml but not found" >&2
failed=1
fi
done
return $failed
}
# ─────────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────────
check_refs=0
while [ $# -gt 0 ]; do
case "$1" in
--check-exists)
check_refs=1
shift
;;
--help|-h)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
echo "vault/policies/validate.sh — validating policy HCL files"
echo " VAULT_ADDR: $VAULT_ADDR"
echo " roles.yaml: $ROLES_YAML (exists: $([ -f "$ROLES_YAML" ] && echo yes || echo no))"
echo ""
# Run fmt check
fmt_check || exit 1
# Run syntax validation
validate_syntax || exit 1
# Run reference check if requested
if [ "$check_refs" -eq 1 ]; then
check_policy_references || exit 2
fi
echo ""
echo "vault/policies/validate.sh: all checks passed"
exit 0