#!/bin/bash # verify-sops-isolation.sh — Verify SOPS zero trust key isolation # # Usage: bash dev/verify-sops-isolation.sh [--keys-dir PATH] # # Verifies that: # 1. dev-key can ONLY decrypt *.dev.enc.yaml # 2. prod-key can ONLY decrypt *.prod.enc.yaml # 3. test-key can decrypt *.test.enc.yaml (if any) # 4. test-key CANNOT decrypt dev or prod files # 5. mac_only_encrypted is set in all files # 6. All files decrypt successfully with appropriate keys # # Requires: sops, age keys in SOPS_AGE_KEY_DEV/PROD/TEST env vars # or provide --keys-dir with separate key files set -euo pipefail cd "$(dirname "$0")/.." RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' PASS=0 FAIL=0 WARN=0 check() { local desc=$1 expected=$2 actual=$3 if [ "$expected" = "$actual" ]; then echo -e " ${GREEN}✓${NC} $desc" PASS=$((PASS+1)) else echo -e " ${RED}✗${NC} $desc (expected=$expected, got=$actual)" FAIL=$((FAIL+1)) fi } ORIG_KEYS="" if [ -f ~/.config/sops/age/keys.txt ]; then ORIG_KEYS=$(cat ~/.config/sops/age/keys.txt) fi restore_keys() { if [ -n "$ORIG_KEYS" ]; then echo "$ORIG_KEYS" > ~/.config/sops/age/keys.txt fi } trap restore_keys EXIT # --- Check .sops.yaml --- echo -e "\n${YELLOW}[1] Checking .sops.yaml configuration${NC}" if [ -f .sops.yaml ]; then check ".sops.yaml exists" "yes" "yes" else check ".sops.yaml exists" "yes" "no" exit 1 fi MAC_RULES=$(grep -c '^\s*mac_only_encrypted: true' .sops.yaml || echo 0) check "mac_only_encrypted rules in .sops.yaml (>=4)" "yes" "$([ "$MAC_RULES" -ge 4 ] && echo yes || echo no)" # --- Check all encrypted files --- echo -e "\n${YELLOW}[2] Checking mac_only_encrypted in encrypted files${NC}" TOTAL_ENC=$(find . -name '*.enc.yaml' -not -path './.git/*' | wc -l) MAC_ENC=$(grep -rl 'mac_only_encrypted: true' $(find . -name '*.enc.yaml' -not -path './.git/*' 2>/dev/null) 2>/dev/null | wc -l) check "mac_only_encrypted in all encrypted files" "$TOTAL_ENC" "$MAC_ENC" # --- Key isolation tests --- echo -e "\n${YELLOW}[3] Key isolation: dev-key${NC}" if [ -n "${SOPS_AGE_KEY_DEV:-}" ]; then echo "$SOPS_AGE_KEY_DEV" > ~/.config/sops/age/keys.txt for f in $(find . -name '*.dev.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "dev-key decrypts $(basename $f)" "yes" "$result" done for f in $(find . -name '*.prod.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "dev-key CANNOT decrypt $(basename $f)" "no" "$result" done else echo -e " ${YELLOW}⚠ SOPS_AGE_KEY_DEV not set, skipping${NC}" WARN=$((WARN+1)) fi echo -e "\n${YELLOW}[4] Key isolation: prod-key${NC}" if [ -n "${SOPS_AGE_KEY_PROD:-}" ]; then echo "$SOPS_AGE_KEY_PROD" > ~/.config/sops/age/keys.txt for f in $(find . -name '*.prod.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "prod-key decrypts $(basename $f)" "yes" "$result" done for f in $(find . -name '*.dev.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "prod-key CANNOT decrypt $(basename $f)" "no" "$result" done else echo -e " ${YELLOW}⚠ SOPS_AGE_KEY_PROD not set, skipping${NC}" WARN=$((WARN+1)) fi echo -e "\n${YELLOW}[5] Key isolation: test-key${NC}" if [ -n "${SOPS_AGE_KEY_TEST:-}" ]; then echo "$SOPS_AGE_KEY_TEST" > ~/.config/sops/age/keys.txt for f in $(find . -name '*.dev.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "test-key CANNOT decrypt $(basename $f)" "no" "$result" done for f in $(find . -name '*.prod.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "test-key CANNOT decrypt $(basename $f)" "no" "$result" done for f in $(find . -name '*.test.enc.yaml' -not -path './.git/*'); do result=$(sops decrypt "$f" > /dev/null 2>&1 && echo "yes" || echo "no") check "test-key decrypts $(basename $f)" "yes" "$result" done else echo -e " ${YELLOW}⚠ SOPS_AGE_KEY_TEST not set, skipping${NC}" WARN=$((WARN+1)) fi # --- Summary --- echo -e "\n=== Summary ===" echo -e "${GREEN}Passed: $PASS${NC} ${RED}Failed: $FAIL${NC} ${YELLOW}Warnings: $WARN${NC}" [ $FAIL -eq 0 ] && echo -e "${GREEN}All checks passed!${NC}" || echo -e "${RED}Some checks failed!${NC}" exit $FAIL