From 15dbd89a28b4e172699a4a6d82b9a832e74c9113 Mon Sep 17 00:00:00 2001
From: Julien Moutinho <julm@sourcephile.fr>
Date: Tue, 23 Jun 2020 19:16:49 +0200
Subject: [PATCH] nix: add module security.pass
---
nixos/defaults.nix | 1 +
nixos/modules.nix | 3 +-
nixos/modules/install.nix | 46 +++++
nixos/modules/security/install.nix | 11 --
nixos/modules/security/pass.nix | 158 ++++++++++++++++++
servers/losurdo.nix | 29 +---
servers/losurdo/acme/autogeree.net.nix | 52 +++---
servers/losurdo/acme/sourcephile.fr.nix | 53 +++---
servers/losurdo/fail2ban.nix | 1 +
servers/losurdo/keys.nix | 7 -
servers/losurdo/networking.nix | 2 +-
servers/losurdo/postgresql/openconcerto.nix | 34 ++--
servers/losurdo/system.nix | 35 ++++
servers/losurdo/users.nix | 6 +-
servers/mermet.nix | 25 +--
servers/mermet/dovecot.nix | 1 -
servers/mermet/gitolite.nix | 1 -
servers/mermet/knot.nix | 4 +-
servers/mermet/knot/autogeree.net.nix | 26 +--
servers/mermet/knot/sourcephile.fr.nix | 26 +--
servers/mermet/networking.nix | 2 +-
servers/mermet/nginx.nix | 1 -
servers/mermet/postfix.nix | 1 -
servers/mermet/rspamd.nix | 24 ++-
servers/mermet/rspamd/autogeree.net.nix | 26 ++-
servers/mermet/rspamd/sourcephile.fr.nix | 30 ++--
servers/mermet/system.nix | 2 +
shell.nix | 15 +-
.../development/libraries/nix-plugins.nix | 1 +
shell/modules/tools/security/gnupg.nix | 37 ++--
shell/openpgp.nix | 44 ++++-
31 files changed, 468 insertions(+), 236 deletions(-)
create mode 100644 nixos/modules/install.nix
delete mode 100644 nixos/modules/security/install.nix
create mode 100644 nixos/modules/security/pass.nix
delete mode 100644 servers/losurdo/keys.nix
diff --git a/nixos/defaults.nix b/nixos/defaults.nix
index 5e53a7f..d97874d 100644
--- a/nixos/defaults.nix
+++ b/nixos/defaults.nix
@@ -108,6 +108,7 @@ environment = {
binutils
#dnsutils
dstat
+ gnupg
htop
inetutils
iotop
diff --git a/nixos/modules.nix b/nixos/modules.nix
index 0222728..c28cd7f 100644
--- a/nixos/modules.nix
+++ b/nixos/modules.nix
@@ -3,7 +3,8 @@
# its clearer, safer and more flexible if not quicker.
{
imports = [
- modules/security/install.nix
+ modules/install.nix
+ modules/security/pass.nix
modules/services/networking/domains.nix
#modules/services/networking/knot.nix
modules/services/databases/openldap.nix
diff --git a/nixos/modules/install.nix b/nixos/modules/install.nix
new file mode 100644
index 0000000..80e5bfe
--- /dev/null
+++ b/nixos/modules/install.nix
@@ -0,0 +1,46 @@
+{ pkgs, lib, config, ... }:
+let
+ inherit (builtins) listToAttrs;
+ inherit (lib) types;
+ inherit (config) networking;
+ cfg = config.install;
+in
+{
+options.install = {
+ enable = lib.mkEnableOption "Install";
+ shellHook = lib.mkOption {
+ type = types.lines;
+ default = "";
+ };
+ shellScript = lib.mkOption {
+ type = types.lines;
+ default = "";
+ apply = pkgs.writeShellScriptBin "bash";
+ };
+ target = lib.mkOption {
+ type = types.str;
+ default = "root@${networking.hostName}.${networking.domain}";
+ };
+ generations = lib.mkOption {
+ type = types.str;
+ default = "+10";
+ };
+ profile = lib.mkOption {
+ type = types.str;
+ default = "/nix/var/nix/profiles/system";
+ };
+};
+config = lib.mkIf cfg.enable {
+ install.shellScript =
+ let nixos = config.system.build.toplevel; in ''
+ PATH="$PATH:${with pkgs; lib.makeBinPath [nix openssh]}"
+ set -x
+ nix ''${TRACE:+-L} copy \
+ --to ssh://${cfg.target} --substitute-on-destination \
+ ${nixos}
+ ssh ${cfg.target} nix-env --profile "${cfg.profile}" --set "${nixos}" \
+ '&&' nix-env --profile "${cfg.profile}" --delete-generations "${cfg.generations}" \
+ '&&' "${cfg.profile}"/bin/switch-to-configuration "''${switch:-switch}"
+ '';
+};
+}
diff --git a/nixos/modules/security/install.nix b/nixos/modules/security/install.nix
deleted file mode 100644
index 898c89e..0000000
--- a/nixos/modules/security/install.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-{ pkgs, lib, config, ... }:
-let inherit (lib) types; in
-{
-options.security.install = {
- shellHook = lib.mkOption {
- type = types.lines;
- default = "";
- };
- # TODO: more structured options, like NixOps' deployment.keys
-};
-}
diff --git a/nixos/modules/security/pass.nix b/nixos/modules/security/pass.nix
new file mode 100644
index 0000000..2a9fd1d
--- /dev/null
+++ b/nixos/modules/security/pass.nix
@@ -0,0 +1,158 @@
+{ pkgs, lib, config, ... }:
+let
+ inherit (builtins) head listToAttrs match split;
+ inherit (lib) types;
+ inherit (config.security) pass;
+ dirname = p:
+ let dir = match "^(.+)/[^/]*$" p; in
+ if dir == [] then "." else head dir;
+ escapeUnitName = name:
+ lib.concatMapStrings (s: if lib.isList s then "-" else s)
+ (split "[^a-zA-Z0-9_.\\-]+" name);
+in
+{
+options.security.pass = {
+ store = lib.mkOption {
+ type = types.path;
+ description = ''
+ Default path to the password-store of the orchestrating system.
+ '';
+ };
+ secrets = lib.mkOption {
+ default = {};
+ type = types.attrsOf (types.submodule ({name, config, ...}: {
+ options = {
+ gpg = lib.mkOption {
+ type = types.path;
+ default = builtins.path {
+ path = toString pass.store + "/${name}.gpg";
+ name = "${escapeUnitName name}.gpg";
+ };
+ description = ''
+ The path to the gnupg-encrypted secret.
+ It will be copied into the Nix store of the orchestrating and of the target system.
+ It must be decipherable by an OpenPGP key within <literal>gnupgHome</literal>,
+ whose passhrase is on the target system into <literal>passphraseFile</literal>.
+ Defaults to the name of the secret, prefixed by <literal>passwordStore</literal>
+ and suffixed by <literal>.gpg</literal>.
+ '';
+ };
+ gnupgHome = lib.mkOption {
+ type = types.str;
+ default = "/root/.gnupg";
+ description = ''
+ The directory on the target system to the <literal>gnupg</literal> home
+ used to decrypt the secret.
+ '';
+ };
+ passphraseFile = lib.mkOption {
+ type = types.str;
+ default = "/root/key.pass";
+ description = ''
+ The directory on the target system to a file containing
+ the password of an OpenPGP key in <literal>gnupgHome</literal>,
+ to which <literal>gpg</literal> secret is encrypted to.
+ '';
+ };
+ mode = lib.mkOption {
+ type = types.str;
+ default = "400";
+ description = ''
+ Permission mode of the secret <literal>path</literal>.
+ '';
+ };
+ user = lib.mkOption {
+ type = types.str;
+ default = "root";
+ description = ''
+ Owner of the secret <literal>path</literal>.
+ '';
+ };
+ group = lib.mkOption {
+ type = types.str;
+ default = "root";
+ description = ''
+ Group of the secret <literal>path</literal>.
+ '';
+ };
+ pipe = lib.mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Shell command taking the deciphered secret on its standard input
+ and which must put on its standard output
+ the actual material to be installed.
+ This allows to decorate the secret with non-secret bits.
+ '';
+ };
+ path = lib.mkOption {
+ type = types.str;
+ default = name;
+ apply = p: if match "^/.*" p == null then "/run/pass-secrets/"+p+"/file" else p;
+ description = ''
+ The path on the target system where the secret is installed to.
+ Any non-absolute path is relative to <filename>/run/pass-secrets</filename>.
+ Default to the name of the secret.
+ '';
+ };
+ service = lib.mkOption {
+ type = types.str;
+ default = "secret-" + escapeUnitName name + ".service";
+ description = ''
+ The name of the systemd service.
+ Useful to put constraints like <literal>after</literal> or <literal>wants</wants>
+ into services requiring this secret.
+ '';
+ };
+ postStart = lib.mkOption {
+ type = types.lines;
+ default = "";
+ example = "systemctl reload nginx.service";
+ description = ''
+ Commands to run after new secrets go live. Typically
+ the web server and other servers using secrets need to
+ be reloaded.
+ '';
+ };
+ };
+ }));
+ };
+};
+config = lib.mkIf (pass.secrets != {}) {
+ #systemd.tmpfiles.rules = [ "d /run/secrets 0755 root root -" ];
+ systemd.services =
+ lib.mapAttrs' (target: secret:
+ lib.nameValuePair (lib.removeSuffix ".service" secret.service) {
+ description = "Install secret ${secret.path}";
+ after = [ "network.target" "network-online.target" ];
+ wantedBy = lib.mkIf (!config.boot.isContainer) [ "multi-user.target" ];
+ script = ''
+ set -o pipefail
+ set -eux
+ decrypt() {
+ {
+ ${pkgs.gnupg}/bin/gpg --batch --pinentry-mode loopback \
+ --homedir '${secret.gnupgHome}' \
+ --passphrase-file '${secret.passphraseFile}' \
+ --decrypt '${secret.gpg}' \
+ ${lib.optionalString (secret.pipe != null) (" | "+secret.pipe)}
+ } |
+ install -D -m '${secret.mode}' -o '${secret.user}' -g '${secret.group}' /dev/stdin \
+ '${secret.path}'
+ }
+ while ! decrypt; do sleep 1; done
+ '';
+ inherit (secret) postStart;
+ serviceConfig = {
+ Type = "oneshot";
+ PrivateTmp = true;
+ WorkingDirectory = dirname secret.gnupgHome;
+ } // lib.optionalAttrs (match "^/.*" target == null) {
+ RuntimeDirectory = dirname secret.path;
+ RuntimeDirectoryMode = "711";
+ RuntimeDirectoryPreserve = false; # FIXME: when does the removal actualy occur with with Type=oneshot?
+ };
+ }
+ ) pass.secrets;
+};
+}
diff --git a/servers/losurdo.nix b/servers/losurdo.nix
index fe92994..a71b771 100644
--- a/servers/losurdo.nix
+++ b/servers/losurdo.nix
@@ -2,8 +2,12 @@
#
# Show configuration options with, for example:
# nix-instantiate servers/losurdo.nix --eval -A config.networking.hostName
+# or:
+# nix eval servers.losurdo.config.networking.hostName
# Install/upgrade with:
# nix run install -f servers/losurdo.nix
+# or:
+# nix run servers.losurdo.install
let
ipv4 = "80.67.180.251";
system = import <nixpkgs/nixos/lib/eval-config.nix> {
@@ -31,26 +35,7 @@ let
servers = import ../servers.nix;
};
};
- inherit (system.config) networking;
- lib = system.pkgs.lib;
-in with system; system // {
-inherit ipv4;
-install =
- let target = "root@${networking.hostName}.${networking.domain}";
- profile = "/nix/var/nix/profiles/system";
- generations = "+10";
- nixos = config.system.build.toplevel;
- in
- pkgs.writeShellScriptBin "bash" ''
- PATH="$PATH:${with pkgs; lib.makeBinPath [nix openssh pass]}"
- set -eux
- nix ''${TRACE:+-L} copy \
- --to ssh://${target} --substitute-on-destination \
- ${nixos}
- target="${target}"
- ${config.security.install.shellHook}
- ssh ${target} nix-env --profile "${profile}" --set "${nixos}" \
- '&&' nix-env --profile "${profile}" --delete-generations "${generations}" \
- '&&' "${profile}"/bin/switch-to-configuration "''${switch:-switch}"
-'';
+in system // {
+ inherit ipv4;
+ install = system.config.install.shellScript;
}
diff --git a/servers/losurdo/acme/autogeree.net.nix b/servers/losurdo/acme/autogeree.net.nix
index bd95905..23dee6b 100644
--- a/servers/losurdo/acme/autogeree.net.nix
+++ b/servers/losurdo/acme/autogeree.net.nix
@@ -2,17 +2,14 @@
let
domain = "autogeree.net";
domainID = lib.replaceStrings ["."] ["_"] domain;
- credentialsFile = "/var/lib/acme/.lego/${domain}/rfc2136";
+ inherit (config.security) pass;
inherit (config.users) users groups;
in
{
-systemd.services."acme-${domain}".after = [
- "unbound.service"
-];
networking.nftables.ruleset = ''
# for lego to update ACME DNS-01 challenge
- add rule inet filter fw2net ip daddr ${servers.mermet.ipv4} tcp dport 53 counter accept comment "DNS"
- add rule inet filter fw2net ip daddr ${servers.mermet.ipv4} udp dport 53 counter accept comment "DNS"
+ add rule inet filter fw2net ip daddr ${servers.mermet.ipv4} tcp dport 53 counter accept comment "ACME DNS-01"
+ add rule inet filter fw2net ip daddr ${servers.mermet.ipv4} udp dport 53 counter accept comment "ACME DNS-01"
# for lego to check DNS propagation on ns6.gandi.net
add rule inet filter fw2net ip daddr 217.70.177.40 tcp dport 53 skuid ${users.root.name} counter accept comment "DNS gandi"
add rule inet filter fw2net ip daddr 217.70.177.40 udp dport 53 skuid ${users.root.name} counter accept comment "DNS gandi"
@@ -33,23 +30,30 @@ security.acme.certs."${domain}" = {
# ns6.gandi.net takes roughly 5min to update
# hence lego's RFC2136_PROPAGATION_TIMEOUT=1000
#dnsPropagationCheck = false;
- inherit credentialsFile;
+ credentialsFile = pass.secrets."lego/${domain}/rfc2136".path;
+};
+security.pass.secrets."lego/${domain}/rfc2136" = {
+ pipe = ''
+ cat - ${pkgs.writeText "env" ''
+ RFC2136_NAMESERVER=ns.${domain}:53
+ RFC2136_TSIG_ALGORITHM=hmac-sha256.
+ RFC2136_TSIG_KEY=acme_${domainID}
+ RFC2136_PROPAGATION_TIMEOUT=1000
+ RFC2136_POLLING_INTERVAL=30
+ RFC2136_SEQUENCE_INTERVAL=30
+ RFC2136_DNS_TIMEOUT=1000
+ RFC2136_TTL=1
+ ''}
+ '';
+};
+systemd.services."acme-${domain}" = {
+ after = [
+ "unbound.service"
+ pass.secrets."lego/${domain}/rfc2136".service
+ ];
+ wants = [
+ "unbound.service"
+ pass.secrets."lego/${domain}/rfc2136".service
+ ];
};
-security.install.shellHook = ''
- {
- cat <<-EOF
- RFC2136_NAMESERVER=ns.${domain}:53
- RFC2136_TSIG_ALGORITHM=hmac-sha256.
- RFC2136_TSIG_KEY=acme_${domainID}
- RFC2136_PROPAGATION_TIMEOUT=1000
- RFC2136_POLLING_INTERVAL=30
- RFC2136_SEQUENCE_INTERVAL=30
- RFC2136_DNS_TIMEOUT=1000
- RFC2136_TTL=1
- EOF
- pass "servers/losurdo/lego/${domain}/rfc2136"
- } |
- ssh "$target" install -D -m 0400 -o root -g root /dev/stdin \
- ${credentialsFile}
-'';
}
diff --git a/servers/losurdo/acme/sourcephile.fr.nix b/servers/losurdo/acme/sourcephile.fr.nix
index a71f9ea..d1b431b 100644
--- a/servers/losurdo/acme/sourcephile.fr.nix
+++ b/servers/losurdo/acme/sourcephile.fr.nix
@@ -2,17 +2,15 @@
let
domain = "sourcephile.fr";
domainID = lib.replaceStrings ["."] ["_"] domain;
- credentialsFile = "/var/lib/acme/.lego/${domain}/rfc2136";
+ inherit (config) install;
+ inherit (config.security) pass;
inherit (config.users) users groups;
in
{
-systemd.services."acme-${domain}".after = [
- "unbound.service"
-];
networking.nftables.ruleset = ''
# for lego to update ACME DNS-01 challenge
- add rule inet filter fw2net tcp dport 53 ip daddr ${servers.mermet.ipv4} counter accept comment "DNS"
- add rule inet filter fw2net udp dport 53 ip daddr ${servers.mermet.ipv4} counter accept comment "DNS"
+ add rule inet filter fw2net tcp dport 53 ip daddr ${servers.mermet.ipv4} counter accept comment "ACME DNS-01"
+ add rule inet filter fw2net udp dport 53 ip daddr ${servers.mermet.ipv4} counter accept comment "ACME DNS-01"
# for lego to check DNS propagation on ns6.gandi.net
add rule inet filter fw2net ip daddr 217.70.177.40 tcp dport 53 skuid ${users.root.name} counter accept comment "DNS gandi"
add rule inet filter fw2net ip daddr 217.70.177.40 udp dport 53 skuid ${users.root.name} counter accept comment "DNS gandi"
@@ -30,23 +28,30 @@ security.acme.certs."${domain}" = {
# ns6.gandi.net takes roughly 5min to update
# hence lego's RFC2136_PROPAGATION_TIMEOUT=1000
#dnsPropagationCheck = false;
- inherit credentialsFile;
+ credentialsFile = pass.secrets."lego/${domain}/rfc2136".path;
+};
+security.pass.secrets."lego/${domain}/rfc2136" = {
+ pipe = ''
+ cat - ${pkgs.writeText "env" ''
+ RFC2136_NAMESERVER=ns.${domain}:53
+ RFC2136_TSIG_ALGORITHM=hmac-sha256.
+ RFC2136_TSIG_KEY=acme_${domainID}
+ RFC2136_PROPAGATION_TIMEOUT=1000
+ RFC2136_POLLING_INTERVAL=30
+ RFC2136_SEQUENCE_INTERVAL=30
+ RFC2136_DNS_TIMEOUT=1000
+ RFC2136_TTL=1
+ ''}
+ '';
+};
+systemd.services."acme-${domain}" = {
+ after = [
+ "unbound.service"
+ pass.secrets."lego/${domain}/rfc2136".service
+ ];
+ wants = [
+ "unbound.service"
+ pass.secrets."lego/${domain}/rfc2136".service
+ ];
};
-security.install.shellHook = ''
- {
- cat <<-EOF
- RFC2136_NAMESERVER=ns.${domain}:53
- RFC2136_TSIG_ALGORITHM=hmac-sha256.
- RFC2136_TSIG_KEY=acme_${domainID}
- RFC2136_PROPAGATION_TIMEOUT=1000
- RFC2136_POLLING_INTERVAL=30
- RFC2136_SEQUENCE_INTERVAL=30
- RFC2136_DNS_TIMEOUT=1000
- RFC2136_TTL=1
- EOF
- pass "servers/losurdo/lego/${domain}/rfc2136"
- } |
- ssh "$target" install -D -m 0400 -o root -g root /dev/stdin \
- ${credentialsFile}
-'';
}
diff --git a/servers/losurdo/fail2ban.nix b/servers/losurdo/fail2ban.nix
index f0f8018..2f8d109 100644
--- a/servers/losurdo/fail2ban.nix
+++ b/servers/losurdo/fail2ban.nix
@@ -20,6 +20,7 @@ services.fail2ban = {
servers.mermet.ipv4
servers.losurdo.ipv4
"198.252.154.1" # wren.riseup.net
+ "90.78.73.73" # openconcerto user
];
jails = {
DEFAULT = ''
diff --git a/servers/losurdo/keys.nix b/servers/losurdo/keys.nix
deleted file mode 100644
index 6a11b1f..0000000
--- a/servers/losurdo/keys.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-{ pkgs, lib, config, ... }:
-let
- inherit (builtins) readFile;
- inherit (builtins.extraBuiltins) pass;
-in
-{
-}
diff --git a/servers/losurdo/networking.nix b/servers/losurdo/networking.nix
index e98a452..3dde6a7 100644
--- a/servers/losurdo/networking.nix
+++ b/servers/losurdo/networking.nix
@@ -1,7 +1,7 @@
{ pkgs, lib, config, nodes, ... }:
with builtins;
let
- inherit (builtins.extraBuiltins) pass pass-to-file;
+ inherit (builtins.extraBuiltins) pass-to-file;
inherit (config) networking users;
lanIPv4 = "192.168.1.215";
lanNet = "192.168.1.0/24";
diff --git a/servers/losurdo/postgresql/openconcerto.nix b/servers/losurdo/postgresql/openconcerto.nix
index d4a9e04..d88c14f 100644
--- a/servers/losurdo/postgresql/openconcerto.nix
+++ b/servers/losurdo/postgresql/openconcerto.nix
@@ -5,6 +5,8 @@ let
url = "https://www.openconcerto.org/fr/telechargement/1.6/OpenConcerto-1.6.3.sql.zip";
sha256 = "02h35ni9xknzrjsra56c3zhlhs0ji9qc61kcgi7vgcpylqjw0s6n";
};
+ inherit (config.security) pass;
+ inherit (config.users) users groups;
inherit (config) networking;
# Example of ~/.config/OpenConcerto/main.properties
# DOC: https://code.openconcerto.org/filedetails.php?repname=OpenConcerto&path=%2Ftrunk%2FOpenConcerto%2Fsrc%2Forg%2Fopenconcerto%2Fsql%2FPropsConfiguration.java
@@ -30,7 +32,21 @@ let
'';
in
{
+services.postgresql = {
+ authentication = lib.mkForce ''
+ # CONNECTION DATABASE USER AUTH OPTIONS
+ # FIXME: using scram-sha-256 instead of md5 requires postfix >= 11
+ hostssl ${db} ${owner} all md5
+ '';
+ identMap = ''
+ # MAPNAME SYSTEM-USERNAME PG-USERNAME
+ user root ${owner}
+ '';
+};
+security.pass.secrets."postgresql/pass/${owner}" = {};
systemd.services.postgresql = {
+ after = [ pass.secrets."postgresql/pass/${owner}".service ];
+ wants = [ pass.secrets."postgresql/pass/${owner}".service ];
postStart = lib.mkAfter ''
sed -e 's/ \(TO\|FROM\) \+openconcerto/ \1 ${owner}/g' \
${sql}/OpenConcerto-1.6.3.sql |
@@ -39,7 +55,7 @@ systemd.services.postgresql = {
lc_collate=fr_FR.UTF-8 \
lc_type=fr_FR.UTF-8 \
owner=${owner} \
- pass=$(cat /run/keys/postgresql_pass_${owner}) \
+ pass=$(cat ${pass.secrets."postgresql/pass/${owner}".path}) \
pg_createdb ${db} >/dev/null
$PSQL -d "${db}" -AqtX --set ON_ERROR_STOP=1 -f - <<EOF
@@ -54,20 +70,4 @@ systemd.services.postgresql = {
EOF
'';
};
-services.postgresql = {
- authentication = lib.mkForce ''
- # CONNECTION DATABASE USER AUTH OPTIONS
- # FIXME: using scram-sha-256 instead of md5 requires postfix >= 11
- hostssl ${db} ${owner} all md5
- '';
- identMap = ''
- # MAPNAME SYSTEM-USERNAME PG-USERNAME
- user root ${owner}
- '';
-};
-security.install.shellHook = ''
- pass "servers/losurdo/postgresql/pass/${owner}" |
- ssh "$target" install -D -m 0400 -o root -g root /dev/stdin \
- /run/keys/postgresql_pass_${owner}
-'';
}
diff --git a/servers/losurdo/system.nix b/servers/losurdo/system.nix
index 8b0f87f..3e955b1 100644
--- a/servers/losurdo/system.nix
+++ b/servers/losurdo/system.nix
@@ -1,4 +1,8 @@
{ pkgs, lib, config, ... }:
+let
+ inherit (config) networking;
+ inherit (config.security) pass;
+in
{
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database servers.
@@ -9,6 +13,36 @@ system.stateVersion = "19.09"; # Did you read the comment?
# and let mosh work smoothly.
services.logind.killUserProcesses = false;
+install = {
+ enable = true;
+ shellScript = lib.mkBefore ''
+ PATH="$PATH:${with pkgs; lib.makeBinPath [gnupg openssh]}"
+ set -x
+ gpg --decrypt '${pass.store}/root/key.pass.gpg' |
+ ssh '${config.install.target}' install -D -m 400 -o root -g root /dev/stdin /root/key.pass
+ '';
+};
+security.pass = {
+ store = ../../../sec/pass/servers/losurdo;
+ secrets."root/key" = {
+ postStart = ''
+ set -x
+ ${pkgs.gnupg}/bin/gpg --batch --pinentry-mode loopback \
+ --homedir /root/.gnupg \
+ --passphrase-file /root/key.pass \
+ --import '${pass.secrets."root/key".path}'
+ shred -u '${pass.secrets."root/key".path}'
+ '';
+ };
+};
+systemd.services = lib.mapAttrs' (target: secret:
+ lib.nameValuePair (lib.removeSuffix ".service" secret.service)
+ (lib.optionalAttrs (target != "root/key") {
+ after = [ pass.secrets."root/key".service ];
+ wants = [ pass.secrets."root/key".service ];
+ })
+ ) pass.secrets;
+
services.unbound.enable = true;
environment.systemPackages = with pkgs; [
@@ -27,5 +61,6 @@ environment.systemPackages = with pkgs; [
socat
sanoid
#iptables-nftables-compat
+ gnupg
];
}
diff --git a/servers/losurdo/users.nix b/servers/losurdo/users.nix
index a9ecb91..766277d 100644
--- a/servers/losurdo/users.nix
+++ b/servers/losurdo/users.nix
@@ -39,9 +39,5 @@ users = {
};
};
-security.install.shellHook = ''
- pass "servers/losurdo/root/ssh/id_ed25519" |
- ssh "$target" install -m 0400 -o root -g root /dev/stdin \
- /root/.ssh/id_ed25519
-'';
+security.pass.secrets."/root/.ssh/id_ed25519" = {};
}
diff --git a/servers/mermet.nix b/servers/mermet.nix
index 2c602b5..0c2ada8 100644
--- a/servers/mermet.nix
+++ b/servers/mermet.nix
@@ -41,26 +41,7 @@ let
servers = import ../servers.nix;
};
};
- inherit (system.config) networking;
- lib = system.pkgs.lib;
-in with system; system // {
-inherit ipv4;
-install =
- let target = "root@${networking.hostName}.${networking.domain}";
- profile = "/nix/var/nix/profiles/system";
- generations = "+10";
- nixos = config.system.build.toplevel;
- in
- pkgs.writeShellScriptBin "bash" ''
- PATH="$PATH:${with pkgs; lib.makeBinPath [nix openssh pass]}"
- set -eux
- nix ''${TRACE:+-L} copy \
- --to ssh://${target} --substitute-on-destination \
- ${nixos}
- target="${target}"
- ${config.security.install.shellHook}
- ssh ${target} nix-env --profile "${profile}" --set "${nixos}" \
- '&&' nix-env --profile "${profile}" --delete-generations "${generations}" \
- '&&' "${profile}"/bin/switch-to-configuration "''${switch:-switch}"
-'';
+in system // {
+ inherit ipv4;
+ install = system.config.install.shellScript;
}
diff --git a/servers/mermet/dovecot.nix b/servers/mermet/dovecot.nix
index 2488e3c..06e5566 100644
--- a/servers/mermet/dovecot.nix
+++ b/servers/mermet/dovecot.nix
@@ -1,7 +1,6 @@
{ pkgs, lib, config, system, ... }:
let
inherit (builtins) toString toFile readFile;
- inherit (builtins.extraBuiltins) pass;
inherit (lib) types;
inherit (pkgs.lib) loadFile unlines unlinesAttrs unlinesValues unwords;
inherit (config) networking;
diff --git a/servers/mermet/gitolite.nix b/servers/mermet/gitolite.nix
index 28b877c..8ad0fc8 100644
--- a/servers/mermet/gitolite.nix
+++ b/servers/mermet/gitolite.nix
@@ -1,7 +1,6 @@
{ pkgs, lib, config, ... }:
let
inherit (builtins) readFile;
- inherit (builtins.extraBuiltins) pass;
inherit (lib) types;
inherit (config) networking;
inherit (config.services) gitolite;
diff --git a/servers/mermet/knot.nix b/servers/mermet/knot.nix
index 9ec00c2..87f1ea8 100644
--- a/servers/mermet/knot.nix
+++ b/servers/mermet/knot.nix
@@ -12,8 +12,8 @@ imports = [
options.services.knot = {
zones = lib.mkOption {
default = {};
- type = types.attrsOf (types.submodule ({domain, ...}: {
- #config.domain = lib.mkDefault domain;
+ type = types.attrsOf (types.submodule ({name, ...}: {
+ #config.domain = lib.mkDefault name;
options = {
conf = lib.mkOption {
type = types.lines;
diff --git a/servers/mermet/knot/autogeree.net.nix b/servers/mermet/knot/autogeree.net.nix
index de8a75a..02a6e06 100644
--- a/servers/mermet/knot/autogeree.net.nix
+++ b/servers/mermet/knot/autogeree.net.nix
@@ -3,28 +3,17 @@ let
domain = "autogeree.net";
domainID = lib.replaceStrings ["."] ["_"] domain;
inherit (builtins) attrValues;
- inherit (builtins.extraBuiltins) pass git;
+ inherit (builtins.extraBuiltins) git;
inherit (config) networking;
inherit (config.services) knot;
+ inherit (config.security) pass;
inherit (config.users) users groups;
# Use the Git commit time of the ${domain}.nix file to set the serial number.
# WARNING: the ${domain}.nix must be committed into Git for this to work.
# WARNING: this does not take other .nix into account, though they may contribute to the zone's data.
serial = domain: toString (git ./. [ "log" "-1" "--format=%ct" "--" (domain + ".nix") ]);
- includes = {
- "${domain}/acme.conf" = "/var/lib/knot/tsig/${domain}/acme.conf";
- };
in
{
-security.install.shellHook = ''
- # Generated with: keymgr -t acme_${domain}
- pass "servers/mermet/knot/${domain}/acme.conf" |
- ssh "$target" install -D -m 0400 -o ${users."knot".name} -g root /dev/stdin \
- ${includes."${domain}/acme.conf"}
-'';
-services.knot = {
- keyFiles = attrValues includes;
-};
services.knot.zones."${domain}" = {
conf = ''
acl:
@@ -108,6 +97,17 @@ services.knot.zones."${domain}" = {
@ CAA 128 issue "letsencrypt.org"
'';
};
+services.knot = {
+ keyFiles = [ pass.secrets."knot/tsig/${domain}/acme.conf".path ];
+};
+security.pass.secrets."knot/tsig/${domain}/acme.conf" = {
+ # Generated with: keymgr -t acme_${domainID}
+ user = users.knot.name;
+};
+systemd.services.knot = {
+ after = [ pass.secrets."knot/tsig/${domain}/acme.conf".service ];
+ wants = [ pass.secrets."knot/tsig/${domain}/acme.conf".service ];
+};
/* Useless since the zone is public
services.unbound.extraConfig = ''
stub-zone:
diff --git a/servers/mermet/knot/sourcephile.fr.nix b/servers/mermet/knot/sourcephile.fr.nix
index 571a5b2..3f61556 100644
--- a/servers/mermet/knot/sourcephile.fr.nix
+++ b/servers/mermet/knot/sourcephile.fr.nix
@@ -3,28 +3,17 @@ let
domain = "sourcephile.fr";
domainID = lib.replaceStrings ["."] ["_"] domain;
inherit (builtins) attrValues;
- inherit (builtins.extraBuiltins) pass git;
+ inherit (builtins.extraBuiltins) git;
inherit (config) networking;
+ inherit (config.security) pass;
inherit (config.services) knot;
inherit (config.users) users groups;
# Use the Git commit time of the ${domain}.nix file to set the serial number.
# WARNING: the ${domain}.nix must be committed into Git for this to work.
# WARNING: this does not take other .nix into account, though they may contribute to the zone's data.
serial = domain: toString (git ./. [ "log" "-1" "--format=%ct" "--" (domain + ".nix") ]);
- includes = {
- "${domain}/acme.conf" = "/var/lib/knot/tsig/${domain}/acme.conf";
- };
in
{
-security.install.shellHook = ''
- # Generated with: keymgr -t acme_${domainID}
- pass "servers/mermet/knot/${domain}/acme.conf" |
- ssh "$target" install -D -m 0400 -o ${users."knot".name} -g root /dev/stdin \
- ${includes."${domain}/acme.conf"}
-'';
-services.knot = {
- keyFiles = attrValues includes;
-};
services.knot.zones."${domain}" = {
conf = ''
acl:
@@ -128,6 +117,17 @@ services.knot.zones."${domain}" = {
@ CAA 128 issue "letsencrypt.org"
'';
};
+services.knot = {
+ keyFiles = [ pass.secrets."knot/tsig/${domain}/acme.conf".path ];
+};
+security.pass.secrets."knot/tsig/${domain}/acme.conf" = {
+ # Generated with: keymgr -t acme_${domainID}
+ user = users.knot.name;
+};
+systemd.services.knot = {
+ after = [ pass.secrets."knot/tsig/${domain}/acme.conf".service ];
+ wants = [ pass.secrets."knot/tsig/${domain}/acme.conf".service ];
+};
/* Useless since the zone is public
services.unbound.extraConfig = ''
stub-zone:
diff --git a/servers/mermet/networking.nix b/servers/mermet/networking.nix
index 7951bc7..d6787a6 100644
--- a/servers/mermet/networking.nix
+++ b/servers/mermet/networking.nix
@@ -1,7 +1,7 @@
{ pkgs, lib, config, ipv4, ... }:
with builtins;
let
- inherit (builtins.extraBuiltins) pass pass-to-file;
+ inherit (builtins.extraBuiltins) pass-to-file;
inherit (config) networking users;
netIPv4 = ipv4;
netIPv4Gateway = "80.67.180.134";
diff --git a/servers/mermet/nginx.nix b/servers/mermet/nginx.nix
index 89d2f81..8a359be 100644
--- a/servers/mermet/nginx.nix
+++ b/servers/mermet/nginx.nix
@@ -1,7 +1,6 @@
{pkgs, lib, config, system, ...}:
let
inherit (builtins) readFile;
- inherit (builtins.extraBuiltins) pass;
inherit (lib) types;
inherit (pkgs.lib) loadFile;
inherit (config) networking;
diff --git a/servers/mermet/postfix.nix b/servers/mermet/postfix.nix
index 9803640..179db16 100644
--- a/servers/mermet/postfix.nix
+++ b/servers/mermet/postfix.nix
@@ -1,7 +1,6 @@
{ pkgs, lib, config, ... }:
let
inherit (builtins) attrNames concatStringsSep readFile toPath;
- inherit (builtins.extraBuiltins) pass;
inherit (lib) types;
inherit (pkgs.lib) loadFile unlines unwords unlinesAttrs;
inherit (config) networking users;
diff --git a/servers/mermet/rspamd.nix b/servers/mermet/rspamd.nix
index 3ed3ef2..129c076 100644
--- a/servers/mermet/rspamd.nix
+++ b/servers/mermet/rspamd.nix
@@ -1,9 +1,9 @@
{ pkgs, lib, config, ... }:
let
inherit (builtins) attrNames listToAttrs readFile;
- inherit (builtins.extraBuiltins) pass pass-chomp;
inherit (lib) types;
inherit (pkgs.lib) unlinesAttrs;
+ inherit (config.security) pass;
inherit (config.services) postfix rspamd dovecot2 redis;
inherit (config.users) users;
in
@@ -22,7 +22,6 @@ options = {
};
config = {
users.users."${rspamd.user}".extraGroups = [
- "keys"
users.redis.group
];
services.rspamd = {
@@ -32,12 +31,12 @@ services.rspamd = {
locals = {
"dkim_signing.conf".text = ''
selector_map = ${rspamd.dkimSelectorMap};
- path = "/run/keys/dkim.$domain.$selector.key";
+ path = "/run/pass-secrets/rspamd/dkim/$domain/$selector.key";
allow_username_mismatch = true;
'';
"arc.conf".text = ''
selector_map = ${rspamd.dkimSelectorMap};
- path = "/run/keys/dkim.$domain.$selector.key";
+ path = "/run/pass-secrets/rspamd/dkim/$domain/$selector.key";
allow_username_mismatch = true;
'';
"redis.conf".text = ''
@@ -95,19 +94,30 @@ services.rspamd = {
'';
};
controller = {
- includes = [ "$CONFDIR/worker-controller.inc" ];
+ includes = [
+ "$CONFDIR/worker-controller.inc"
+ pass.secrets."rspamd/controller/hashedPassword".path
+ ];
bindSockets = [
"127.0.0.1:11334"
];
extraConfig = ''
#count = 1;
#static_dir = "''${WWWDIR}";
- # USE: rspamadm pw
- password = "${pass-chomp "servers/mermet/rspamd/controller/hashedPassword"}";
'';
};
};
};
+security.pass.secrets."rspamd/controller/hashedPassword" = {
+ # Generated with: rspamadm pw
+ user = rspamd.user;
+ pipe = ''sed -e '/.*/password = "\\0"/' '';
+ postRun = "systemctl reload rspamd";
+};
+systemd.services.rspamd = {
+ wants = [ pass.secrets."rspamd/controller/hashedPassword".service ];
+ after = [ pass.secrets."rspamd/controller/hashedPassword".service ];
+};
/*
services.postfix.extraConfig = ''
smtpd_milters = unix:/run/rspamd.sock
diff --git a/servers/mermet/rspamd/autogeree.net.nix b/servers/mermet/rspamd/autogeree.net.nix
index b072e06..17ec9d2 100644
--- a/servers/mermet/rspamd/autogeree.net.nix
+++ b/servers/mermet/rspamd/autogeree.net.nix
@@ -1,18 +1,16 @@
{ domain, ... }:
{ pkgs, lib, config, ... }:
let
- inherit (builtins.extraBuiltins) pass;
+ inherit (config.security) pass;
inherit (config.services) rspamd;
selector = "20200101";
in
{
-systemd.services.rspamd.after =
- [ "dkim.${domain}.${selector}.key-key.service" ];
services.rspamd.dkimSelectorMap = ''
${domain} ${selector}
'';
# rspamadm dkim_keygen -d autogeree.net -s 20200101 -b 4096 -t rsa -k /proc/self/fd/3 3>&1 >>servers/mermet/rspamd/autogeree.net.nix |
-# pass insert -m dkim/autogeree.net/20200101.key
+# pass insert -m servers/mermet/rspamd/dkim/autogeree.net/20200101.key
services.knot.zones."${domain}".data = ''
20200101._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAk15FhAquBY4pcb6HsCqyxK6Sm9AnScsyw7yAOPGQc+26mUKUYTBwywsjAR0zG58tZaCVXZ5EzaRAK/MsKShZ5kwGLzyZoBkexjepcJkP0DuB6WhBQeLhLvdXQVeBuosbqnklW7UHJw0EkNMbThxUrpjwd6P6tmLCFI9pNl2LC3VxfPNu7o8EVgHcuHm4+UCFRUAeHisWasEtD0kVj"
@@ -20,16 +18,12 @@ services.knot.zones."${domain}".data = ''
"+hH+Mr/4V1wnKtdosk/7+3VIQ6clTIfWhD6PlnWd78Uo5lfWnYxTem7EMc2q7j6tzGwj+Q+b4Li9fdhLqxGuD0V64/nVZit90b0HyfiV5srln2lK6Hczrwqr0gOEBGQ4YeLjOF6ldaV01mFWR9ddr9a5/gVCqw8vw7vhqXvU7yK8VHW2rdsvkNZ0bDOa66MCveD7pH2vyljrfZq9k0T/NLHrsu8CAwEAAQ=="
)
'';
-services.nsd.zones."${domain}".data = ''
- 20200101._domainkey IN TXT ( "v=DKIM1; k=rsa; "
- "p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAk15FhAquBY4pcb6HsCqyxK6Sm9AnScsyw7yAOPGQc+26mUKUYTBwywsjAR0zG58tZaCVXZ5EzaRAK/MsKShZ5kwGLzyZoBkexjepcJkP0DuB6WhBQeLhLvdXQVeBuosbqnklW7UHJw0EkNMbThxUrpjwd6P6tmLCFI9pNl2LC3VxfPNu7o8EVgHcuHm4+UCFRUAeHisWasEtD0kVj"
- "vDOoFvLEJ/KNI7jBZYFd8Q6dDL8NF28A3LUpKm/Fk73aW7cLAeigT6wiyuW94gIdU4Co0mXLVbakgiofYNC32L4FsbgFw+UN0XuBJwMZQskD6AkQHhZ0T7wYXCAcPGrbjmrqtPfV9YZSOB6lob3EMcPuZgpikWiT1bgsR7LBAA5KsZpRpuWjnpH4fgay3biEc2kXBvvzh4baozJvhF32vV9bSVc5z0jR9rZjR/qgJKSce8xQa0RfbZLJsVI9TgJ"
- "+hH+Mr/4V1wnKtdosk/7+3VIQ6clTIfWhD6PlnWd78Uo5lfWnYxTem7EMc2q7j6tzGwj+Q+b4Li9fdhLqxGuD0V64/nVZit90b0HyfiV5srln2lK6Hczrwqr0gOEBGQ4YeLjOF6ldaV01mFWR9ddr9a5/gVCqw8vw7vhqXvU7yK8VHW2rdsvkNZ0bDOa66MCveD7pH2vyljrfZq9k0T/NLHrsu8CAwEAAQ=="
- )
-'';
-security.install.shellHook = ''
- pass "dkim/${domain}/${selector}.key" |
- ssh "$target" install -D -m 0400 -o ${rspamd.user} -g root /dev/stdin \
- /run/keys/"dkim.${domain}.${selector}.key"
-'';
+security.pass.secrets."rspamd/dkim/${domain}/${selector}.key" = {
+ user = rspamd.user;
+ postRun = "systemctl reload rspamd";
+};
+systemd.services.rspamd = {
+ wants = [ pass.secrets."rspamd/dkim/${domain}/${selector}.key".service ];
+ after = [ pass.secrets."rspamd/dkim/${domain}/${selector}.key".service ];
+};
}
diff --git a/servers/mermet/rspamd/sourcephile.fr.nix b/servers/mermet/rspamd/sourcephile.fr.nix
index 9307d12..f700c42 100644
--- a/servers/mermet/rspamd/sourcephile.fr.nix
+++ b/servers/mermet/rspamd/sourcephile.fr.nix
@@ -1,18 +1,17 @@
{ domain, ... }:
{ pkgs, lib, config, ... }:
let
- inherit (builtins.extraBuiltins) pass;
- inherit (lib) types;
+ inherit (config.security) pass;
inherit (config.services) rspamd;
selector = "20200101";
in
{
-systemd.services.rspamd.after =
- [ "dkim.${domain}.${selector}.key-key.service" ];
services.rspamd.dkimSelectorMap = ''
mermet ${selector}
${domain} ${selector}
'';
+# rspamadm dkim_keygen -d sourcephile.fr -s 20200101 -b 4096 -t rsa -k /proc/self/fd/3 3>&1 >>servers/mermet/rspamd/sourcephile.fr.nix |
+# pass insert -m servers/mermet/rspamd/dkim/sourcephile.fr/20200101.key
services.knot.zones."${domain}".data = ''
20200101._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7EKzverbG+5JF+yFjH3MrxLyauiHyLqBbV/8LEMunoKXF8sqhBpQtAQXruLqsyUkxR/4CAyPMyzmcdrU43boMj9yFqLrg/kEz2RIvai9jXBqRoWRW1y7F0LbZmdtOTncuDSP8Zzo02XUzsOC4f/C3tEQHS5rc"
@@ -23,19 +22,12 @@ services.knot.zones."${domain}".data = ''
"rWWtSTdO8DilDqN8CAwEAAQ=="
)
'';
-services.nsd.zones."${domain}".data = ''
- 20200101._domainkey IN TXT ( "v=DKIM1; k=rsa; "
- "p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7EKzverbG+5JF+yFjH3MrxLyauiHyLqBbV/8LEMunoKXF8sqhBpQtAQXruLqsyUkxR/4CAyPMyzmcdrU43boMj9yFqLrg/kEz2RIvai9jXBqRoWRW1y7F0LbZmdtOTncuDSP8Zzo02XUzsOC4f/C3tEQHS5rc"
- "hzfhU5FY1CeO6eBMV79qKBOvGMKahQTrrtU6olAAJxOhn6wRuwSf"
- "+m3on1OqiuXYYIgNHKdRhJ8gDwIm/3LEpYMD0gTgJiyclCLoLGHGtKZy1Wf9xV9/7V6fHE4JW5SDivwslVTL+KPXOlIpo5NDHpMxPYOcIg2K4Rj/j7jhavo+fG43q1LhwaPkEMQMbplgnjeMY8300odRiklTkMMpH0m35ZNeHQJSRpEtV8y5xUNxVaGzfqX5iStwV/mQ1Kn"
- "ZSe8ORTNq+eTTFnDk6zdUXjagcf0wO6QsSTeAz/G8CqOBbwmrU+q"
- "F8WbGAeRnhz51mH6fTTfsQ1nwjAiF4ou+eQGTkTMN23KkCKpuozJnxqx4DCEr6J1bL83fhXw7CgcfgKgTOk/HFJpeiGhqodw18r4DWBA6G57z9utm7Mr/9SoVnMq6iK9iEcbCllLR8Sz4viatLSRzhodbk7hfvXS3jmCFjILAjFmA7aMTemDMBDQhpAGF9F8sjFUbEJIZjK"
- "rWWtSTdO8DilDqN8CAwEAAQ=="
- )
-'';
-security.install.shellHook = ''
- pass "dkim/${domain}/${selector}.key" |
- ssh "$target" install -D -m 0400 -o ${rspamd.user} -g root /dev/stdin \
- /run/keys/"dkim.${domain}.${selector}.key"
-'';
+security.pass.secrets."rspamd/dkim/${domain}/${selector}.key" = {
+ user = rspamd.user;
+ postRun = "systemctl reload rspamd";
+};
+systemd.services.rspamd = {
+ after = [ pass.secrets."rspamd/dkim/${domain}/${selector}.key".service ];
+ wants = [ pass.secrets."rspamd/dkim/${domain}/${selector}.key".service ];
+};
}
diff --git a/servers/mermet/system.nix b/servers/mermet/system.nix
index 91cffc9..3146672 100644
--- a/servers/mermet/system.nix
+++ b/servers/mermet/system.nix
@@ -7,6 +7,8 @@ system.stateVersion = "19.09"; # Did you read the comment?
services.unbound.enable = true;
+security.passwordStore = ../../../sec/pass/servers/mermet;
+
environment.systemPackages = with pkgs; [
cryptsetup
direnv
diff --git a/shell.nix b/shell.nix
index b5693d1..281180c 100644
--- a/shell.nix
+++ b/shell.nix
@@ -58,6 +58,7 @@ let
# to expand shellHook and buildInputs of this shell.nix
configuration = {config, ...}: {
imports = [
+ shell/openpgp.nix
];
nix = {
nixConf = ''
@@ -70,7 +71,6 @@ let
gnupg = {
enable = true;
gnupgHome = toString ../sec/gnupg;
- keys = import shell/openpgp.nix;
gpgExtraConf = ''
# julm@sourcephile.fr
trusted-key 0xB2450D97085B7B8C
@@ -185,9 +185,17 @@ pkgs.mkShell {
shellHook = ''
echo >&2 "nix: running shellHook"
+ # password-store
+ export PASSWORD_STORE_DIR="$PWD"/../sec/pass
+
# Nix
PATH=$NIX_SHELL_PATH:$PATH
- export NIX_PATH="servers=$PWD/servers.nix:nixpkgs=${toString pkgs.path}:nixpkgs-overlays=$PWD/nixpkgs/overlays.nix"
+ export NIX_PATH="${lib.concatStringsSep ":" [
+ "servers=$PWD/servers.nix"
+ #"pass=$PASSWORD_STORE_DIR"
+ "nixpkgs=${toString pkgs.path}"
+ "nixpkgs-overlays=$PWD/nixpkgs/overlays.nix"
+ ]}"
# Since the .envrc calls this shellHook
# the EXIT trap cannot be freely used
@@ -197,9 +205,6 @@ pkgs.mkShell {
${modules.nix-shell.shellHook}
- # password-store
- export PASSWORD_STORE_DIR="$PWD"/../sec/pass
-
# gpg
export GPG_TTY=$(tty)
gpg-connect-agent updatestartuptty /bye >/dev/null
diff --git a/shell/modules/development/libraries/nix-plugins.nix b/shell/modules/development/libraries/nix-plugins.nix
index 1b6ada8..ee3d69b 100644
--- a/shell/modules/development/libraries/nix-plugins.nix
+++ b/shell/modules/development/libraries/nix-plugins.nix
@@ -71,6 +71,7 @@ in
pass-to-file = path: name: exec [ "${nix-pass-to-file}" path name ];
git = dir: args: exec ([ "${nix-git}" dir ] ++ args);
git-time = dir: path: exec [ "${nix-git}" dir "log" "-1" "--format=%ct" "--" path ];
+ gpg = args: exec ([ "${pkgs.gnupg}/bin/gpg" ] ++ args);
'';
description = ''
Content put in extra-builtins.nix for nix-plugins.
diff --git a/shell/modules/tools/security/gnupg.nix b/shell/modules/tools/security/gnupg.nix
index 4c2f3b1..f096017 100644
--- a/shell/modules/tools/security/gnupg.nix
+++ b/shell/modules/tools/security/gnupg.nix
@@ -14,13 +14,14 @@ let
, expire ? "-"
, passPath
, subKeys ? {}
+ , postRun ? ""
, ...
}@primary:
''
info "generateKey uid=\"${uid}\""
if ! ${gpg-with-home}/bin/gpg-with-home --list-secret-keys -- "=${uid}" >/dev/null 2>/dev/null
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--quick-generate-key "${uid}" "${algo}" "${unwords usage}" "${expire}"
@@ -35,6 +36,7 @@ let
''
+ unlines (map (generateSubKey primary) subKeys)
+ generateBackupKey "$fpr" primary
+ + postRun
;
generateSubKey =
primary:
@@ -47,7 +49,7 @@ let
info " generateSubKey usage=[${unwords usage}]"
if ! printf '%s\n' "$caps" | ${pkgs.gnugrep}/bin/grep -Fqx "${lettersKeyUsage usage}"
then
- ${pkgs.pass}/bin/pass "${primary.passPath}" |
+ ${if primary.passPath != "" then "${pkgs.pass}/bin/pass '${primary.passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--quick-add-key "$fpr" "${algo}" "${unwords usage}" "${expire}"
@@ -74,9 +76,10 @@ let
fi
'' + (if backupRecipients == [""] then
''
- if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc"
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc" &&
+ ${gpg-with-home}/bin/gpg-with-home --list-secret-keys "${fpr}" | grep -q "sec "
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--pinentry-mode loopback --passphrase-fd 0 \
--armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc" \
@@ -84,7 +87,7 @@ let
fi
if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec" \
@@ -93,7 +96,7 @@ let
fi
if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec" \
@@ -103,7 +106,7 @@ let
'' else ''
if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--pinentry-mode loopback --passphrase-fd 0 \
--armor --gen-revoke "${fpr}" |
@@ -112,7 +115,7 @@ let
fi
if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--armor --export-options export-backup \
@@ -122,7 +125,7 @@ let
fi
if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
+ ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
${gpg-with-home}/bin/gpg-with-home \
--batch --pinentry-mode loopback --passphrase-fd 0 \
--armor --export-options export-backup \
@@ -298,13 +301,12 @@ options.gnupg = {
backupRecipients = ["@john@doe.pro"];
};
};
- type = types.attrsOf (types.submodule ({uid, ...}: {
- #config.uid = lib.mkDefault uid;
+ type = types.attrsOf (types.submodule ({name, ...}: {
options = {
uid = lib.mkOption {
type = types.str;
example = "John Doe <john.doe@example.coop>";
- default = uid;
+ default = name;
description = ''
User ID.
'';
@@ -319,7 +321,7 @@ options.gnupg = {
};
expire = lib.mkOption {
type = types.str;
- default = "1y";
+ default = "0";
example = "1y";
description = ''
Expiration timeout.
@@ -353,7 +355,7 @@ options.gnupg = {
};
expire = lib.mkOption {
type = types.str;
- default = "1y";
+ default = "0";
example = "1y";
description = ''
Expiration timeout.
@@ -378,6 +380,13 @@ options.gnupg = {
Backup keys used to encrypt the a backup copy of the secret keys.
'';
};
+ postRun = lib.mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell code to run after the key has been generated or tested to exist.
+ '';
+ };
};
}));
};
diff --git a/shell/openpgp.nix b/shell/openpgp.nix
index 01937ae..692c6f3 100644
--- a/shell/openpgp.nix
+++ b/shell/openpgp.nix
@@ -1,4 +1,6 @@
+{ pkgs, lib, config, ... }:
{
+gnupg.keys = {
"Julien Moutinho <julm@sourcephile.fr>" = {
uid = "Julien Moutinho <julm@sourcephile.fr>";
algo = "rsa4096";
@@ -6,10 +8,10 @@
usage = ["cert" "sign"];
passPath = "members/julm/gpg/password";
subKeys = [
- { algo = "rsa4096"; expire = "3y"; usage = ["sign"];}
- { algo = "rsa4096"; expire = "3y"; usage = ["encrypt"];}
- { algo = "rsa4096"; expire = "3y"; usage = ["auth"];}
- ];
+ { algo = "rsa4096"; expire = "3y"; usage = ["sign"]; }
+ { algo = "rsa4096"; expire = "3y"; usage = ["encrypt"]; }
+ { algo = "rsa4096"; expire = "3y"; usage = ["auth"]; }
+ ];
backupRecipients = [""];
};
"Julien Moutinho <julm@mermet>" = {
@@ -19,10 +21,36 @@
usage = ["cert" "sign"];
passPath = "members/julm/gpg/password";
subKeys = [
- { algo = "rsa4096"; expire = "3y"; usage = ["sign"];}
- { algo = "rsa4096"; expire = "3y"; usage = ["encrypt"];}
- { algo = "rsa4096"; expire = "3y"; usage = ["auth"];}
- ];
+ { algo = "rsa4096"; expire = "3y"; usage = ["sign"]; }
+ { algo = "rsa4096"; expire = "3y"; usage = ["encrypt"]; }
+ { algo = "rsa4096"; expire = "3y"; usage = ["auth"]; }
+ ];
backupRecipients = [""];
};
+"root@losurdo.sourcephile.fr" = let srv = "losurdo"; in {
+ uid = "root@${srv}.sourcephile.fr";
+ algo = "rsa4096";
+ expire = "0";
+ usage = ["cert" "sign"];
+ passPath = "servers/${srv}/root/key.pass";
+ subKeys = [
+ { algo = "rsa4096"; expire = "0"; usage = ["encrypt"]; }
+ ];
+ backupRecipients = [""];
+ # This subkey is put into a root/key.gpg, and then on losurdo's Nix store,
+ # to decrypt servers.losurdo.config.security.secrets
+ # Its passphrase in root/key.pass is decrypted and sent by ssh before each call to nix copy.
+ postRun = ''
+ info " generate $PASSWORD_STORE_DIR/servers/${srv}/root/key.gpg"
+ test -s "$PASSWORD_STORE_DIR/servers/${srv}/root/key.gpg" || {
+ ${pkgs.gnupg}/bin/gpg --batch --pinentry-mode loopback --export-secret-keys --armor \
+ --passphrase-fd 3 3< <(${pkgs.gnupg}/bin/gpg --decrypt "$PASSWORD_STORE_DIR/servers/${srv}/root/key.pass.gpg") \
+ --export-options export-minimal @root@${srv}.sourcephile.fr |
+ ${pkgs.gnupg}/bin/gpg --symmetric --batch --pinentry-mode loopback \
+ --passphrase-fd 3 3< <(${pkgs.gnupg}/bin/gpg --decrypt "$PASSWORD_STORE_DIR/servers/${srv}/root/key.pass.gpg") \
+ --output "$PASSWORD_STORE_DIR/servers/${srv}/root/key.gpg"
+ }
+ '';
+};
+};
}
--
2.49.0