• chkno@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    10 months ago

    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
    
  • EhLlie@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    10 months 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.