Hook up SOPS with Git
Why use a Git hook?
Section titled “Why use a Git hook?”Git hooks are scripts that run automatically at certain points in the Git workflow. By using a Git hook, you can ensure that sensitive files are encrypted before they are committed to the repository. This adds an extra layer of security and helps prevent accidental exposure of sensitive data. To set up a Git hook for SOPS, we will use pre-commit, a framework for managing and maintaining multi-language pre-commit hooks.
Install pre-commit
Section titled “Install pre-commit”To use pre-commit, you need to install it on your system. You can install it using pip
or Homebrew
, depending on your operating system.
pip install pre-commit
brew install pre-commit
Add a pre-commit configuration
Section titled “Add a pre-commit configuration”This configuration uses a custom script to encrypt files with SOPS before committing. The script will look for any files that match the pattern *.tfvars
or *.tfstate
and encrypt them using SOPS.
Create a .pre-commit-config.yaml
file in the root of your repository with the following content:
repos: - repo: local hooks: - id: sops-encrypt name: Encrypt secrets to .enc files entry: .pre-commit-sops.sh language: script pass_filenames: false # prevents empty list from breaking things always_run: true # force it to run even if no files match exclude: "" # override exclusions so it runs every time
Create the custom pre-commit script
Section titled “Create the custom pre-commit script”Create a script named .pre-commit-sops.sh
in the root of your repository with the following content:
#!/bin/bashset -euo pipefail
# Array of file extensions to checkFILE_EXTENTIONS=( "*.tfvars" "*.tfvars.json" "*.tfstate" "*.tfstate.backup" ".env")
# Find all secret files (even if ignored), excluding already encryptedsecret_files=$(find . \ -type f \ \( -name "${FILE_EXTENTIONS[0]}" \ -o -name "${FILE_EXTENTIONS[1]}" \ -o -name "${FILE_EXTENTIONS[2]}" \ -o -name "${FILE_EXTENTIONS[3]}" \ -o -name "${FILE_EXTENTIONS[4]}" \) \ ! -name "*.enc")
# Track encrypted files to add after loopenc_files_to_add=()
for filename in $secret_files; do enc_file="${filename}.enc"
# Skip if already encrypted version exists [[ -f "$enc_file" ]] && continue
# Skip if file already contains SOPS metadata grep -q 'sops:' "$filename" && continue
# Encrypt and store output sops --encrypt "$filename" > "$enc_file" enc_files_to_add+=("$enc_file")done
# Stage the new encrypted filesif [ ${#enc_files_to_add[@]} -gt 0 ]; then git add "${enc_files_to_add[@]}"fi
Create the sops configuration file
Section titled “Create the sops configuration file”Create a .sops.yaml
file in the root of your repository with the following content:
creation_rules: # Encrypt .tfvars files (macOS only, used for provisioning) - path_regex: \.tfvars(\.json)?$ encrypted_regex: "^(.*)$" age: "age1xxxx..." # macOS
# Encrypt Terraform state and backups (macOS only) - path_regex: \.tfstate(\.backup)?$ encrypted_regex: "^(.*)$" age: "age1xxxx..." # macOS
# Encrypt .env files for Docker (used by NixOS) - path_regex: \.env$ encrypted_regex: "^(.*)$" age: age1xxxx...,age1yyyy...
Install the git hook scripts
Section titled “Install the git hook scripts”Run the following command in the root of your repository to install the pre-commit hooks:
pre-commit install
This command will set up the pre-commit hooks in your .git/hooks
directory. Now pre-commit will run automatically before each commit and encrypt any files that match the specified patterns.
Test the setup
Section titled “Test the setup”Create a new file with the
.tfvars.json
extension, for exampletest.tfvars.json
, and add some sensitive data to it.test.tfvars.json {"pm_api_url": "https://ip:port/api2/json","pm_api_token_id": "token","pm_api_token_secret": "secret"}Run the following to stage the file:
Terminal window git add test.tfvars.jsonCommit the changes:
Terminal window git commit -m "Test commit"Example output:
Encrypt secrets to .enc files.......................Passed[main 18ea29c] Test commit2 files changed, 22 insertions(+)create mode 100644 test.tfvars.jsoncreate mode 100644 test.tfvars.json.encCheck the repository to see if the
test.tfvars.json.enc
file was created.Verify that the encrypted file contains encrypted data. It should look something like:
test.tfvars.json.enc {"pm_api_url": "ENC[AES256_GCM,data:...]","pm_api_token_id": "ENC[AES256_GCM,data:...]","pm_api_token_secret": "ENC[AES256_GCM,data:...]","sops": {"age": [{"recipient": "age1w6th0yr0gp5...","enc": "-----BEGIN AGE ENCRYPTED FILE-----\n..."}],"lastmodified": "2025-05-17T20:53:08Z","mac": "ENC[AES256_GCM,data:...]","encrypted_regex": "^(.*)$","version": "3.10.2"}}Test the decryption process by running:
Terminal window sops decrypt --input-type json --output-type json test.tfvars.json.encOutput:
{"pm_api_url": "https://ip:port/api2/json","pm_api_token_id": "token","pm_api_token_secret": "secret"}
If everything works as expected, you can now use this setup to automatically encrypt sensitive files before committing them to the repository.