aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/apps/gnupg/default.nix114
-rw-r--r--modules/apps/gnupg/instance.nix68
2 files changed, 182 insertions, 0 deletions
diff --git a/modules/apps/gnupg/default.nix b/modules/apps/gnupg/default.nix
new file mode 100644
index 0000000..7330b2a
--- /dev/null
+++ b/modules/apps/gnupg/default.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+ inherit (lib)
+ concatMapStringsSep concatStrings filterAttrs foldAttrs mapAttrs'
+ mapAttrsToList mkOption optionalString ;
+
+ inherit (lib.types)
+ attrsOf submodule ;
+
+ explicit = filterAttrs (n: v: n != "_module" && v != null);
+ instances = explicit config.nixsap.apps.gnupg;
+
+ keyrings =
+ let
+ ik = mapAttrsToList (_: i: {
+ "${i.user}" = i.secretKeys ++ mapAttrsToList (_: f: f) i.passphrase;
+ }) instances;
+ in foldAttrs (l: r: l ++ r) [] ik;
+
+
+ mkService = name: cfg:
+ let
+
+ pubring = pkgs.runCommand "gnupg-${name}-pubring.kbx" {} ''
+ ${cfg.package}/bin/gpg2 \
+ --homedir . \
+ --import \
+ ${concatMapStringsSep " " (k: "'${k}'") cfg.publicKeys}
+ cp pubring.kbx $out
+ '';
+
+ start = pkgs.writeBashScriptBin "gnupg-${name}-start" ''
+ set -euo pipefail
+ umask 0077
+
+ cat <<'CONF' > '${cfg.home}/gpg.conf'
+ batch
+ no-tty
+ trust-model always
+ yes
+ CONF
+
+ # XXX forking:
+ ${cfg.package}/bin/gpg-agent \
+ --homedir '${cfg.home}' \
+ --allow-preset-passphrase \
+ --batch \
+ --quiet \
+ --daemon
+
+ ${optionalString (cfg.publicKeys != []) ''
+ ln -sf '${pubring}' '${cfg.home}/pubring.kbx'
+ ''}
+
+ export GNUPGHOME='${cfg.home}'
+
+ ${optionalString (cfg.secretKeys != []) ''
+ ${cfg.package}/bin/gpg2 --import \
+ ${concatMapStringsSep " " (k: "'${k}'") cfg.secretKeys}
+ ''}
+
+
+ ${concatStrings (mapAttrsToList (cacheid: f: ''
+ head -n 1 '${f}' \
+ | ${cfg.package}/libexec/gpg-preset-passphrase \
+ --verbose --preset '${cacheid}'
+ '') cfg.passphrase)
+ }
+
+
+ '';
+
+ in {
+ name = "gnupg-${name}";
+ value = {
+ description = "gnupg (${name})";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "keys.target" "local-fs.target" ];
+ preStart = ''
+ mkdir -p -- '${cfg.home}'
+ rm -rf -- '${cfg.home}/'*
+ chmod u=rwX,g=,o= -- '${cfg.home}'
+ chown '${cfg.user}.${cfg.user}' -- '${cfg.home}'
+ '';
+
+ serviceConfig = {
+ ExecStart = "${start}/bin/gnupg-${name}-start";
+ PermissionsStartOnly = true;
+ Restart = "always";
+ Type = "forking";
+ User = cfg.user;
+ };
+ };
+ };
+
+in {
+
+ options = {
+ nixsap.apps.gnupg = mkOption {
+ description = "GnuPG instances";
+ default = {};
+ type = attrsOf (submodule (import ./instance.nix pkgs));
+ };
+ };
+
+ config = {
+ nixsap.deployment.keyrings = keyrings;
+ systemd.services = mapAttrs' mkService instances;
+ };
+
+}
+
diff --git a/modules/apps/gnupg/instance.nix b/modules/apps/gnupg/instance.nix
new file mode 100644
index 0000000..e421a1e
--- /dev/null
+++ b/modules/apps/gnupg/instance.nix
@@ -0,0 +1,68 @@
+pkgs:
+{ lib, name, ... }:
+
+let
+
+ inherit (lib)
+ mkOption ;
+
+ inherit (lib.types)
+ attrsOf listOf package path str ;
+
+in {
+ options = {
+
+ user = mkOption {
+ description = ''
+ User to run as ang keyring owner. This option is required.
+ Note that this user is not created automaically.
+ '';
+ type = str;
+ };
+
+ home = mkOption {
+ description = ''
+ GnuPG home directory where keyrings and gpg-agent socket
+ will be located.
+ '';
+ type = path;
+ default = "/gnupg/${name}";
+ };
+
+ package = mkOption {
+ description = "GnuPG2 package";
+ type = package;
+ default = pkgs.gnupg21;
+ };
+
+ publicKeys = mkOption {
+ description = "Public GPG keys";
+ type = listOf path;
+ default = [];
+ };
+
+ secretKeys = mkOption {
+ description = "Secret GPG keys";
+ type = listOf path;
+ default = [];
+ };
+
+ passphrase = mkOption {
+ description = ''
+ Secret files with pass-phrase to unlock secret keys. Keys are
+ identified by cacheid, which is either a 40 character keygrip of
+ hexadecimal characters identifying the key or an arbitrary string
+ identifying a passphrase. Refer to the `gpg-preset-passphrase`
+ documentation, because it is what stays behind this mechanism.
+ Generally in unattended environments you need to use keygrip.
+ '';
+ type = attrsOf path;
+ default = {};
+ example = {
+ "ABCD...321" = "/run/keys/foo";
+ "myapp:mykey" = "/run/keys/bar";
+ };
+ };
+ };
+}
+