sops-nix

repository·master·Indexed 25 days ago

https://github.com/mic92/sops-nix

A tool for NixOS and nix-darwin that enables atomic, declarative, and reproducible secret management using Mozilla SOPS. It decrypts secrets from SOPS files during activation and stores them as individual files with controlled permissions. Supports encryption via GPG and age, and is compatible with NixOps, nixos-rebuild, krops, morph, nixus, and home-manager. Supports multiple formats including YAML, dotenv, INI, JSON, and binary.

Tokens
5.7K
Snippets
18
Records
32
Agent score
35%

What's inside sops-nix

  1. Overview of sops-nix

    master

    sops-nix provides atomic, declarative, and reproducible secret provisioning for NixOS based on sops. It decrypts secrets from sops files during activation time and stores them as individual files with access-controlled users, permissions, and groups.

    Key features include:

    • Compatibility: Works with NixOps, nixos-rebuild, krops, morph, nixus, and home-manager.
    • Version Control Friendly: Encrypted files can be committed to version control; diffs can be shown in cleartext via sops.
    • CI Friendly: Secrets can be added to the Nix store without leaking them, allowing full machine builds from a repository.
    • Atomic & Rollback Support: New secrets are written to a new directory and replaced atomically. Rollback support is available if sops files are added to the Nix store.
    • Flexible Formats: Supports YAML, dotenv, INI, JSON, or binary storage formats.
    • Cloud KMS Support: While not officially supported by sops-nix yet, AWS KMS, GCP KMS, Azure Key Vault, and Hashicorp Vault can be controlled via environment variables passed to sops.
  2. Create and update SOPS files

    master

    Create a new secret

    Once .sops.yaml is configured, create a new encrypted file using the sops CLI:

    $ nix-shell -p sops --run "sops secrets/example.yaml"

    Update keys for a secret

    If you add a new host to your .sops.yaml, you must update the existing secrets to include the new recipient:

    $ nix-shell -p sops --run "sops updatekeys secrets/example.yaml"
  3. Install sops-nix

    master

    You can install sops-nix using several methods depending on your Nix setup.

    Add sops-nix as an input in your flake and include the sops-nix.nixosModules.sops module in your NixOS configuration.

    nix-darwin

    For macOS users using nix-darwin, use the sops-nix.darwinModules.sops module.

    niv

    If you are not using flakes, add the repository via niv and import the module from the resulting source path.

    fetchTarball

    Manually fetch the repository using builtins.fetchTarball and import the module directly from the archive.

    # Flakes example
    {
      inputs.sops-nix.url = "github:Mic92/sops-nix";
      inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs";
    
      outputs = { self, nixpkgs, sops-nix }: {
        nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            ./configuration.nix
            sops-nix.nixosModules.sops
          ];
        };
      };
    }
  4. Set a user's password using sops-nix

    master

    Because sops-nix runs after NixOS creates users, you cannot directly use users.users.<name>.hashedPasswordFile with standard secrets.

    To work around this, set neededForUsers = true on the secret. This causes the secret to be decrypted to /run/secrets-for-users (instead of /run/secrets) before user creation. The password must be stored as a hash (e.g., generated via mkpasswd).

    Note for Impermanence users: The decryption key (sops.age.keyFile or SSH keys) must be in a persisted directory and loaded early during boot.

    # 1. Generate a hash
    # $ echo "password" | mkpasswd -s
    
    # 2. Configure the secret and user
    { config, ... }: {
      sops.secrets.my-password.neededForUsers = true;
    
      users.users.mic92 = {
        isNormalUser = true;
        hashedPasswordFile = config.sops.secrets.my-password.path;
      };
    }
  5. Use templates to embed secrets in configuration files

    master

    The template feature allows you to inject secrets into configuration files during the activation phase.

    1. Define the secret: Add the secret to sops.secrets.
    2. Create a template: Use sops.templates.<name>.content and reference the secret using ${config.sops.placeholder.<secret-name>}.
    3. Reference the template: Use the .path attribute of the template in your service configuration (e.g., in ExecStart).
    {
      # 1. Define secret
      sops.secrets.your-secret = { };
    
      # 2. Create template
      sops.templates."your-config-with-secrets.toml".content = ''
        password = "${config.sops.placeholder.your-secret}"
      '';
      sops.templates."your-config-with-secrets.toml".owner = "serviceuser";
    
      # 3. Use in service
      systemd.services.myservice = {
        serviceConfig = {
          ExecStart = "${pkgs.myservice}/bin/myservice --config ${config.sops.templates."your-config-with-secrets.toml".path}";
          User = "serviceuser";
        };
      };
    }
  6. Use different file formats (YAML, JSON, Binary)

    master

    sops-nix supports YAML, JSON, INI, dotenv, and binary formats. You can set a global default for your configuration or override it per secret using sops.defaultSopsFile, sops.defaultSopsFormat, or the format and sopsFile options within a specific secret definition.

    {
      imports = [ <sops-nix/modules/sops> ];
      sops.defaultSopsFile = ./secrets.yaml;
    
      sops.secrets.github_token = {
        sopsFile = ./other-secrets.json;
        format = "json";
      };
    }
  7. Use sops-nix with Home Manager

    master

    sops-nix provides a home-manager module for non-root users. Unlike the system-wide module, it uses a systemd user service (sops-nix.service) and places secrets in $XDG_RUNTIME_DIR/secrets.d instead of /run/secrets. Secrets are symlinked to $HOME/.config/sops-nix/secrets.

    Important Requirements:

    • Requires systemd/user.
    • The home.homeDirectory option must be set to determine the home directory during evaluation (especially if using stand-alone home-manager or non-NixOS systems).
    • Services requiring secrets must order after sops-nix.service.
  8. Generate encryption keys (age or GPG)

    master

    To edit secrets, you need a private key. You can use age or GnuPG.

    age

    Generate an age key pair:

    $ mkdir -p ~/.config/sops/age
    $ age-keygen -o ~/.config/sops/age/keys.txt

    Or convert an existing SSH Ed25519 key to age:

    $ mkdir -p ~/.config/sops/age
    $ nix-shell -p ssh-to-age --run "ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt"

    GnuPG

    Generate a GPG key using gpg --full-generate-key.

    You can also convert an RSA SSH key to a GPG key using ssh-to-pgp:

    $ nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet"

    Note for nix-darwin users: Save the age key to $HOME/Library/Application Support/sops/age/keys.txt or set a custom configuration directory.

    # Generate age key
    $ mkdir -p ~/.config/sops/age
    $ age-keygen -o ~/.config/sops/age/keys.txt
  9. Encrypt and decrypt binary files

    master

    For arbitrary binary data that cannot fit into JSON or YAML, use the binary format. In this mode, one file corresponds to exactly one secret.

    To encrypt a file: sops -e <file> > <output_file>

    To decrypt a file: sops -d <file> > <output_file>

  10. Migrate secrets from pass/krops to sops-nix

    master

    Use this one-liner to convert your existing pass (password store) secrets into a YAML structure compatible with sops.

    for i in *.gpg; do echo "$(basename $i .gpg): |\n$(pass $(dirname $i)/$(basename $i .gpg)| sed 's/^/  /')"; done
  11. Get a public key for a target machine

    master

    To allow a target machine to decrypt secrets, you need its public key.

    Using age (via SSH Ed25519)

    Convert an SSH Ed25519 public key to age format:

    $ nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'

    Using GPG (via SSH RSA)

    Convert an SSH RSA key to a GPG key:

    $ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc"
  12. Emit a plain file instead of extracting a specific key

    master

    By default, sops-nix extracts a single key from YAML or JSON files. To use the entire file as a single secret (e.g., to mount a full config file), set the key option to an empty string "".

    sops.secrets.my-config = {
      format = "yaml";
      sopsFile = ./my-config.yaml;
      key = "";
    };