Secrets Management
Secure secrets management with SOPS and hardware wallet encryption.
Overview
Section titled “Overview”This section covers secure secrets management using:
- SOPS (Secrets OPerationS) - Encrypted secrets in Git
- Ledger GPG - Hardware-backed encryption keys
- Nix integration - Declarative secrets deployment
All secrets are encrypted with your Ledger hardware wallet, ensuring keys never touch your computer.
Documentation in This Section
Section titled “Documentation in This Section”Complete guide to secrets management with SOPS and Ledger.
Covers:
- SOPS architecture and security model
- Setting up SOPS with Ledger GPG
- Creating and editing encrypted secrets
- Using secrets in Nix configurations
- Troubleshooting and best practices
- Advanced features (multi-key, rotation)
Status: ✅ Comprehensive guide
Quick Start
Section titled “Quick Start”Prerequisites
Section titled “Prerequisites”- Ledger Nano S with GPG configured
- SOPS installed (included in config)
- GPG key initialized on Ledger
See Ledger Setup first.
Create Your First Secret (2 minutes)
Section titled “Create Your First Secret (2 minutes)”# 1. Set GPG homeexport GNUPGHOME=~/.gnupg-ledger
# 2. Ensure agent runningpgrep -f ledger-gpg-agent || \ ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
# 3. Create encrypted secretsops nix/secrets/secrets.yaml
SOPS will:
- Decrypt using Ledger (button press required)
- Open in your editor
- Re-encrypt when you save
Use Secret in Nix
Section titled “Use Secret in Nix”# In your host config{ sops.secrets."myapp/api-key" = {};
# Secret available at runtime: # /run/secrets/myapp/api-key}
Why SOPS?
Section titled “Why SOPS?”Benefits
Section titled “Benefits”✅ Git-friendly - Encrypted secrets safely committed ✅ Hardware security - Ledger GPG for encryption ✅ Declarative - Nix integration for deployment ✅ Selective encryption - Only values encrypted, keys readable ✅ Multi-format - YAML, JSON, ENV, INI, binary
Security Model
Section titled “Security Model”┌─────────────────────────────────────────────────────────────┐│ SOPS Workflow │├─────────────────────────────────────────────────────────────┤│ ││ 1. Edit: sops secrets.yaml ││ ↓ ││ 2. Decrypt with Ledger GPG ││ ↓ ││ 3. Edit in memory (plaintext) ││ ↓ ││ 4. Save changes ││ ↓ ││ 5. Re-encrypt with Ledger GPG ││ ↓ ││ 6. Safe to commit! ││ │└─────────────────────────────────────────────────────────────┘
Key Points:
- Private key on Ledger (never on disk)
- Physical confirmation for every encrypt/decrypt
- Only encrypted data in Git
- Keys recoverable from Ledger seed phrase
Architecture
Section titled “Architecture”SOPS Configuration
Section titled “SOPS Configuration”Flake input (flake.nix
):
inputs.sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs";};
Module (nix/modules/secrets/sops.nix
):
{ sops = { gnupg.home = "~/.gnupg-ledger"; defaultSopsFile = ./nix/secrets/secrets.yaml; };
environment.variables = { GNUPGHOME = "/Users/wikigen/.gnupg-ledger"; };}
Rules (.sops.yaml
):
creation_rules: - path_regex: .*\.(yaml|json|env|ini)$ pgp: >- D2A7EC63E350CC488197CB2ED369B07E00FB233E
File Structure
Section titled “File Structure”Config/├── .sops.yaml # SOPS configuration├── nix/│ ├── modules/secrets/│ │ └── sops.nix # SOPS Nix module│ └── secrets/│ ├── README.md # Usage docs│ ├── secrets.yaml # Encrypted secrets (safe for Git)│ └── secrets.yaml.example # Template
Common Tasks
Section titled “Common Tasks”Create Secret
Section titled “Create Secret”# New secret filesops nix/secrets/my-app.yaml
Edit Secret
Section titled “Edit Secret”# Existing secretsops nix/secrets/secrets.yaml
View Secret
Section titled “View Secret”# Decrypt to stdoutsops -d nix/secrets/secrets.yaml
# Extract specific keysops -d --extract '["api"]["key"]' nix/secrets/secrets.yaml
Use in Nix
Section titled “Use in Nix”{ # Declare secret sops.secrets."database/password" = { owner = "wikigen"; mode = "0400"; };
# Use in service environment.variables = { DB_PASSWORD_FILE = config.sops.secrets."database/password".path; };}
Secret Formats
Section titled “Secret Formats”YAML (Recommended)
Section titled “YAML (Recommended)”# Nested structuredatabase: host: localhost password: secret123
api: key: api-key-here endpoint: https://api.example.com
{ "database": { "password": "secret123" }, "api": { "key": "api-key-here" }}
ENV File
Section titled “ENV File”DATABASE_PASSWORD=secret123API_KEY=api-key-here
After Encryption
Section titled “After Encryption”database: host: ENC[AES256_GCM,data:bG9jYWxob3N0,iv:...] password: ENC[AES256_GCM,data:c2VjcmV0MTIz,iv:...]sops: pgp: - fp: D2A7EC63E350CC488197CB2ED369B07E00FB233E
Note: Structure visible, values encrypted.
Advanced Features
Section titled “Advanced Features”Multiple GPG Keys
Section titled “Multiple GPG Keys”creation_rules: - path_regex: .*\.yaml$ pgp: >- YOUR-KEY-HERE, TEAMMATE-KEY-HERE, CI-KEY-HERE
Key Rotation
Section titled “Key Rotation”# Update .sops.yaml with new keys# Then rotate all secretssops updatekeys nix/secrets/secrets.yaml
Environment-Specific Secrets
Section titled “Environment-Specific Secrets”creation_rules: # Development - path_regex: secrets/dev/.*\.yaml$ pgp: DEV-KEY
# Production - path_regex: secrets/prod/.*\.yaml$ pgp: PROD-KEY
Integration Examples
Section titled “Integration Examples”Application Config
Section titled “Application Config”{ # Declare secrets sops.secrets."myapp/config" = {};
# Create config file referencing secret environment.etc."myapp/config.json".text = builtins.toJSON { api_key_file = config.sops.secrets."myapp/config".path; };}
Launchd Service
Section titled “Launchd Service”{ sops.secrets."service/token" = {};
launchd.user.agents.myservice = { config = { ProgramArguments = [ "${pkgs.myapp}/bin/myapp" "--token-file" config.sops.secrets."service/token".path ]; }; };}
Environment Variables
Section titled “Environment Variables”{ sops.secrets."aws/credentials" = {};
home.sessionVariables = { AWS_CREDENTIALS_FILE = config.sops.secrets."aws/credentials".path; };}
Troubleshooting
Section titled “Troubleshooting””No GPG key found"
Section titled “”No GPG key found"”# Set GPG homeexport GNUPGHOME=~/.gnupg-ledger
# Verify keygpg --list-keys
# Should show your key fingerprint
"Failed to get data key”
Section titled “"Failed to get data key””# Check .sops.yaml has correct keycat .sops.yaml
# Ensure agent runningpgrep -f ledger-gpg-agent || \ ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
Ledger Not Responding
Section titled “Ledger Not Responding”- Unlock Ledger (enter PIN)
- Open SSH/GPG Agent app
- Verify screen shows “ready”
- Restart agent if needed
See SOPS Guide for details.
Best Practices
Section titled “Best Practices”Secret Organization
Section titled “Secret Organization”# Good: Clear hierarchyenvironments: dev: database: password: xxx api: key: xxx prod: database: password: xxx api: key: xxx
# Avoid: Flat structuredev_db_pw: xxxprod_api_key: xxx
Secret Rotation
Section titled “Secret Rotation”- Generate new secret value
- Update in SOPS:
sops secrets.yaml
- Rebuild:
darwin-rebuild switch
- Verify new secret works
- Revoke old secret in external system
Backup Strategy
Section titled “Backup Strategy”- Ledger seed - 24-word phrase (offline storage)
- Alternative GPG key - For team access
- Test recovery - Periodically verify
Related Documentation
Section titled “Related Documentation”In This Section
Section titled “In This Section”- SOPS Guide - Complete SOPS documentation
Other Sections
Section titled “Other Sections”- Ledger Setup - Hardware wallet setup
- GPG Signing - GPG configuration
- Structure Guide - Config architecture
External Resources
Section titled “External Resources”- SOPS GitHub - Official repository
- sops-nix - NixOS integration
- SOPS Usage Guide - Official docs
- Mozilla SOPS Blog - Original announcement
Ready to manage secrets? Start with the SOPS Guide!