Gitops: Ensure Secret Files Are Encrypted Before Commits
Overview #
A Git pre-commit hook that automatically encrypts secret files before they’re committed prevents accidental exposure of sensitive data.
The Problem #
Manually encrypting secrets is error-prone:
- Forget to encrypt → secrets leak to Git history
- Committed secrets are permanent (even if deleted later)
- Removing from history requires force-pushing and invalidating secrets
The Solution #
Automate encryption with a pre-commit hook that:
- Detects staged files matching secret patterns
- Checks if they’re encrypted (looks for
sops:marker) - Auto-encrypts unencrypted files
- Re-stages the encrypted version
Implementation #
Save to .git/hooks/pre-commit:
#!/bin/bash
# Pre-commit hook: Auto-encrypt secrets based on .sops.yaml patterns
set -e
REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT"
SOPS_CONFIG=".sops.yaml"
if [ ! -f "$SOPS_CONFIG" ]; then
exit 0
fi
# Extract path_regex patterns from .sops.yaml
PATTERNS=$(grep -E '^\s+- path_regex:' "$SOPS_CONFIG" | \
sed -E 's/^\s+- path_regex:\s+//' | \
tr -d '"' | \
sed 's/\.\*/*/g' | \
sed 's/\\\././g' | \
sed 's/\$$//')
if [ -z "$PATTERNS" ]; then
exit 0
fi
# Get staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$STAGED_FILES" ]; then
exit 0
fi
# Find unencrypted files matching patterns
UNENCRYPTED=""
while IFS= read -r file; do
if [ ! -f "$file" ]; then
continue
fi
# Check if file matches any pattern
MATCHES=false
while IFS= read -r pattern; do
case "$file" in
$pattern)
MATCHES=true
break
;;
esac
done <<< "$PATTERNS"
if [ "$MATCHES" = true ] && ! grep -q "sops:" "$file" 2>/dev/null; then
UNENCRYPTED="$UNENCRYPTED$file"$'\n'
fi
done <<< "$STAGED_FILES"
# Remove trailing newline
UNENCRYPTED=$(echo "$UNENCRYPTED" | sed '/^$/d')
if [ -n "$UNENCRYPTED" ]; then
echo "🔒 SOPS: Auto-encrypting unencrypted secrets..."
while IFS= read -r file; do
if [ -n "$file" ]; then
echo " - $file"
if sops --encrypt --in-place "$file" 2>/dev/null; then
git add "$file" # Re-add encrypted version
else
echo " ⚠️ Failed to encrypt $file (skipping)"
fi
fi
done <<< "$UNENCRYPTED"
echo "✅ SOPS: Secrets encrypted and re-staged"
fi
exit 0
Make it executable:
chmod +x .git/hooks/pre-commit
How It Works #
Step 1: Configuration Detection #
Reads encryption rules from .sops.yaml:
creation_rules:
- path_regex: .*secret.*\.yaml$
age: age1nav5lwz...
encrypted_regex: ^(data|stringData)$
Step 2: Pattern Matching #
Converts SOPS regex patterns (.*secret.*\.yaml$) to shell patterns (*secret*.yaml)
Step 3: Encryption Check #
For each staged file matching the pattern:
- Checks if file contains
sops:metadata - If missing → file is unencrypted
Step 4: Auto-Encryption #
Runs sops --encrypt --in-place on unencrypted files and re-stages them
Example Output #
$ git commit -m "Add API keys"
🔒 SOPS: Auto-encrypting unencrypted secrets...
- config/api-keys-secret.yaml
- deployments/db-secret.yaml
✅ SOPS: Secrets encrypted and re-staged
[main abc123] Add API keys
2 files changed, 15 insertions(+)
Prerequisites #
- SOPS and age installed (see: Security: Encrypting Secret Files Locally with Age and SOPS)
.sops.yamlconfigured in repo root- Age key available at
~/.config/sops/age/keys.txt
Benefits #
- Zero-effort security: Developers don’t need to remember to encrypt
- No leaked secrets: Hook prevents unencrypted commits
- Clean history: Only encrypted secrets in Git
- Team-friendly: Works for everyone with the hook installed
Limitations #
- Only protects new commits (doesn’t fix history)
- Requires hook installed locally (not enforced by remote)
- Consider: Server-side hooks or CI checks for enforcement
Alternative: CI/CD Validation #
Add to CI pipeline for additional safety:
# Check for unencrypted secrets
unencrypted=$(find . -name '*secret*.yaml' -type f ! -exec grep -q 'sops:' {} \; -print)
if [ -n "$unencrypted" ]; then
echo "❌ Unencrypted secrets found: $unencrypted"
exit 1
fi
Conclusion #
Automating secret encryption eliminates human error and protects sensitive data before it enters version control. Combined with proper SOPS setup, it creates a robust, developer-friendly security workflow.