From 65e1a9af563d4f6a225d4711995a7c0de1fc3b2a Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 12 Sep 2021 16:30:18 +0200 Subject: [PATCH 1/3] home-manager: create a prototype module for user secret decryption --- flake.nix | 1 + modules/hm-age.nix | 115 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 modules/hm-age.nix diff --git a/flake.nix b/flake.nix index 02221f1..0449392 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,7 @@ in { nixosModules.age = import ./modules/age.nix; + homeManagerModules.age = import ./modules/hm-age.nix; overlay = import ./overlay.nix; diff --git a/modules/hm-age.nix b/modules/hm-age.nix new file mode 100644 index 0000000..e96e4f8 --- /dev/null +++ b/modules/hm-age.nix @@ -0,0 +1,115 @@ +{ config, options, lib, pkgs, ... }: + +with lib; + +let + cfg = config.age; + + # we need at least rage 0.5.0 to support ssh keys + rage = + if lib.versionOlder pkgs.rage.version "0.5.0" + then pkgs.callPackage ../pkgs/rage.nix { } + else pkgs.rage; + ageBin = "${rage}/bin/rage"; + + identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.sshKeyPaths); + installSecret = secretType: '' + echo "decrypting ${secretType.file} to ${secretType.path}..." + TMP_FILE="${secretType.path}.tmp" + $DRY_RUN_CMD mkdir $VERBOSE_ARG -p $(dirname ${secretType.path}) + ( + umask u=r,g=,o= + $DRY_RUN_CMD ${ageBin} --decrypt ${identities} -o "$TMP_FILE" "${secretType.file}" + ) + $DRY_RUN_CMD chmod $VERBOSE_ARG ${secretType.mode} "$TMP_FILE" + $DRY_RUN_CMD chown $VERBOSE_ARG ${secretType.owner}:${secretType.group} "$TMP_FILE" + $DRY_RUN_CMD mv $VERBOSE_ARG -f "$TMP_FILE" "${secretType.path}" + ''; + + isRootSecret = st: (st.owner == "root" || st.owner == "0") && (st.group == "root" || st.group == "0"); + isNotRootSecret = st: !(isRootSecret st); + + rootOwnedSecrets = builtins.filter isRootSecret (builtins.attrValues cfg.secrets); + installRootOwnedSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting root secrets...'" ] ++ (map installSecret rootOwnedSecrets)); + + nonRootSecrets = builtins.filter isNotRootSecret (builtins.attrValues cfg.secrets); + installNonRootSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting non-root secrets...'" ] ++ (map installSecret nonRootSecrets)); + + secretType = types.submodule ({ config, ... }: { + options = { + name = mkOption { + type = types.str; + default = config._module.args.name; + description = '' + Name of the file used in /run/user//secrets + ''; + }; + file = mkOption { + type = types.path; + description = '' + Age file the secret is loaded from. + ''; + }; + path = mkOption { + type = types.str; + default = "/run/user/$UID/secrets/${config.name}"; + description = '' + Path where the decrypted secret is installed. + ''; + }; + mode = mkOption { + type = types.str; + default = "0400"; + description = '' + Permissions mode of the in octal. + ''; + }; + owner = mkOption { + type = types.str; + default = "$UID"; + description = '' + User of the file. + ''; + }; + group = mkOption { + type = types.str; + default = "$(id -g)"; + description = '' + Group of the file. + ''; + }; + }; + }); +in +{ + options.age = { + secrets = mkOption { + type = types.attrsOf secretType; + default = { }; + description = '' + Attrset of secrets. + ''; + }; + sshKeyPaths = mkOption { + type = types.listOf types.path; + default = []; + #if config.services.openssh.enable then + # map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys) + #else [ ]; + description = '' + Path to SSH keys to be used as identities in age decryption. + ''; + }; + }; + config = mkIf (cfg.secrets != { }) (mkMerge [ + + { + assertions = [{ + assertion = cfg.sshKeyPaths != [ ]; + message = "age.sshKeyPaths must be set."; + }]; + + home.activation.agenix = hm.dag.entryAfter [ "writeBoundary" ] installNonRootSecrets; + } + ]); +} From 8926dcc3aca56ca421de6d5072205b071d0c252f Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 10 Oct 2021 18:14:12 +0200 Subject: [PATCH 2/3] hm: cleanup root/non-root stuff for Home Manager module --- modules/hm-age.nix | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/hm-age.nix b/modules/hm-age.nix index e96e4f8..4b0bde8 100644 --- a/modules/hm-age.nix +++ b/modules/hm-age.nix @@ -13,12 +13,13 @@ let ageBin = "${rage}/bin/rage"; identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.sshKeyPaths); + installSecret = secretType: '' echo "decrypting ${secretType.file} to ${secretType.path}..." TMP_FILE="${secretType.path}.tmp" $DRY_RUN_CMD mkdir $VERBOSE_ARG -p $(dirname ${secretType.path}) ( - umask u=r,g=,o= + $DRY_RUN_CMD umask u=r,g=,o= $DRY_RUN_CMD ${ageBin} --decrypt ${identities} -o "$TMP_FILE" "${secretType.file}" ) $DRY_RUN_CMD chmod $VERBOSE_ARG ${secretType.mode} "$TMP_FILE" @@ -26,14 +27,7 @@ let $DRY_RUN_CMD mv $VERBOSE_ARG -f "$TMP_FILE" "${secretType.path}" ''; - isRootSecret = st: (st.owner == "root" || st.owner == "0") && (st.group == "root" || st.group == "0"); - isNotRootSecret = st: !(isRootSecret st); - - rootOwnedSecrets = builtins.filter isRootSecret (builtins.attrValues cfg.secrets); - installRootOwnedSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting root secrets...'" ] ++ (map installSecret rootOwnedSecrets)); - - nonRootSecrets = builtins.filter isNotRootSecret (builtins.attrValues cfg.secrets); - installNonRootSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting non-root secrets...'" ] ++ (map installSecret nonRootSecrets)); + installSecrets = builtins.concatStringsSep "\n" (["echo '[agenix] decrypting user secrets...'" ] ++ (map installSecret cfg.secrets)); secretType = types.submodule ({ config, ... }: { options = { @@ -90,6 +84,10 @@ in Attrset of secrets. ''; }; + sshAskpass = mkOption { + type = types.str; + default = "${pkgs.ssh-askpass-fullscreen}/bin/ssh-askpass-fullscreen"; + }; sshKeyPaths = mkOption { type = types.listOf types.path; default = []; @@ -109,7 +107,7 @@ in message = "age.sshKeyPaths must be set."; }]; - home.activation.agenix = hm.dag.entryAfter [ "writeBoundary" ] installNonRootSecrets; + home.activation.agenix = hm.dag.entryAfter [ "writeBoundary" ] installSecrets; } ]); } From 1c11ad7b11a5b09c28b7892fbbe984274e7031ed Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 10 Oct 2021 18:14:47 +0200 Subject: [PATCH 3/3] hm: remove sshAskpass --- modules/hm-age.nix | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/hm-age.nix b/modules/hm-age.nix index 4b0bde8..af8599b 100644 --- a/modules/hm-age.nix +++ b/modules/hm-age.nix @@ -84,10 +84,6 @@ in Attrset of secrets. ''; }; - sshAskpass = mkOption { - type = types.str; - default = "${pkgs.ssh-askpass-fullscreen}/bin/ssh-askpass-fullscreen"; - }; sshKeyPaths = mkOption { type = types.listOf types.path; default = [];