DEV Community

Kir Axanov
Kir Axanov

Posted on

NixOS. Managing secrets with sops-nix

Hi! Here are some notes about how to use secrets in NixOS configuration securely. As of now I use sops-nix with simple age keys (not generated from ssh keys).

Concept

Image description

We will generate a file with all our secrets (e.g. secrets.yaml). For example:

my_secret_auth: <plain-text secret>
Enter fullscreen mode Exit fullscreen mode

secrets.yaml is stored encrypted. We can edit it with sops secrets.yaml which decrypts it and opens your editor from the $EDITOR environment variable.

For this encryption / decryption sops uses an age public and private keys - we generate those too.

For those secrets to be picked up by nixos we register them in nixos config (we can set secret's owner to allow access for needed service):

sops.secrets = {
  my_secret_auth = {
    owner = "nginx";
  };
};
Enter fullscreen mode Exit fullscreen mode

In the nixos configuration we now can use those secrets like so (an example for nginx basic auth):

basicAuthFile = config.sops.secrets.my_secret_auth.path;
Enter fullscreen mode Exit fullscreen mode

That's basically it. Below are some details on how to setup all of this.

Local secrets with nixos-rebuild

For use on a local machine. We will use niv to install sops-nix.

1) Go to your configuration directory, init a niv project with sops-nix:

# where `configuration.nix` is located, adjust if needed
cd /etc/nixos

# init `niv` with `sops-nix` before installing anything
nix-shell -p niv --run "niv init && niv add Mic92/sops-nix"
Enter fullscreen mode Exit fullscreen mode

2) Create the directory for age keys:

mkdir -p ~/.config/sops/age
Enter fullscreen mode Exit fullscreen mode

3) Configure sops and other stuff in secrets.nix (you can choose another name, don't forget to import this file from your configuration), replace YOUR_USER:

{ pkgs, config, ... }:

{
  imports = [ "${(import ./nix/sources.nix).sops-nix}/modules/sops" ];

  environment.systemPackages = with pkgs; [
    age sops
  ];

  sops = {
    defaultSopsFile = ./secrets/secrets.yaml;
    defaultSopsFormat = "yaml";

    age.keyFile = "/home/${config.users.users.YOUR_USER.name}/.config/sops/age/keys.txt";
  };
}
Enter fullscreen mode Exit fullscreen mode

4) Apply configuration above:

sudo nixos-rebuild switch
Enter fullscreen mode Exit fullscreen mode

5) Generate an age key:

age-keygen -o ~/.config/sops/age/keys.txt
Enter fullscreen mode Exit fullscreen mode

6) Create .sops.yaml and paste public key printed on the previous step (replace capslocked text), use arbitrarily username:

keys:
  - &USERNAME PUBLIC_KEY
creation_rules:
  - path_regex: secrets/secrets.yaml$
    key_groups:
    - age:
      - *USERNAME
Enter fullscreen mode Exit fullscreen mode

7) Register secrets to secrets.nix from step 3:

...
sops = {
  # ...
  secrets = {
    my_secret_auth = {
      owner = "nginx";
    };
  };
}
Enter fullscreen mode Exit fullscreen mode

8) Use your secrets in nixos configuration (where the option expects a file path to the secret):

# ...
basicAuthFile = config.sops.secrets.my_secret_auth.path;
# ...
Enter fullscreen mode Exit fullscreen mode

Remote secrets with flake

For use on a remote machine.

DISCLAIMER: I keep remote's configs locally and send them via scp when updating it.

1) Add this to your flake:

{
  inputs = {
    # ...
    sops-nix.url = "github:Mic92/sops-nix";  # <--
  };

  outputs = {
    # ...
    sops-nix,  # <--
    ...
  }@inputs:  # <--
  {
    nixosConfigurations.your_flake_name = nixpkgs.lib.nixosSystem {
      # ...
      specialArgs = { inherit inputs; };  # <--
      modules = [
        # ...
        sops-nix.nixosModules.sops  # <--
      ];
    };
  };
}
Enter fullscreen mode Exit fullscreen mode

2) Create the directory for age keys:

mkdir -p ~/.config/sops/age
Enter fullscreen mode Exit fullscreen mode

3) Configure sops and other stuff in secrets.nix (you can choose another name, don't forget to import this file from your configuration), replace YOUR_USER:

{ pkgs, config, inputs, ... }:

{
  imports = [ inputs.sops-nix.nixosModules.sops ];  # <-- differs from the local setup

  environment.systemPackages = with pkgs; [
    age sops
  ];

  sops = {
    defaultSopsFile = ./secrets/secrets.yaml;
    defaultSopsFormat = "yaml";

    age.keyFile = "/home/${config.users.users.YOUR_USER.name}/.config/sops/age/keys.txt";
  };
}
Enter fullscreen mode Exit fullscreen mode

4) Apply configuration above:

# I'm copying the configuration via ssh and running update on a remote - you do you
nixos-rebuild switch --flake .#update
Enter fullscreen mode Exit fullscreen mode

5) Generate an age key:

age-keygen -o ~/.config/sops/age/keys.txt
Enter fullscreen mode Exit fullscreen mode

6) Create .sops.yaml and paste public key printed on the previous step (replace capslocked text), use arbitrarily username:

keys:
  - &USERNAME PUBLIC_KEY
creation_rules:
  - path_regex: secrets/secrets.yaml$
    key_groups:
    - age:
      - *USERNAME
Enter fullscreen mode Exit fullscreen mode

7) Register secrets to secrets.nix from step 3:

...
sops = {
  ...
  secrets = {
    my_secret_auth = {
      owner = "nginx";
    };
  };
  ...
Enter fullscreen mode Exit fullscreen mode

8) Use your secrets in nixos configuration (where the option expects a file path to the secret):

...
basicAuthFile = config.sops.secrets.my_secret_auth.path;
...
Enter fullscreen mode Exit fullscreen mode

9) If you use sops on both local and remote, it's useful to copy age private key from the remote machine to your home keys.txt (so you'll have two keys there) - this way you can edit remote's secrets.yaml locally.

Resources

Top comments (0)