#!/usr/bin/env bash # vault/validate-roles.sh — Validate roles.yaml for Vault workload identity # # Usage: vault/validate-roles.sh # # This script validates the roles.yaml file for Nomad workload identity: # 1. yamllint check — ensures YAML syntax is valid # 2. Policy reference check — each role references a policy that exists # 3. Required fields check — each role has required fields (name, policies, auth) # # Exit codes: # 0 — all checks pass # 1 — YAML syntax or validation error # # Environment: # VAULT_POLICY_DIR — Directory containing policy HCL files (default: vault/policies/) # # CI usage: # vault/validate-roles.sh # # Notes: # - Requires yamllint to be installed # - Policy existence check requires Vault policy files to exist # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROLES_YAML="${SCRIPT_DIR}/roles.yaml" VAULT_POLICY_DIR="${VAULT_POLICY_DIR:-${SCRIPT_DIR}/policies}" usage() { cat < /dev/null 2>&1; then echo "validate-roles.sh: yamllint not found in PATH" >&2 echo " Install with: pip install yamllint" >&2 exit 1 fi } # ───────────────────────────────────────────────────────────────────────────── # Step 1: yamllint check # ───────────────────────────────────────────────────────────────────────────── yamllint_check() { echo "yamllint: $ROLES_YAML" if ! yamllint -q "$ROLES_YAML" 2>&1; then echo " ERROR: yamllint found issues" >&2 return 1 fi echo " OK" return 0 } # ───────────────────────────────────────────────────────────────────────────── # Step 2: Extract policy names from roles.yaml # ───────────────────────────────────────────────────────────────────────────── get_referenced_policies() { if command -v yq > /dev/null 2>&1; then # yq is available, use it yq -r '.roles[].policies[]?' "$ROLES_YAML" 2>/dev/null | sort -u || true else # Fallback: grep for 'policies:' lines and extract values grep -E '^\s*policies:' "$ROLES_YAML" 2>/dev/null | \ sed -E 's/.*policies:\s*\[(.*)\].*/\1/' | \ tr ',' '\n' | \ sed 's/^[[:space:]]*"//;s/"[[:space:]]*$//' | \ grep -v '^$' | \ sort -u || true fi } # ───────────────────────────────────────────────────────────────────────────── # Step 3: Check that referenced policies exist # ───────────────────────────────────────────────────────────────────────────── check_policy_references() { local failed=0 if [ ! -d "$VAULT_POLICY_DIR" ]; then echo "validate-roles.sh: policy directory not found: $VAULT_POLICY_DIR" >&2 return 1 fi # Get list of policy names (basenames without .hcl) local policy_names policy_names=$(find "$VAULT_POLICY_DIR" -maxdepth 1 -name '*.hcl' -type f -exec basename {} .hcl \; | sort) if [ -z "$policy_names" ]; then echo "validate-roles.sh: no .hcl files found in $VAULT_POLICY_DIR" >&2 return 1 fi local referenced_policies referenced_policies=$(get_referenced_policies) if [ -z "$referenced_policies" ]; then echo "validate-roles.sh: no policies referenced in roles.yaml" >&2 return 1 fi for policy in $referenced_policies; do if ! echo "$policy_names" | grep -q "^${policy}$"; then echo "validate-roles.sh: ERROR: policy '$policy' referenced in roles.yaml but not found" >&2 failed=1 fi done return $failed } # ───────────────────────────────────────────────────────────────────────────── # Step 4: Check required fields in roles # ───────────────────────────────────────────────────────────────────────────── check_required_fields() { local failed=0 if command -v yq > /dev/null 2>&1; then # Check each role has required fields local roles_count roles_count=$(yq '.roles | length' "$ROLES_YAML" 2>/dev/null || echo "0") if [ "$roles_count" -eq 0 ]; then echo "validate-roles.sh: WARNING: no roles defined in roles.yaml" >&2 return 0 fi for ((i=0; i\"" "$ROLES_YAML" 2>/dev/null || echo "") # Check for name field if [ "$role_name" = "" ]; then echo "validate-roles.sh: ERROR: role missing 'name' field" >&2 failed=1 fi # Check for policies field local policies_count policies_count=$(yq ".roles[$i].policies | length" "$ROLES_YAML" 2>/dev/null || echo "0") if [ "$policies_count" -eq 0 ]; then echo "validate-roles.sh: ERROR: role '$role_name' has no policies defined" >&2 failed=1 fi # Check for auth field (JWT auth config) local auth_method auth_method=$(yq -r ".roles[$i].auth // \"\"" "$ROLES_YAML" 2>/dev/null || echo "") if [ "$auth_method" = "" ]; then echo "validate-roles.sh: ERROR: role '$role_name' has no auth configuration" >&2 failed=1 fi done fi return $failed } # ───────────────────────────────────────────────────────────────────────────── # Main # ───────────────────────────────────────────────────────────────────────────── # Parse arguments while [ $# -gt 0 ]; do case "$1" in --help|-h) usage ;; *) echo "Unknown option: $1" >&2 usage ;; esac done echo "vault/validate-roles.sh — validating roles.yaml" echo " roles.yaml: $ROLES_YAML (exists: $([ -f "$ROLES_YAML" ] && echo yes || echo no))" echo " policy dir: $VAULT_POLICY_DIR" echo "" # Exit early if roles.yaml doesn't exist (it will be created in a future step) if [ ! -f "$ROLES_YAML" ]; then echo "vault/validate-roles.sh: roles.yaml not found, skipping validation" >&2 exit 0 fi # Check dependencies check_dependencies # Run validations yamllint_check || exit 1 check_policy_references || exit 1 check_required_fields || exit 1 echo "" echo "vault/validate-roles.sh: all checks passed" exit 0