From 363203d9ae35fa38643fa5296f4c2adb48423075 Mon Sep 17 00:00:00 2001 From: Dmitriy Kholkin Date: Wed, 16 Jun 2021 23:42:44 +0300 Subject: [PATCH] move to new secrets module --- machines/AMD-Workstation/default.nix | 1 + machines/Dell-Laptop/default.nix | 1 + machines/NixOS-VM/default.nix | 1 + modules/applications/spotify/default.nix | 9 +- modules/devices.nix | 10 ++ modules/filesystems.nix | 20 ++- modules/secrets-envsubst.nix | 115 +++++++++++++++ modules/secrets.nix | 180 ++++++++++++++++++----- modules/wireguard.nix | 2 +- modules/workspace/gpg.nix | 5 +- profiles/base.nix | 1 + 11 files changed, 293 insertions(+), 52 deletions(-) create mode 100644 modules/secrets-envsubst.nix diff --git a/machines/AMD-Workstation/default.nix b/machines/AMD-Workstation/default.nix index 060a751..07608d6 100644 --- a/machines/AMD-Workstation/default.nix +++ b/machines/AMD-Workstation/default.nix @@ -24,4 +24,5 @@ deviceSpecific.isShared = false; deviceSpecific.isGaming = true; deviceSpecific.enableVirtualisation = true; + deviceSpecific.wireguard.enable = true; } diff --git a/machines/Dell-Laptop/default.nix b/machines/Dell-Laptop/default.nix index da6d6f7..06b6bc0 100644 --- a/machines/Dell-Laptop/default.nix +++ b/machines/Dell-Laptop/default.nix @@ -25,6 +25,7 @@ deviceSpecific.isShared = false; deviceSpecific.isGaming = true; deviceSpecific.enableVirtualisation = false; + deviceSpecific.wireguard.enable = true; boot.blacklistedKernelModules = [ "psmouse" diff --git a/machines/NixOS-VM/default.nix b/machines/NixOS-VM/default.nix index 9f9876d..1c4980c 100644 --- a/machines/NixOS-VM/default.nix +++ b/machines/NixOS-VM/default.nix @@ -25,4 +25,5 @@ deviceSpecific.isShared = false; deviceSpecific.isGaming = false; deviceSpecific.enableVirtualisation = false; + deviceSpecific.wireguard.enable = false; } diff --git a/modules/applications/spotify/default.nix b/modules/applications/spotify/default.nix index 8877309..54105a0 100644 --- a/modules/applications/spotify/default.nix +++ b/modules/applications/spotify/default.nix @@ -54,13 +54,18 @@ in { }; }) { + secrets.spotify = { + owner = "alukard"; + services = [ "spotifyd" ]; + }; + services.spotifyd-user = { enable = true; package = (pkgs.spotifyd.override { withALSA = false; withPulseAudio = true; withPortAudio = false; }); settings = { global = { - username = "${config.secrets.spotify.user}"; - password = "${config.secrets.spotify.password}"; + username = "alukard.files@gmail.com"; + password_cmd = "${pkgs.coreutils}/bin/cat ${config.secrets.spotify.decrypted}"; backend = "pulseaudio"; volume_controller = "softvol"; device_name = "nix"; diff --git a/modules/devices.nix b/modules/devices.nix index d238af4..cc7671c 100644 --- a/modules/devices.nix +++ b/modules/devices.nix @@ -61,6 +61,16 @@ with types; { type = bool; default = config.deviceSpecific.devInfo.drive.type == "ssd"; }; + wireguard = { + enable = mkOption { + type = bool; + default = false; + }; + killswitch = mkOption { + type = bool; + default = true; + }; + }; }; }; } diff --git a/modules/filesystems.nix b/modules/filesystems.nix index 69db99d..dac902d 100644 --- a/modules/filesystems.nix +++ b/modules/filesystems.nix @@ -22,6 +22,14 @@ with deviceSpecific; # monthly = 2; # }; # }; + secrets.samba-windows = { + encrypted = "${config.home-manager.users.alukard.xdg.dataHome}/password-store/samba/windows.gpg"; + services = [ ]; + }; + secrets.samba-linux = { + encrypted = "${config.home-manager.users.alukard.xdg.dataHome}/password-store/samba/linux.gpg"; + services = [ ]; + }; fileSystems = { "/shared/nixos" = lib.mkIf isVM { @@ -53,8 +61,7 @@ with deviceSpecific; fsType = "cifs"; device = "//192.168.0.100/data"; options = [ - "user=${secrets.linux-samba.user}" - "password=${secrets.linux-samba.password}" + "credentials=${secrets.samba-linux.decrypted}" "uid=${toString config.users.users.alukard.uid}" "gid=${toString config.users.groups.users.gid}" "vers=3.0" @@ -84,8 +91,7 @@ with deviceSpecific; fsType = "cifs"; device = "//192.168.0.100/files"; options = [ - "user=${secrets.linux-samba.user}" - "password=${secrets.linux-samba.password}" + "credentials=${secrets.samba-linux.decrypted}" "uid=${toString config.users.users.alukard.uid}" "gid=${toString config.users.groups.users.gid}" "vers=3.0" @@ -104,8 +110,7 @@ with deviceSpecific; fsType = "cifs"; device = "//192.168.0.100/Files"; options = [ - "user=${secrets.windows-samba.user}" - "password=${secrets.windows-samba.password}" + "credentials=${secrets.samba-windows.decrypted}" "uid=${toString config.users.users.alukard.uid}" "gid=${toString config.users.groups.users.gid}" "vers=3.0" @@ -123,8 +128,7 @@ with deviceSpecific; fsType = "cifs"; device = "//192.168.0.100/Data"; options = [ - "user=${secrets.windows-samba.user}" - "password=${secrets.windows-samba.password}" + "credentials=${secrets.samba-windows.decrypted}" "uid=${toString config.users.users.alukard.uid}" "gid=${toString config.users.groups.users.gid}" "vers=3.0" diff --git a/modules/secrets-envsubst.nix b/modules/secrets-envsubst.nix new file mode 100644 index 0000000..e5d05e1 --- /dev/null +++ b/modules/secrets-envsubst.nix @@ -0,0 +1,115 @@ +{ pkgs, config, lib, inputs, ... }: +with lib; +with types; +let + envsubstSecrets = { name, ... }: { + options = { + directory = mkOption { + type = nullOr str; + default = name; + }; + secrets = mkOption { type = listOf str; }; + template = mkOption { type = str; }; + prefix = mkOption { + type = nullOr str; + default = null; + }; + substituted = mkOption { + type = path; + default = "/var/secrets/${name}-envsubst"; + }; + envsubst = mkOption { + type = str; + default = "${pkgs.envsubst}/bin/envsubst -no-unset -no-empty"; + }; + owner = mkOption { + type = str; + default = "root:root"; + }; + permissions = mkOption { + type = lib.types.addCheck lib.types.str + (perm: !isNull (builtins.match "[0-7]{3}" perm)); + default = "400"; + }; + services = mkOption { + type = listOf str; + default = [ "${name}.service" ]; + }; + __toString = mkOption { + readOnly = true; + default = s: s.substituted; + }; + }; + }; + + exportSecrets = name: cfg: + let prefix = lib.optionalString (!isNull cfg.prefix) "${cfg.prefix}_"; + in map (secret: + '' + export ${prefix}${secret}="$(cat ${ + config.secrets."${name}-envsubst-${secret}".decrypted + })"'') cfg.secrets; + + envsubst = name: cfg: + with cfg; { + "${name}-envsubst" = rec { + + requires = [ "user@1000.service" ] + ++ map (secret: "${name}-envsubst-${secret}-secrets.service") + cfg.secrets; + after = requires; + bindsTo = requires; + + preStart = "mkdir -p '${builtins.dirOf substituted}'"; + + script = '' + ${builtins.concatStringsSep "\n" (exportSecrets name cfg)} + + if cat '${ + builtins.toFile "template" template + }' | ${cfg.envsubst} > '${substituted}.tmp'; then + mv -f '${substituted}.tmp' '${substituted}' + chown '${owner}' '${substituted}' + chmod '${permissions}' '${substituted}' + else + echo "Failed to run the substition" + rm '${substituted}.tmp' + exit 1 + fi + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + }; + }; + + addDependencies = name: cfg: + with cfg; + genAttrs services (service: rec { + requires = [ "${name}-envsubst" ]; + after = requires; + bindsTo = requires; + }); + mkServices = name: cfg: [ (envsubst name cfg) (addDependencies name cfg) ]; + + mkIndividualSecrets = name: cfg: + map (x: { + "${name}-envsubst-${x}" = { + encrypted = "${config.home-manager.users.alukard.xdg.dataHome}/password-store/${ + lib.optionalString (!isNull cfg.directory) "${cfg.directory}/" + }${x}.gpg"; + services = [ ]; + }; + }) cfg.secrets; +in { + options.secrets-envsubst = lib.mkOption { + type = attrsOf (submodule envsubstSecrets); + default = { }; + }; + config.systemd.services = + mkMerge (concatLists (mapAttrsToList mkServices config.secrets-envsubst)); + config.secrets = mkMerge + (concatLists (mapAttrsToList mkIndividualSecrets config.secrets-envsubst)); +} diff --git a/modules/secrets.nix b/modules/secrets.nix index c47de11..35b1771 100644 --- a/modules/secrets.nix +++ b/modules/secrets.nix @@ -1,46 +1,146 @@ -{ pkgs, config, lib, ... }: +{ pkgs, config, lib, inputs, ... }: with lib; with types; let - secret = description: - mkOption { - inherit description; - type = nullOr str; + password-store = "${config.home-manager.users.alukard.xdg.dataHome}/password-store"; + secret = { name, ... }: { + options = { + encrypted = mkOption { + type = path; + default = "${password-store}/${name}.gpg"; + }; + decrypted = mkOption { + type = path; + default = "/var/secrets/${name}"; + }; + decrypt = mkOption { + default = pkgs.writeShellScript "gpg-decrypt" '' + set -euo pipefail + export GPG_TTY="$(tty)" + ${pkgs.gnupg}/bin/gpg-connect-agent updatestartuptty /bye 1>&2 + ${pkgs.gnupg}/bin/gpg --batch --no-tty --decrypt + ''; + }; + user = mkOption { + type = str; + default = "alukard"; + }; + owner = mkOption { + type = str; + default = "root:root"; + }; + permissions = mkOption { + type = lib.types.addCheck lib.types.str + (perm: !isNull (builtins.match "[0-7]{3}" perm)); + default = "400"; + }; + services = mkOption { + type = listOf str; + default = [ "${name}" ]; + }; + __toString = mkOption { + readOnly = true; + default = s: s.decrypted; + }; + }; + }; + + decrypt = name: cfg: + with cfg; { + "${name}-secrets" = rec { + + requires = [ "user@1000.service" ]; + after = requires; + + preStart = '' + stat '${encrypted}' + mkdir -p '${builtins.dirOf decrypted}' + ''; + + script = '' + if cat '${encrypted}' | /run/wrappers/bin/sudo -u ${user} ${cfg.decrypt} > '${decrypted}.tmp'; then + mv -f '${decrypted}.tmp' '${decrypted}' + chown '${owner}' '${decrypted}' + chmod '${permissions}' '${decrypted}' + else + echo "Failed to decrypt the secret" + rm '${decrypted}.tmp' + exit 1 + fi + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + }; + }; + + addDependencies = name: cfg: + with cfg; + genAttrs services (service: rec { + requires = [ "${name}-secrets.service" ]; + after = requires; + bindsTo = requires; + }); + + mkServices = name: cfg: [ (decrypt name cfg) (addDependencies name cfg) ]; + + allServices = toString (map (name: "${name}-envsubst.service") + (builtins.attrNames config.secrets-envsubst) + ++ map (name: "${name}-secrets.service") + (builtins.attrNames config.secrets)); +in { + options.secrets = lib.mkOption { + type = attrsOf (submodule secret); + default = { }; + }; + + options.secretsConfig = { + repo = lib.mkOption { + type = str; + default = "ssh://git@github.com/AlukardBF/pass"; + }; + }; + + config.systemd.services = + mkMerge (concatLists (mapAttrsToList mkServices config.secrets)); + + config.environment.systemPackages = [ + (pkgs.writeShellScriptBin "activate-secrets" '' + set -euo pipefail + # Make sure card is available and unlocked + # echo fetch | gpg --card-edit --no-tty --command-fd=0 + # ${pkgs.gnupg}/bin/gpg --card-status + if [ -d "${password-store}/.git" ]; then + cd "${password-store}"; ${pkgs.git}/bin/git pull + else + ${pkgs.git}/bin/git clone ${lib.escapeShellArg config.secretsConfig.repo} "${password-store}" + fi + ln -sf ${ + pkgs.writeShellScript "push" "${pkgs.git}/bin/git push origin master" + } "${password-store}/.git/hooks/post-commit" + cat ${password-store}/spotify.gpg | ${pkgs.gnupg}/bin/gpg --decrypt > /dev/null + sudo systemctl restart ${allServices} + '') + ]; + + config.security.sudo.extraRules = [{ + users = [ "alukard" ]; + commands = [{ + command = "/run/current-system/sw/bin/systemctl restart ${allServices}"; + options = [ "NOPASSWD" ]; + }]; + }]; + + config.home-manager.users.alukard = { + xsession.windowManager.i3 = { + config.startup = [{ command = "activate-secrets"; }]; + }; + programs.password-store = { + enable = true; + package = pkgs.pass-nodmenu; + settings.PASSWORD_STORE_DIR = password-store; }; - mkCredOption = service: extra: - mkOption { - description = "Credentials for ${service}"; - type = nullOr (submodule { - options = { - user = mkOption { - type = str; - description = "Username for ${service}"; - }; - password = mkOption { - type = str; - description = "Password for ${service}"; - }; - } // extra; - }); - }; -in rec { - options.secrets = { - wireguard = mkOption { - description = "Wireguard conf"; - type = attrs; - }; - windows-samba = mkCredOption "samba on windows" { }; - linux-samba = mkCredOption "samba on linux" { }; - spotify = mkCredOption "Spotify" { }; }; - config = let - unlocked = import (pkgs.runCommand "check-secret" { } - "set +e; grep -qI . ${../secret.nix}; echo $? > $out") == 0; - secretnix = import ../secret.nix; - secrets = if !unlocked || isNull secretnix then - builtins.trace "secret.nix locked, building without any secrets" - (mapAttrs (n: v: null) options.secrets) - else - secretnix; - in { inherit secrets; }; } diff --git a/modules/wireguard.nix b/modules/wireguard.nix index 236f24d..e385982 100644 --- a/modules/wireguard.nix +++ b/modules/wireguard.nix @@ -1,7 +1,7 @@ { config, pkgs, lib, ... }: with lib; let - cfg = config.secrets.wireguard.${config.device}; + cfg = config.deviceSpecific.wireguard; kernel = config.boot.kernelPackages; in { config = mkIf cfg.enable { diff --git a/modules/workspace/gpg.nix b/modules/workspace/gpg.nix index a921c23..b11a4ff 100644 --- a/modules/workspace/gpg.nix +++ b/modules/workspace/gpg.nix @@ -9,7 +9,10 @@ enable = true; enableSshSupport = true; pinentryFlavor = "gnome3"; - sshKeys = [ "E6A6377C3D0827C36428A290199FDB3B91414AFE" ]; + sshKeys = [ + # "E6A6377C3D0827C36428A290199FDB3B91414AFE" + "7A7130ABF128CC2C32B3D6AD27515056B0193CE1" + ]; }; }; } \ No newline at end of file diff --git a/profiles/base.nix b/profiles/base.nix index 4a10f70..e3c84ce 100644 --- a/profiles/base.nix +++ b/profiles/base.nix @@ -14,6 +14,7 @@ nix overlay secrets + secrets-envsubst security ssh xdg