this post was submitted on 14 Nov 2023
2 points (100.0% liked)

Nix

10 readers
1 users here now

founded 1 year ago
MODERATORS
top 7 comments
sorted by: hot top controversial new old
[–] [email protected] 1 points 1 year ago (1 children)

In a previous comment thread someone asked me if I could talk about sops-nix in comparison to agenix, so here is a write-up on the different approaches for handling secrets in NixOS and when I think each of them is appropriate (with lots of example code!)

[–] [email protected] 1 points 1 year ago

Thanks for writing this up!

[–] [email protected] 1 points 1 year ago (1 children)

I treat secrets like dependency injection: You don't make a thing that knows how to connect to the database / knows a secret / knows how to get a secret. Instead, you make a thing that takes an argument that is a database connection / secret. You bind late — at execution time. This keeps things very simple and needs no special frameworks / libraries / secrets-tools.

Concrete example:

  • Passing a secret to a VM
  • by wrapping it in a script
  • that copies the secret into an ephemeral filesystem image
  • that the VM mounts

In demo.nix:

{ pkgs ? import  { }, }:
let
  vmConfiguration = { lib, modulesPath, ... }: {
    imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ];
    config = {
      networking.hostName = "demo";
      system.stateVersion = lib.versions.majorMinor lib.version; # Ephemeral

      boot.initrd.availableKernelModules = [ "iso9660" ];
      fileSystems = lib.mkVMOverride {
        "/suitcase" = {
          device = "/dev/disk/by-label/suitcase";
          fsType = "iso9660";
          options = [ "ro" ];
        };
      };

      systemd.services.demo-secret-access = {
        description = "Demonstrate access to secret";
        wants = [ "suitcase.mount" ];
        after = [ "suitcase.mount" ];
        wantedBy = [ "multi-user.target" ];
        script = ''
          echo "Demo: The secret is: $(cat /suitcase/secret)" >&2
        '';
      };
    };
  };

  vmWrapper = { nixos, cdrtools, writeShellApplication, }:
    writeShellApplication {
      name = "demo";
      runtimeInputs = [ cdrtools ];
      text = ''
        if (( $# < 1 ));then
          echo usage: demo suitcase_path ... >&2
          exit 1
        fi
        if [[ ! -d "$1" ]];then
          echo Expected first argument to be a directory >&2
          exit 1
        fi

        suitcase_contents=$(realpath "$1")
        shift

        d=
        trap '[[ "$d" && -e "$d" ]] && rm -r "$d"' EXIT
        d=$(mktemp -d)
        cd "$d"

        (umask 077; mkisofs -R -uid 0 -gid 0 -V suitcase -o suitcase.iso "$suitcase_contents")

        ${(nixos vmConfiguration).config.system.build.vm}/bin/run-demo-vm \
          -drive file="$d/suitcase.iso",format=raw,id=suitcase,if=none,read-only=on,werror=report \
          -device virtio-blk-pci,drive=suitcase \
          "$@"
      '';
    };

in pkgs.callPackage vmWrapper { }

Use:

$ mkdir foo
$ (umask 077; echo hello > foo/secret)
$ $(nix-build demo.nix)/bin/demo foo

and the VM logs:

Nov 15 01:31:27 demo demo-secret-access-start[639]: Demo: The secret is: hello
[–] [email protected] 1 points 1 year ago (1 children)

Isn't sops / agenix basically the same thing except instead of you manually putting the secret in foo/secret it's stored encrypted in the Git repo and then it automatically decrypts it at execution time into /var/wherever?

[–] [email protected] 1 points 1 year ago (1 children)
[–] [email protected] 1 points 1 year ago

I've spent a significant amount of time over the past two weeks evaluating the differences and pros/cons between agenix and sops-nix. And a bit of time looking over your wall of text.

The reality is that your thinking/comic is completely backwards.

[–] [email protected] 1 points 1 year ago

One thing I feel like is missing here, is that both agenix and sops-nix only work at system activation time. Any secret that needs to be used before the system boots can't be encrypted using those solutions. For example the fido2 credential used for decrypting your luks partition, if that partition also happens to be the root partition. A nice overview otherwise.