From b9067e7f1315d7f61c74c1578a1f8398b7e40a94 Mon Sep 17 00:00:00 2001 From: niten Date: Sun, 17 Oct 2021 22:10:26 -0700 Subject: [PATCH] Add initial support for encrypted filesystems. --- lib/default.nix | 1 + lib/fudo/host-filesystems.nix | 69 +++++++++++++++++++++++++++++++++++ lib/fudo/secrets.nix | 15 ++++++++ lib/types/host.nix | 59 ++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 lib/fudo/host-filesystems.nix diff --git a/lib/default.nix b/lib/default.nix index a54d000..7bd9165 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -20,6 +20,7 @@ with lib; { ./fudo/global.nix ./fudo/grafana.nix ./fudo/hosts.nix + ./fudo/host-filesystems.nix ./fudo/initrd-network.nix ./fudo/ipfs.nix ./fudo/kdc.nix diff --git a/lib/fudo/host-filesystems.nix b/lib/fudo/host-filesystems.nix new file mode 100644 index 0000000..897ec40 --- /dev/null +++ b/lib/fudo/host-filesystems.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + hostname = config.instance.hostname; + host-filesystems = config.fudo.hosts.${hostname}.encrypted-filesystems; +in { + config = { + fileSystems = mapAttrs' (filesystem-name: opts: + nameValuePair opts.target-path + { + device = "/dev/mapper/${filesystem-name}"; + fsType = opts.filesystem-type; + options = opts.filesystem-options; + }) + host-filesystems; + + systemd = { + mounts = let + filesystems = mapAttrstoList + (fs: opts: { filesystem = fs; opts = opts; }) + host-filesystems; + + mounts = concatMap + (fs: mapAttrsToList + (mp: mp-opts: + nameValuePair "${fs.filesystem}-${mp}-mount" { + what = "/dev/mapper/${fs.filesystem}"; + type = fs.opts.filesystem-type; + where = mp; + options = concatStringsSep "," (fs.opts.options ++ mp-opts.options); + wantedBy = [ "default.target" ]; + description = "${fs.opts.filesystem-type} filesystem on ${filesystem} mounted to ${mp}"; + requires = [ "${fs.filesystem}-decrypt.service" ]; + }) + fs.opts.mountpoints) + filesystems; + in mounts; + + services = mapAttrs' (filesystem-name: opts: + nameValuePair "${filesystem-name}-decrypt" + { + wantedBy = [ "default.target" ]; + description = "Decrypt the ${filesystem-name} filesystem when the key is available at ${opts.key-path}"; + path = with pkgs; [ cryptsetup ]; + serviceConfig = { + ExecStart = pkgs.writeShellScript "decrypt-${filesystem-name}.sh" '' + cryptsetup open --type luks --key-file ${opts.key-path} ${opts.device} ${filesystem-name} + ''; + ExecStop = pkgs.writeShellScript "close-${filesystem-name}.sh" '' + cryptsetup close /dev/mapper/${filesystem-name} + ''; + }; + }) + host-filesystems; + + paths = mapAttrs' (filesystem-name: opts: + nameValuePair "${filesystem-name}-decrypt" + { + wantedBy = [ "default.target" ]; + description = "Watch for decryption key, then decrypt the target filesystem."; + pathConfig = { + PathExists = opts.key-path; + Service = "${filesystem-name}-decrypt.service"; + }; + }) host-filesystems; + }; + }; +} diff --git a/lib/fudo/secrets.nix b/lib/fudo/secrets.nix index 35c80bb..0ca12a9 100644 --- a/lib/fudo/secrets.nix +++ b/lib/fudo/secrets.nix @@ -102,6 +102,21 @@ in { default = { }; }; + host-deep-secrets = mkOption { + type = attrsOf (attrsOf (submodule secretOpts)); + description = '' + Secrets that are only passed during deployment. + + These secrets will be passed as nixops deployment secrets, + _unlike_ regular secrets that are passed to hosts as part of + the nixops store, but encrypted with the host SSH key. Regular + secrets are kept secret from normal users. These secrets will + be kept secret from _everybody_. However, they won't be + available on the host at boot until a new deployment occurs. + ''; + default = { }; + }; + secret-users = mkOption { type = listOf str; description = "List of users with read-access to secrets."; diff --git a/lib/types/host.nix b/lib/types/host.nix index cc3f0cf..f9fd7c7 100644 --- a/lib/types/host.nix +++ b/lib/types/host.nix @@ -2,6 +2,55 @@ with lib; rec { + encryptedFilesystemOpts = { ... }: let + mountpoint = { mp, ... }: { + options = with types; { + mountpoint = mkOption { + type = str; + description = "Path at which to mount the filesystem."; + default = mp; + }; + + options = mkOption { + type = listOf str; + description = "List of filesystem options specific to this mountpoint (eg: subvol)."; + }; + }; + }; + in { + options = with types; { + device = mkOption { + type = str; + description = "Path to the encrypted device."; + }; + + key-path = mkOption { + type = str; + description = '' + Path at which to locate the key file. + + The filesystem will be decrypted and mounted once available."; + ''; + }; + + filesystem-type = mkOption { + type = str; + description = "Filesystem type of the decrypted filesystem."; + }; + + options = mkOption { + type = str; + description = "List of filesystem options with which to mount."; + }; + + mountpoints = mkOption { + type = attrsOf (submodule mountpoint); + description = "A map of mountpoints for this filesystem to fs options. Multiple to support btrfs."; + default = {}; + }; + }; + }; + masterKeyOpts = { ... }: { options = with types; { key-path = mkOption { @@ -177,6 +226,12 @@ rec { android-dev = mkEnableOption "Enable ADB on the host."; + encrypted-filesystems = mkOption { + type = attrsOf (submodule encrypteFSOpts); + description = "List of encrypted filesystems to mount on the local host when the key is available."; + default = { }; + }; + initrd-network = let keypair-type = { ... }: { options = { @@ -202,6 +257,10 @@ rec { type = (submodule keypair-type); description = "SSH host key pair to use for initrd."; }; + interface = mkOption { + type = str; + description = "Name of interface on which to listen for connections."; + }; }; };