# Secrets Management with SOPS + Age This directory contains encrypted secrets for the infrastructure using [SOPS](https://github.com/getsops/sops) with [Age](https://github.com/FiloSottile/age) encryption. ## 🔐 Security Model - **Encryption**: All secret files encrypted with Age before committing to Git - **Key Storage**: Age private key stored OUTSIDE this repository - **Git-Safe**: Only encrypted files (.sops.yaml) are committed - **Decryption**: Happens at runtime by Ansible or manually with `sops` ## 📁 Directory Structure ``` secrets/ ├── README.md # This file ├── shared.sops.yaml # Shared secrets (encrypted) └── clients/ └── *.sops.yaml # Per-client secrets (encrypted) ``` ## 🔑 Age Key Location **IMPORTANT**: The Age private key is stored at: ``` keys/age-key.txt ``` This file is **gitignored** and must **NEVER** be committed. ### Key Backup Checklist ✅ **You MUST backup the Age key securely:** 1. **Password Manager**: Store in Bitwarden/1Password/etc ```bash # Copy key content cat keys/age-key.txt # Store as secure note in password manager ``` 2. **Print Backup** (optional but recommended): ```bash # Print and store in secure physical location cat keys/age-key.txt | lpr ``` 3. **Encrypted USB Drive** (optional): ```bash # Copy to encrypted USB for offline backup cp keys/age-key.txt /Volumes/SecureUSB/infrastructure-age-key.txt ``` ⚠️ **WARNING**: If you lose this key, encrypted secrets are PERMANENTLY UNRECOVERABLE! ## 🚀 Quick Start ### Prerequisites ```bash # Install SOPS and Age brew install sops age # Ensure you have the Age key ls -la keys/age-key.txt ``` ### View Encrypted Secrets ```bash # View shared secrets SOPS_AGE_KEY_FILE=keys/age-key.txt sops secrets/shared.sops.yaml # View client secrets SOPS_AGE_KEY_FILE=keys/age-key.txt sops secrets/clients/test.sops.yaml ``` ### Edit Encrypted Secrets ```bash # Edit shared secrets (decrypts, opens $EDITOR, re-encrypts on save) SOPS_AGE_KEY_FILE=keys/age-key.txt sops secrets/shared.sops.yaml # Edit client secrets SOPS_AGE_KEY_FILE=keys/age-key.txt sops secrets/clients/test.sops.yaml ``` ### Create New Client Secrets ```bash # Copy template cp secrets/clients/test.sops.yaml secrets/clients/newclient.sops.yaml # Edit with generated passwords SOPS_AGE_KEY_FILE=keys/age-key.txt sops secrets/clients/newclient.sops.yaml ``` ### Generate Secure Passwords ```bash # Random 32-character password openssl rand -base64 32 # Random 24-character password openssl rand -base64 24 # Random hex string (32-byte, 64 characters) openssl rand -hex 32 ``` ## 🔧 Usage with Ansible Ansible automatically decrypts SOPS files using the `community.sops` collection. **In playbooks:** ```yaml - name: Load client secrets community.sops.load_vars: file: "{{ playbook_dir }}/../secrets/clients/{{ client_name }}.sops.yaml" name: client_secrets - name: Use decrypted secret debug: msg: "DB Password: {{ client_secrets.authentik_db_password }}" ``` **Environment variable required:** ```bash export SOPS_AGE_KEY_FILE=/path/to/infrastructure/keys/age-key.txt ``` ## 📝 Secret File Structure ### shared.sops.yaml Contains secrets shared across all infrastructure: - Hetzner Cloud API token - Hetzner Storage Box credentials - ACME email for SSL certificates ### clients/*.sops.yaml Per-client secrets: - Database passwords (Authentik, Nextcloud) - Admin passwords - Secret keys and tokens - Restic repository password - OIDC credentials (after generation) ## 🛠️ Common Tasks ### Decrypt to Temporary File ```bash # Decrypt for one-time use SOPS_AGE_KEY_FILE=keys/age-key.txt sops --decrypt secrets/shared.sops.yaml > /tmp/secrets.yaml # Use the file cat /tmp/secrets.yaml # IMPORTANT: Delete when done rm /tmp/secrets.yaml ``` ### Encrypt New File ```bash # Create plaintext file cat > secrets/newfile.sops.yaml <