Skip to content

Secrets Management

All secrets are managed using a hybrid approach combining 1Password as the primary source of truth, and Agenix for the initial deployment bootstrap. This ensures no sensitive secrets are exposed in the git repository while maintaining a fully automated deployment.

  1. Agenix for the Bootstrap Token: We use Agenix to securely encrypt a single file in the repository: 1password-token.age. This file contains the 1Password Service Account Token. NixOS decrypts this file at boot using the host’s private SSH key (/etc/ssh/ssh_host_ed25519_key).
  2. 1Password for Everything Else: Once the 1password-token is available on the machine, NixOS systemd services (like cloudflared) use the 1Password CLI (op) to fetch the rest of the required credentials dynamically at runtime.

To maintain security, the infrastructure only has access to the specific secrets it needs:

  1. Dedicated Vault: A specific vault (e.g., Homelab) contains only the items required for this infrastructure (Cloudflare tokens, etc.).
  2. Service Account: A 1Password Service Account is created and granted “Read-Only” access exclusively to that dedicated vault.
  3. Token-based Auth: The server uses the Service Account Token (injected via Agenix) to authenticate. If the machine is ever compromised, your personal vaults and other sensitive data remain completely isolated and inaccessible.

To inject secrets securely, we use a unified set of NixOS helpers defined in the infrastructure repository. This follows security best practices:

  1. Unified Helper Flow:
    • homelab.mkSecretService: Creates a dedicated oneshot service (e.g., transmission-secrets) to fetch credentials. It runs as root, fetches secrets with automatic retries, writes them to a private RAM-based directory (/run/<service>/), and sets correct ownership.
    • homelab.serviceConfig: Configures the main service to have access to these secrets, setting 0700 permissions on the runtime directory and loading the 1Password token via systemd LoadCredential.
  2. Resilience & Security:
    • Automatic Retries: All 1Password fetching includes a retry mechanism to handle transient network issues.
    • Safe Writing: Environment files are generated using printf to ensure special characters in passwords don’t break the shell parsing.
    • RAM-based storage: Secrets never touch the disk; they live in tmpfs (/run) and are automatically wiped on service stop or reboot.
    • Leak Prevention: We avoid passing secrets as environment variables in the service definition. Instead, we use EnvironmentFile or direct file-based reading, preventing secrets from appearing in systemctl status or /proc.
  3. DynamicUser Support: For services that use systemd DynamicUser (where the user ID changes on every start), we use an inline ExecStartPre hook via homelab.fetchSecretFile to fetch secrets directly within the service context.

You can still interact with secrets manually using the 1Password CLI:

Terminal window
# Authenticate (if not using a Service Account token locally)
op signin
# Read a specific secret
op read "op://Homelab/Cloudflare/api-token"