NixOS anywhere: failing to deploy secrets

I have been stuck for 2 days trying to correctly deploy secrets installing NixOS using nixos-anywhere. All I’d like to achieve is declaratively setting passwords for my user and root.

Relevant sections of my config:

# configuration.nix
{
    users = {
        mutableUsers = false;
        users = {
            sh4k0 = {
                isNormalUser = true;
                extraGroups = [
                    "keys"
                ];
                hashedPasswordFile = config.sops.secrets."passwords/sh4k0".path;
           };
           root = {
               hashedPasswordFile = config.sops.secrets."passwords/root".path;
           };
       };
    };

    sops = {
        age.keyFile = "/etc/sops/age/cetus.txt";
        defaultSopsFile = ../../secrets/hosts/cetus/secrets.yaml;
        secrets = {
            "passwords/sh4k0" = {};   # my password
            "passwords/root" = {};    # root password
        };
    };
}

secrets.yaml:

passwords:
    sh4k0: ENC[...]
    root: ENC[...]

Shell script with which I deploy the configuration:

#!/usr/bin/env bash

# Create a temporary directory
temp=$(mktemp -d)

# Function to cleanup temporary directory on exit
cleanup() {
  rm -rf "$temp"
}
trap cleanup EXIT

# Create the directory where sshd expects to find the host keys
install -d -m755 "$temp/etc/sops/age"

# Decrypt your private key from the password store and copy it to the temporary directory
cp /home/sh4k0/.config/sops/age/cetus.txt "$temp"/etc/sops/age/cetus.txt

# Set the correct permissions so sshd will accept the key
chmod 600 "$temp"/etc/sops/age/cetus.txt

# Install NixOS to the host system with our secrets
nixos-anywhere \
  --flake ~/.repos/nixcfg#cetus \
  --extra-files "$temp" \
  --target-host root@192.168.88.62 \
  --build-on-remote \
  --build-on remote \
  --generate-hardware-config nixos-generate-config ~/.repos/nixcfg/hosts/cetus/hardware-configuration.nix

What happens:

The installation goes without error nor warning, and the nixos-anywhere logs are clean.
The key is where it should be on the newly installed host (in /etc/sops/age/cetus.txt), and has correct permissions (600, owned by root). However secrets aren’t anywhere in /run/secrets.d (where I’d expect to find them).
Upon rebooting the freshly installed machine, I briefly see the confirmation of this:

warning: password file '/run/secrets/passwords/root' does not exist
warning: password file '/run/secrets/passwords/sh4k0' does not exist

The only thing I see is a file /run/secrets.d/age-keys.txt on the new host, which I don’t mention anywhere in my config.
I use sops-nix successfully in a very similar manner on a different host (although with the home-manager module), the only difference being that I rebuild it locally, so there is probably something I’m doing wrong when it comes to deploying the configuration over ssh. Anyone has a hint?

What I’m missing in your config is the needeForUsers = true; for the secrets. I wonder if that is the only thing that is missing.

@eblechschmidt robably not. :slight_smile:
It is not quite clear to me what should be the workflow to set up secrets on a new host (using nixos-anywhere, in this case), and I am likely doing something wrong.

Let’s assume I am installing NixOS from host A to host B.
What I’m doing is: generating an age key on host A and manually transferring it to host B (see shell script above):

…to the location where sops expects to find it (see configuration.nix above):

From my understanding, this should suffice in allowing secrets to be decrypted on the new host upon installing/rebuilding.

Indeed, the key is copied to the correct location in the new host. But for some reason, sops-nix is unable to use it to decrypt secrets.

As stated above, the age key is correctly transfered to the new host in the location specified by the script, but also there is a /run/secrets.d/age-keys.txt which I don’t mention anywhere in my config. Perhaps is it created automatically by nixos-anywhere from an existing ssh key? So should I transfer an ssh key, rather than an age key?

What I normally do is:

  1. Make a basic install (until now with the standard installer and not with nixos-anywhere) and let the new system generate ssh keys for me.
  2. Derive an age key from the ssh keys and use this to encrypt my secrets.
  3. Deploy the final config including secrets to the new machine

Tbh. I haven’t used nixos-anywhere so far so I might not be much help :wink:

Yes, I know that might work, but I’d like to do everything in one step via ssh…

I figured it out. For some reason, transferring an age key to the target host would result in failure. Instead, I transferred a private ssh key, almost copy-pasting the script in nixos-anywhere/docs/howtos/secrets.md at 73b80b8638212642e212408443582b0b9b4d121e · nix-community/nixos-anywhere · GitHub.

Then, I would instruct sops-nix to import the ssh key specified above:

  sops = {
    age.sshKeyPaths = [ "/etc/ssh/mykey_ed25519" ];
  };

Obviously, the corresponding age key needs to be added to the recipients in .sops.yaml.

Doing all of that, I can deploy new hosts with correctly configured secrets simply by ssh-ing into the installer and running a single nixos-anywhere invocation from another host :slight_smile:

1 Like

I am not using a script I did set the path of the age key into

age.keyFile = "/keys.txt";

Then I execute from my pc

nix run github:nix-community/nixos-anywhere/1.9.0  -- --flake .#<...> --extra-files /your_path/sops-kvm root@<ip of target>

with keys.txt being inside of /your_path/sops-kvm

1 Like

If you want to figure out why this is happening, you could grab the activation script logs, it’s likely that there is some issue with how you’re specifying that key.

(Apparently these aren’t logged anywhere with scripted initrd, but if you have systemd initrd enabled, you should be able to journalctl -u initrd-nixos-activation to see the activation script’s output.)

1 Like