-
Notifications
You must be signed in to change notification settings - Fork 0
SOPS Encryption
rsenv integrates with SOPS to encrypt sensitive files in your vault. This enables secure backup, version control, and sharing of encrypted vaults.
SOPS (Secrets OPerationS) encrypts file contents while preserving structure. rsenv provides batch operations:
| Command | Purpose |
|---|---|
rsenv sops encrypt |
Encrypt files matching configured patterns |
rsenv sops decrypt |
Decrypt .enc files |
rsenv sops clean |
Delete plaintext files (after encryption) |
rsenv sops status |
Show encryption state |
rsenv sops gitignore-sync |
Sync .gitignore with encryption patterns |
rsenv sops gitignore-status |
Show gitignore sync status |
rsenv sops gitignore-clean |
Remove rsenv-managed gitignore section |
rsenv hook install |
Install pre-commit hook in vault's git repo |
rsenv hook remove |
Remove pre-commit hook |
rsenv hook status |
Show hook installation status |
rsenv uses content-addressed encryption to detect when plaintext files change after encryption:
Filename format: {name}.{hash}.enc where hash is SHA-256 of plaintext content.
Example:
config.env # Plaintext file
config.env.a1b2c3d4e5f6.enc # Encrypted (hash embedded in name)
Status categories:
| Status | Meaning |
|---|---|
current |
Encrypted file exists with matching hash (up-to-date) |
stale |
Encrypted file exists but hash differs (needs re-encryption) |
pending_encrypt |
Plaintext exists, no encrypted version |
orphaned |
Encrypted file exists, no plaintext |
This enables the pre-commit hook to detect modified files that weren't re-encrypted.
# macOS
brew install sops
# Linux
curl -LO https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
chmod +x sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops# Generate key
gpg --gen-key
# List keys (note the fingerprint)
gpg --list-keys --keyid-format long# Create global config
rsenv config init --globalEdit ~/.config/rsenv/rsenv.toml:
[sops]
gpg_key = "60A4127E82E218297532FAB6D750B66AE08F3B90" # Your GPG fingerprint
# File extensions to encrypt
file_extensions_enc = ["env", "envrc", "yaml", "yml", "json"]
# Specific filenames to encrypt
file_names_enc = ["dot_pypirc", "dot_pgpass", "kube_config"]
# Extensions to decrypt (encrypted files)
file_extensions_dec = ["enc"]rsenv uses content-addressed filenames for encrypted files. The hash of the plaintext content is embedded in the filename:
{filename}.{hash8}.enc
Examples:
secrets.env → secrets.env.a1b2c3d4.enc
config.envrc → config.envrc.e5f6g7h8.enc
guarded/dot.env → guarded/dot.env.b9c0d1e2.enc
This design prevents accidental data loss:
- Staleness detection: If you edit a plaintext file, rsenv detects the hash mismatch and marks it as "stale"
-
Safe cleanup:
rsenv sops cleanonly deletes plaintext when the hash matches - never deletes unsaved changes -
Pre-commit enforcement: The
--checkflag enables git hooks to block commits with stale/unencrypted files
- Algorithm: SHA-256 of plaintext content
- Length: First 8 hex characters (32 bits)
- Collision resistance: 4 billion combinations - sufficient for any vault
# Encrypt single file
rsenv sops encrypt secrets.env
# Encrypt files in project's vault (default)
rsenv sops encrypt
# Encrypt all vaults
rsenv sops encrypt --global
# Encrypt files in specific directory
rsenv sops encrypt --dir /path/to/dirArguments:
-
FILE: Single file to encrypt (optional)
Options:
-
--global/-g: Encrypt all vaults -
--dir/-d: Encrypt specific directory -
--vault-base: Override vaults directory (requires --global)
Without arguments or options, encrypts all matching files in the current project's vault.
What happens:
- Finds files matching
file_extensions_encandfile_names_enc - Computes SHA-256 hash of each plaintext file
- Encrypts each:
file.env→file.env.{hash8}.enc - Removes any old
file.env.*.encwith different hashes - Original plaintext files remain (use
cleanto remove)
# Decrypt single file
rsenv sops decrypt secrets.env.enc
# Decrypt .enc files in project's vault (default)
rsenv sops decrypt
# Decrypt all vaults
rsenv sops decrypt --global
# Decrypt in specific directory
rsenv sops decrypt --dir /path/to/dirArguments:
-
FILE: Single file to decrypt (optional)
What happens:
- Finds files matching
file_extensions_dec(default:.enc) - Parses hash from filename:
file.env.{hash8}.enc - Decrypts each:
file.env.{hash8}.enc→file.env - Verifies decrypted content matches the hash (integrity check)
- Encrypted files remain
# Remove plaintext files in project's vault
rsenv sops clean
# Clean all vaults
rsenv sops clean --globalWhat happens:
- Finds files matching encryption patterns
- Computes hash of each plaintext file
-
Only deletes if a matching
{name}.{hash}.encexists (hash must match!) - Refuses to delete stale files (protects unsaved changes)
- Only encrypted files remain after successful clean
Safety guarantee: If you've edited a file since last encryption, clean will NOT delete it. You must run encrypt first to update the encrypted version.
# Status for project's vault
rsenv sops status
# Status for all vaults
rsenv sops status --global
# Check mode (for scripts/hooks) - exits 0 if clean, 1 if needs attention
rsenv sops status --checkOptions:
-
--check: Exit with code 1 if any files need encryption (for CI/hooks)
File Categories:
| Category | Meaning | Action Needed |
|---|---|---|
| Pending encrypt | New file, no .enc exists |
Run encrypt
|
| Stale | Plaintext changed since encryption | Run encrypt
|
| Current | Hash matches, up-to-date | None (can clean) |
| Orphaned |
.enc exists but no plaintext |
Can delete |
Output:
SOPS Status for /home/user/.rsenv/vaults/myproject-abc123:
Pending encryption (1):
envs/new-secrets.env
Stale (1):
envs/local.env (current: e5f6g7h8, encrypted: a1b2c3d4)
Current (2):
envs/prod.env.b9c0d1e2.enc
envs/staging.env.f3a4b5c6.enc
Orphaned (1):
envs/old-config.env.deadbeef.enc
Exit codes for scripting:
-
rsenv sops status --check: Returns exit code 1 if any files need encryption (pending or stale), 0 otherwise
# 1. Work on plaintext files
vim $RSENV_VAULT/envs/local.env
# 2. Check status (optional - see what changed)
rsenv sops status
# 3. Encrypt when done
rsenv sops encrypt
# 4. Remove plaintext (optional, for sharing)
rsenv sops clean # Safe: won't delete if hash mismatch
# 5. Commit encrypted files
cd $RSENV_VAULT
git add *.enc
git commit -m "Update encrypted configs" # Hook blocks if staleWith pre-commit hook installed: Steps 2-3 can be enforced automatically - the hook blocks commit if you forget to encrypt.
# 1. Decrypt to work on files
rsenv sops decrypt
# 2. Edit plaintext
vim $RSENV_VAULT/envs/local.env
# 3. Re-encrypt
rsenv sops encrypt
# 4. Clean up
rsenv sops cleanWhen you run rsenv sops encrypt on the vault directory, rsenv automatically updates .gitignore:
# ---------------------------------- rsenv-sops-start -----------------------------------
*.env # sops-managed 2024-01-15 10:30:45
*.envrc # sops-managed 2024-01-15 10:30:45
*.yaml # sops-managed 2024-01-15 10:30:45
dot_pgpass # sops-managed 2024-01-15 10:30:45
# ---------------------------------- rsenv-sops-end -----------------------------------Patterns come from:
-
file_extensions_enc→*.{ext}patterns -
file_names_enc→ exact filename patterns
Benefits:
- Prevents accidental commit of plaintext secrets
- Updated automatically during encryption
- Preserved across encrypt/decrypt cycles
Manage the rsenv-managed section explicitly:
# Show status for current vault's gitignore
rsenv sops gitignore-status
# Show status for global gitignore only
rsenv sops gitignore-status --global
# Sync patterns to current vault's .gitignore
rsenv sops gitignore-sync
# Sync patterns to global gitignore only
rsenv sops gitignore-sync --global
# Remove rsenv section from vault's .gitignore
rsenv sops gitignore-clean
# Remove rsenv section from global gitignore only
rsenv sops gitignore-clean --globalOptions:
-
--global: Operate on global gitignore only (not per-vault) -
-y, --yes: Skip confirmation prompt (gitignore-sync only)
Note: Without --global, these commands only affect the current vault's gitignore. To sync both vault and global, run the command twice (with and without --global).
SOPS auto-detects file formats:
| Extension | Handling |
|---|---|
.json |
Structured (keys encrypted, structure visible) |
.yaml, .yml
|
Structured |
.env |
dotenv format (--input-type dotenv) |
.envrc |
Default SOPS format (shell scripts, not dotenv) |
| Other | Binary (entire file encrypted) |
For .env files only, rsenv tells SOPS to preserve the dotenv format:
# Plaintext
DATABASE_URL=postgres://localhost/mydb
API_KEY=sk-secret-123
# Encrypted (structure preserved)
DATABASE_URL=ENC[AES256_GCM,data:...,tag:...]
API_KEY=ENC[AES256_GCM,data:...,tag:...]# ~/.config/rsenv/rsenv.toml
[sops]
# GPG key fingerprint (required for GPG encryption)
gpg_key = "60A4127E82E218297532FAB6D750B66AE08F3B90"
# Age public key (alternative to GPG)
# age_key = "age1..."
# Extensions to encrypt
file_extensions_enc = [
"env",
"envrc",
"yaml",
"yml",
"json",
"p12", # PKCS#12 certificates
"keystore", # Java keystores
]
# Specific filenames to encrypt
file_names_enc = [
"dot_pypirc",
"dot_pgpass",
"kube_config",
]
# Extensions to decrypt (typically just "enc")
file_extensions_dec = ["enc"]
# Specific filenames to decrypt (usually empty)
file_names_dec = []Override config via environment:
export RSENV_SOPS_GPG_KEY="fingerprint"
export RSENV_SOPS_FILE_EXTENSIONS_ENC="env,yaml,json"rsenv uses parallel execution (via rayon) for batch operations:
- Default: 8 parallel threads
- Speeds up encryption/decryption of many files
SOPS also supports Age encryption:
# Generate Age key
age-keygen -o key.txt
# Configure
[sops]
age_key = "age1..."Age is simpler than GPG but less widely supported.
rsenv can install a pre-commit hook in your vault's git repository to prevent committing with unencrypted or stale files.
# Install hook in vault's .git/hooks/pre-commit
rsenv hook install
# Overwrite existing hook
rsenv hook install --forcersenv hook removersenv hook statusThe pre-commit hook runs rsenv sops status --check before each commit:
- Blocks commit if any files need encryption (pending or stale)
- Allows commit only when all files are current
- Shows clear error message with instructions
Example blocked commit:
$ git commit -m "Update configs"
ERROR: Unencrypted or stale files in vault.
Run 'rsenv sops encrypt' to update encryption.
In emergencies, you can bypass with --no-verify:
git commit --no-verify -m "Emergency fix"Warning: This defeats the safety mechanism. Use sparingly.
- Vault location: Outside git repository
- Symlinks: Git only sees symlinks, not actual secrets
- Encryption: Vault contents encrypted at rest
- gitignore: Auto-excludes plaintext files
- Content-addressed filenames: Hash in filename detects stale encryption
- Pre-commit hook: Blocks commits with unencrypted changes
# Work locally (plaintext)
rsenv sops decrypt
vim $RSENV_VAULT/envs/prod.env
# Before backup/share (encrypted)
rsenv sops encrypt
rsenv sops clean
# Vault now contains only *.enc filesYour GPG key isn't available:
# List available keys
gpg --list-secret-keys
# Import if needed
gpg --import key.ascEnsure config is set:
rsenv config show | grep sopsCheck your patterns:
# Show what would be encrypted
rsenv sops status
# Verify config
rsenv config showSOPS stores key info in the encrypted file. Verify you have the right key:
# Check what key was used
sops --decrypt --verbose file.enc 2>&1 | grep -i keyrsenv provides a git pre-commit hook to prevent committing when files need encryption.
# Install at default location (base_dir, typically ~/.rsenv)
rsenv hook install
# Install at specific git repository
rsenv hook install --dir /path/to/repo
# Force overwrite existing hook
rsenv hook install --forceThe hook runs rsenv sops status --global --check before each commit:
- Exit 0: All files encrypted and current → commit allowed
- Exit 1: Files need encryption (pending or stale) → commit blocked
| Command | Purpose |
|---|---|
rsenv hook install |
Install pre-commit hook |
rsenv hook remove |
Remove pre-commit hook |
rsenv hook status |
Show hook installation status |
Options:
-
--dir <PATH>: Target git repository (default: base_dir) -
--force/-f: Overwrite existing hook (install only)
rsenv hook remove
# Remove from specific location
rsenv hook remove --dir /path/to/repo- Configuration - Full rsenv configuration options
- Vault Management - Vault structure and guarding
- Core Concepts - Security philosophy
rsenv Documentation