Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Secrets

NixFleet provides a secrets wiring scope that handles identity path management, impermanence persistence, and boot ordering. Fleet repos bring their own backend (agenix, sops-nix) and wire it to the framework.

Enabling the secrets scope

nixfleet.secrets.enable = true;

The scope computes config.nixfleet.secrets.resolvedIdentityPaths based on its options:

  • Servers (enableUserKey = false, the default for the server role): host SSH key only (/etc/ssh/ssh_host_ed25519_key)
  • Workstations (enableUserKey = true, the default for the workstation role): host SSH key + user key fallback (~/.keys/id_ed25519)

On impermanent hosts, identity keys are automatically persisted.

agenix example

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

# In your host modules
{inputs, config, ...}: {
  imports = [inputs.agenix.nixosModules.default];

  # Use framework-computed identity paths
  age.identityPaths = config.nixfleet.secrets.resolvedIdentityPaths;

  age.secrets.root-password.file = "${inputs.secrets}/org/root-password.age";

  hostSpec = {
    hashedPasswordFile = config.age.secrets.root-password.path;
    rootHashedPasswordFile = config.age.secrets.root-password.path;
  };
}

sops-nix example

# flake.nix inputs
inputs.sops-nix.url = "github:Mic92/sops-nix";
inputs.sops-nix.inputs.nixpkgs.follows = "nixfleet/nixpkgs";

# In your host modules
{inputs, config, ...}: {
  imports = [inputs.sops-nix.nixosModules.sops];

  sops = {
    defaultSopsFile = ./secrets/secrets.yaml;
    # sops-nix also uses age keys - resolvedIdentityPaths works here too
    age.keyFile = builtins.head config.nixfleet.secrets.resolvedIdentityPaths;
  };

  sops.secrets.root-password.neededForUsers = true;

  hostSpec = {
    hashedPasswordFile = config.sops.secrets.root-password.path;
    rootHashedPasswordFile = config.sops.secrets.root-password.path;
  };
}

Extension points

hostSpec provides three options for wiring secrets into the framework:

OptionTypePurpose
secretsPathnullOr strHint for the path to your secrets repo/directory.
hashedPasswordFilenullOr strPath to a hashed password file for the primary user.
rootHashedPasswordFilenullOr strPath to a hashed password file for root.

When hashedPasswordFile or rootHashedPasswordFile is non-null, the core NixOS module sets users.users.<name>.hashedPasswordFile accordingly.

Bootstrapping

New machines need a decryption key before they can decrypt secrets. Two approaches:

–extra-files (nixos-anywhere)

Pass the key during initial install:

mkdir -p /tmp/extra/etc/ssh
cp /path/to/ssh_host_ed25519_key /tmp/extra/etc/ssh/ssh_host_ed25519_key
chmod 600 /tmp/extra/etc/ssh/ssh_host_ed25519_key

nixos-anywhere --flake .#myhost --extra-files /tmp/extra root@192.168.1.50

The build-vm and test-vm apps do this automatically when a key is found at ~/.keys/id_ed25519 or ~/.ssh/id_ed25519. You can also pass a key explicitly with --identity-key PATH. For real hardware, pass --extra-files to nixos-anywhere to inject the key during install.

The secrets scope’s nixfleet-host-key-check service auto-generates the host key at /etc/ssh/ssh_host_ed25519_key on first boot if the key is missing, so bootstrapping without a pre-provisioned key is safe.

Generate on target

SSH into the machine and the host key will be generated automatically by nixfleet-host-key-check before sshd starts. Alternatively, generate one manually and add it to your secrets configuration:

ssh root@192.168.1.50
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

Then extract the public key, add it to your secrets configuration (e.g., secrets.nix for agenix), and re-encrypt the affected secrets.

Key placement on impermanent hosts

On impermanent hosts, the secrets scope automatically persists:

  • /etc/ssh/ssh_host_ed25519_key (and .pub)
  • The user key directory (~/.keys) when enableUserKey is true

The impermanence scope also persists ~/.keys independently, providing defense in depth.