Advanced Secret Management - bcl1713/nixos GitHub Wiki

Secret Management with Agenix

This guide explains how to manage sensitive information securely using Agenix within this NixOS configuration.

Introduction to Agenix

Agenix is a secrets management tool for NixOS that allows you to securely store sensitive data in your git repository. It uses age encryption to ensure that secrets are:

  • Encrypted using public keys
  • Decryptable only by authorized private keys
  • Safe to store in version control
  • Easily integrated with NixOS and Home Manager

The main benefits of using Agenix compared to other solutions:

  1. Security: Strong encryption based on age
  2. Simplicity: Simple key management and straightforward usage
  3. Integration: Seamless NixOS and Home Manager integration
  4. Version Control: Safe to commit encrypted secrets to Git

Setting Up Agenix

Installation

Agenix is already included in this configuration. The module is imported in flake.nix:

# In flake.nix
inputs = {
  agenix.url = "github:ryantm/agenix";
  agenix.inputs.nixpkgs.follows = "nixpkgs";
};

outputs = { nixpkgs, home-manager, agenix, ... }@inputs: {
  nixosConfigurations = {
    nixbook = lib.nixosSystem {
      # ...
      modules = [
        ./profiles/${systemSettings.profile}/configuration.nix
        agenix.nixosModules.default
      ];
    };
  };
};

And in the home configuration:

# In profiles/personal/home.nix
{
  imports = [ ../../user/packages inputs.agenix.homeManagerModules.default ];
  
  age = {
    identityPaths = [ "/home/${userSettings.username}/.ssh/id_ed25519" ];
    secrets = {
      personal-email = { file = ../../secrets/personal-email.age; };
    };
  };
}

Key Configuration

To set up your encryption keys:

  1. Generate an SSH key if you don't already have one:

    ssh-keygen -t ed25519 -C "[email protected]"
    
  2. Configure secret recipients in secrets/secrets.nix:

    let
      # Your SSH public key
      yourusername = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
      
      # Your system's host key (optional, for system secrets)
      hostname = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    
      allUsers = [ yourusername hostname ];
    in {
      # Define access for each secret
      "personal-email.age".publicKeys = allUsers;
    }
    
  3. Verify your identity path is correctly set in your configuration:

    age.identityPaths = [ "/home/yourusername/.ssh/id_ed25519" ];
    

Managing Secrets

Creating Secrets

To create a new secret:

  1. Create a plain text file with your secret:

    echo "secret-data" > /tmp/mysecret
    
  2. Encrypt the secret using agenix:

    agenix -e secrets/mysecret.age -i /tmp/mysecret
    
  3. Add the secret to your secrets configuration in secrets/secrets.nix:

    "mysecret.age".publicKeys = allUsers;
    
  4. Add the secret to your home or system configuration:

    For Home Manager:

    # In profiles/personal/home.nix
    age.secrets.mysecret = {
      file = ../../secrets/mysecret.age;
    };
    

    For system-wide:

    # In profiles/personal/secrets.nix
    age.secrets.mysecret = {
      file = ../../secrets/mysecret.age;
      owner = "yourusername";
      group = "users";
      mode = "0400";
    };
    

Editing Secrets

To edit an existing secret:

# Edit with temporary decryption
agenix -e secrets/mysecret.age

# Or decrypt to a file, edit, then re-encrypt
agenix -d secrets/mysecret.age > /tmp/mysecret
# Edit /tmp/mysecret
agenix -e secrets/mysecret.age -i /tmp/mysecret
# Remember to securely delete /tmp/mysecret
shred -u /tmp/mysecret

Using Secrets in Your Configuration

Accessing Secrets in Home Manager

# Access a secret for use with a service
programs.someprogram = {
  enable = true;
  passwordFile = config.age.secrets.mysecret.path;
};

# Dynamically load secret content during activation
home.activation.loadSecret = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
  if [ -n "${config.age.secrets.mysecret.path}" ](/bcl1713/nixos/wiki/--n-"${config.age.secrets.mysecret.path}"-); then
    # Read secret content
    SECRET_VALUE=$(cat "${config.age.secrets.mysecret.path}")
    # Use the secret
    $DRY_RUN_CMD some-command --password "$SECRET_VALUE"
  fi
'';

System-wide Secret Usage

# In a system service
services.someservice = {
  enable = true;
  passwordFile = config.age.secrets.mysecret.path;
};

Example: Email Configuration

The configuration includes an example of secret management for personal email:

  1. Secret Definition (in secrets/secrets.nix):

    "personal-email.age".publicKeys = allUsers;
    
  2. Secret Declaration (in profiles/personal/home.nix):

    age.secrets.personal-email = { 
      file = ../../secrets/personal-email.age; 
    };
    
  3. Usage (in user/packages/apps/development/git.nix):

    # Set email from secret file if not explicitly configured
    home.activation.setGitEmail = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
      if [[ -z "${cfg.userEmail}" && -n "${
        if config.age.secrets ? personal-email then
          config.age.secrets.personal-email.path
        else
          ""
      }" ]]; then
        # User hasn't specified an email and the secret exists
        EMAIL=$(cat "${config.age.secrets.personal-email.path}")
        $DRY_RUN_CMD ${pkgs.git}/bin/git config --global user.email "$EMAIL"
      elif [ -n "${cfg.userEmail}" ](/bcl1713/nixos/wiki/--n-"${cfg.userEmail}"-); then
        # User explicitly set an email, use that 
        $DRY_RUN_CMD ${pkgs.git}/bin/git config --global user.email "${cfg.userEmail}"
      fi
    '';
    

Best Practices

Security Considerations

  • Never commit unencrypted secrets to your repository
  • Keep your private keys secure
  • Use restrictive file permissions (0400 or 0600)
  • Clean up temporary files containing sensitive data

Secret Organization

  • Group related secrets together (e.g., all database credentials)
  • Use descriptive filenames
  • Document the purpose of each secret in comments

Backup Strategies

  • Ensure your private keys are backed up securely
  • Consider using multiple recipient keys for redundancy
  • Document key locations for disaster recovery

Collaboration

When collaborating with others:

  • Add their public keys to secrets/secrets.nix
  • Re-encrypt secrets for multiple recipients
  • Establish a secure process for key exchange

Troubleshooting

Common Issues

  1. "Failed to decrypt":

    • Ensure private key is at the correct location
    • Verify you are a recipient for the secret
    • Check file permissions on your private key
  2. "Secret not found":

    • Check the path in your configuration
    • Verify secret is properly declared
  3. Permission Issues:

    • Check owner/group settings
    • Verify mode settings (permissions)

Further Reading

Related Pages