aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2021-10-07 15:17:30 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2021-10-13 00:00:55 +0200
commit282c67a117b7d349b30a96972b050d630f906dec (patch)
tree6686bdc126d5c0bd548cd6286a41be5c8cfdc01f
parent97f5a24bc8839328571b23eb5f910de206ddbe1f (diff)
downloadNix-282c67a117b7d349b30a96972b050d630f906dec.tar.gz
Nix-282c67a117b7d349b30a96972b050d630f906dec.tar.zst
Nix-282c67a117b7d349b30a96972b050d630f906dec.zip
Refactor secrets handling
-rw-r--r--.envrc1
-rw-r--r--.gitconfig6
-rw-r--r--Makefile2
-rw-r--r--flakes/backports/flake.lock6
-rw-r--r--flakes/backports/flake.nix8
-rw-r--r--modules/private/buildbot/default.nix2
-rw-r--r--modules/private/databases/default.nix2
-rw-r--r--modules/private/environment.nix25
-rw-r--r--modules/private/system.nix7
-rw-r--r--modules/private/system/backup-2.nix5
-rw-r--r--modules/private/system/dilion.nix11
-rw-r--r--modules/private/system/eldiron.nix5
-rw-r--r--modules/private/system/monitoring-1.nix5
-rw-r--r--modules/private/system/quatresaisons.nix5
-rw-r--r--modules/secrets.nix83
-rw-r--r--nixops/.sops.yaml19
-rw-r--r--nixops/Makefile13
-rw-r--r--nixops/default.nix13
-rw-r--r--nixops/public_keys/Immae.pub322
-rwxr-xr-xnixops/scripts/setup12
-rwxr-xr-xnixops/scripts/with_env8
m---------nixops/secrets0
-rw-r--r--shell.nix2
23 files changed, 479 insertions, 83 deletions
diff --git a/.envrc b/.envrc
index b4859fd..438d807 100644
--- a/.envrc
+++ b/.envrc
@@ -1,5 +1,4 @@
1# vim: filetype=bash 1# vim: filetype=bash
2export PASSWORD_STORE_DIR=$(expand_path nixops/secrets)
3export NIX_PATH=nixpkgs=$(cat $(expand_path nix/sources.json) | jq -r '."nixpkgs-nixops".url') 2export NIX_PATH=nixpkgs=$(cat $(expand_path nix/sources.json) | jq -r '."nixpkgs-nixops".url')
4NIX_PATH=$NIX_PATH:nixpkgs-nix=$(cat $(expand_path nix/sources.json) | jq -r '."nixpkgs-nix".url') 3NIX_PATH=$NIX_PATH:nixpkgs-nix=$(cat $(expand_path nix/sources.json) | jq -r '."nixpkgs-nix".url')
5 4
diff --git a/.gitconfig b/.gitconfig
index e503780..7aa8870 100644
--- a/.gitconfig
+++ b/.gitconfig
@@ -1,3 +1,9 @@
1; git config --local include.path '../.gitconfig' 1; git config --local include.path '../.gitconfig'
2[push] 2[push]
3 recurseSubmodules = on-demand 3 recurseSubmodules = on-demand
4; Find a way to include this file automatically?
5; git -C nixops/secrets config --local diff.gpgdiffer.textconv "gpg --quiet -d"
6[diff "gpgdiffer"]
7 textconv = "gpg --quiet -d"
8[diff "sopsdiffer"]
9 textconv = "sops -d"
diff --git a/Makefile b/Makefile
index e5b9284..50fa09f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
1subrecipes = setup nix-info edit_env 1subrecipes = setup nix-info edit_env edit_vars
2subrecipes += ssh-eldiron ssh-backup-2 ssh-monitoring-1 ssh-4c 2subrecipes += ssh-eldiron ssh-backup-2 ssh-monitoring-1 ssh-4c
3subrecipes += debug build dry-run upload deploy next-boot deploy-reboot 3subrecipes += debug build dry-run upload deploy next-boot deploy-reboot
4subrecipes += list-generations delete-generations cleanup 4subrecipes += list-generations delete-generations cleanup
diff --git a/flakes/backports/flake.lock b/flakes/backports/flake.lock
index 0a2de64..5f9289e 100644
--- a/flakes/backports/flake.lock
+++ b/flakes/backports/flake.lock
@@ -17,11 +17,11 @@
17 }, 17 },
18 "nixpkgs": { 18 "nixpkgs": {
19 "locked": { 19 "locked": {
20 "lastModified": 1629925853, 20 "lastModified": 1634032472,
21 "narHash": "sha256-gK2Q0o3Ov5/xT7XhCPbtfoFQ0Ty1Mu2aeWcNX6+XzjQ=", 21 "narHash": "sha256-IoSg358w6nPpTYLWhvN3UgnU6r322dDPOLFXHyqyIkM=",
22 "owner": "NixOS", 22 "owner": "NixOS",
23 "repo": "nixpkgs", 23 "repo": "nixpkgs",
24 "rev": "9e98c9db932a19bbd8fd4d3f879cd94f66270a43", 24 "rev": "3abf4b55b7c991909fde3115827d398dd7c5a299",
25 "type": "github" 25 "type": "github"
26 }, 26 },
27 "original": { 27 "original": {
diff --git a/flakes/backports/flake.nix b/flakes/backports/flake.nix
index 89a9f40..fe65d2a 100644
--- a/flakes/backports/flake.nix
+++ b/flakes/backports/flake.nix
@@ -21,7 +21,9 @@
21 mpd-small = pkgs.mpd-small; 21 mpd-small = pkgs.mpd-small;
22 pg_activity = pkgs.pg_activity; 22 pg_activity = pkgs.pg_activity;
23 signald = pkgs.signald; 23 signald = pkgs.signald;
24 ssh-to-age = pkgs.ssh-to-age;
24 stgit = pkgs.stgit; 25 stgit = pkgs.stgit;
26 sops = pkgs.sops;
25 telegram-cli = pkgs.telegram-cli; 27 telegram-cli = pkgs.telegram-cli;
26 woob = pkgs.python3Packages.woob; 28 woob = pkgs.python3Packages.woob;
27 zrepl = pkgs.zrepl; 29 zrepl = pkgs.zrepl;
@@ -39,7 +41,9 @@
39 mpd-small = flake-utils.lib.mkApp { drv = packages.mpd-small; name = "mpd"; }; 41 mpd-small = flake-utils.lib.mkApp { drv = packages.mpd-small; name = "mpd"; };
40 pg_activity = flake-utils.lib.mkApp { drv = packages.pg_activity; name = "pg_activity"; }; 42 pg_activity = flake-utils.lib.mkApp { drv = packages.pg_activity; name = "pg_activity"; };
41 signald = flake-utils.lib.mkApp { drv = packages.signald; name = "signald"; }; 43 signald = flake-utils.lib.mkApp { drv = packages.signald; name = "signald"; };
44 ssh-to-age = flake-utils.lib.mkApp { drv = packages.ssh-to-age; name = "ssh-to-age"; };
42 stgit = flake-utils.lib.mkApp { drv = packages.stgit; name = "stgit"; }; 45 stgit = flake-utils.lib.mkApp { drv = packages.stgit; name = "stgit"; };
46 sops = flake-utils.lib.mkApp { drv = packages.sops; name = "sops"; };
43 telegram-cli = flake-utils.lib.mkApp { drv = packages.telegram-cli; name = "telegram-cli"; }; 47 telegram-cli = flake-utils.lib.mkApp { drv = packages.telegram-cli; name = "telegram-cli"; };
44 woob = flake-utils.lib.mkApp { drv = packages.woob; name = "woob"; }; 48 woob = flake-utils.lib.mkApp { drv = packages.woob; name = "woob"; };
45 zrepl = flake-utils.lib.mkApp { drv = packages.zrepl; name = "zrepl"; }; 49 zrepl = flake-utils.lib.mkApp { drv = packages.zrepl; name = "zrepl"; };
@@ -67,7 +71,9 @@
67 mpd = final: prev: { mpd = self.packages."${final.system}".mpd; }; 71 mpd = final: prev: { mpd = self.packages."${final.system}".mpd; };
68 pg_activity = final: prev: { pg_activity = self.packages."${final.system}".pg_activity; }; 72 pg_activity = final: prev: { pg_activity = self.packages."${final.system}".pg_activity; };
69 signald = final: prev: { signald = self.packages."${final.system}".signald; }; 73 signald = final: prev: { signald = self.packages."${final.system}".signald; };
74 ssh-to-age = final: prev: { ssh-to-age = self.packages."${final.system}".ssh-to-age; };
70 stgit = final: prev: { stgit = self.packages."${final.system}".stgit; }; 75 stgit = final: prev: { stgit = self.packages."${final.system}".stgit; };
76 sops = final: prev: { sops = self.packages."${final.system}".sops; };
71 telegram-cli = final: prev: { telegram-cli = self.packages."${final.system}".telegram-cli; }; 77 telegram-cli = final: prev: { telegram-cli = self.packages."${final.system}".telegram-cli; };
72 woob = final: prev: { woob = self.packages."${final.system}".woob; }; 78 woob = final: prev: { woob = self.packages."${final.system}".woob; };
73 zrepl = final: prev: { zrepl = self.packages."${final.system}".zrepl; }; 79 zrepl = final: prev: { zrepl = self.packages."${final.system}".zrepl; };
@@ -84,7 +90,9 @@
84 // overlays.mpd final prev 90 // overlays.mpd final prev
85 // overlays.pg_activity final prev 91 // overlays.pg_activity final prev
86 // overlays.signald final prev 92 // overlays.signald final prev
93 // overlays.ssh-to-age final prev
87 // overlays.stgit final prev 94 // overlays.stgit final prev
95 // overlays.sops final prev
88 // overlays.telegram-cli final prev 96 // overlays.telegram-cli final prev
89 // overlays.woob final prev 97 // overlays.woob final prev
90 // overlays.zrepl final prev 98 // overlays.zrepl final prev
diff --git a/modules/private/buildbot/default.nix b/modules/private/buildbot/default.nix
index ac34845..ea0bef6 100644
--- a/modules/private/buildbot/default.nix
+++ b/modules/private/buildbot/default.nix
@@ -138,7 +138,7 @@ in
138 permissions = "0600"; 138 permissions = "0600";
139 user = "buildbot"; 139 user = "buildbot";
140 group = "buildbot"; 140 group = "buildbot";
141 text = builtins.readFile "${config.myEnv.privateFiles}/buildbot_ssh_key"; 141 text = config.myEnv.buildbot.ssh_key.private;
142 dest = "buildbot/ssh_key"; 142 dest = "buildbot/ssh_key";
143 } 143 }
144 ]; 144 ];
diff --git a/modules/private/databases/default.nix b/modules/private/databases/default.nix
index 6cd6feb..1241658 100644
--- a/modules/private/databases/default.nix
+++ b/modules/private/databases/default.nix
@@ -25,7 +25,7 @@ in
25 }; 25 };
26 26
27 openldap = { 27 openldap = {
28 accessFile = "${config.myEnv.privateFiles}/ldap.conf"; 28 accessFile = ../../../nixops/secrets/ldap.conf;
29 baseDn = config.myEnv.ldap.base; 29 baseDn = config.myEnv.ldap.base;
30 rootDn = config.myEnv.ldap.root_dn; 30 rootDn = config.myEnv.ldap.root_dn;
31 rootPw = config.myEnv.ldap.root_pw; 31 rootPw = config.myEnv.ldap.root_pw;
diff --git a/modules/private/environment.nix b/modules/private/environment.nix
index f0af572..65d9f0a 100644
--- a/modules/private/environment.nix
+++ b/modules/private/environment.nix
@@ -805,6 +805,15 @@ in
805 description = "Buildbot configuration"; 805 description = "Buildbot configuration";
806 type = submodule { 806 type = submodule {
807 options = { 807 options = {
808 ssh_key = mkOption {
809 description = "SSH key information";
810 type = submodule {
811 options = {
812 public = mkOption { type = str; description = "Public part of the key"; };
813 private = mkOption { type = lines; description = "Private part of the key"; };
814 };
815 };
816 };
808 workerPassword = mkOption { description = "Buildbot worker password"; type = str; }; 817 workerPassword = mkOption { description = "Buildbot worker password"; type = str; };
809 user = mkOption { 818 user = mkOption {
810 description = "Buildbot user"; 819 description = "Buildbot user";
@@ -961,6 +970,15 @@ in
961 type = submodule { 970 type = submodule {
962 options = { 971 options = {
963 ldap = mkLdapOptions "Gitolite" {}; 972 ldap = mkLdapOptions "Gitolite" {};
973 ssh_key = mkOption {
974 description = "SSH key information";
975 type = submodule {
976 options = {
977 public = mkOption { type = str; description = "Public part of the key"; };
978 private = mkOption { type = lines; description = "Private part of the key"; };
979 };
980 };
981 };
964 }; 982 };
965 }; 983 };
966 }; 984 };
@@ -1461,13 +1479,6 @@ in
1461 }; 1479 };
1462 }; 1480 };
1463 }; 1481 };
1464
1465 privateFiles = mkOption {
1466 type = path;
1467 description = ''
1468 Path to secret files to make available during build
1469 '';
1470 };
1471 }; 1482 };
1472 options.hostEnv = mkOption { 1483 options.hostEnv = mkOption {
1473 readOnly = true; 1484 readOnly = true;
diff --git a/modules/private/system.nix b/modules/private/system.nix
index 0e72d99..c7e277c 100644
--- a/modules/private/system.nix
+++ b/modules/private/system.nix
@@ -4,7 +4,12 @@
4 networking.extraHosts = builtins.concatStringsSep "\n" 4 networking.extraHosts = builtins.concatStringsSep "\n"
5 (lib.mapAttrsToList (n: v: "${v.config.hostEnv.ips.main.ip4} ${n}") nodes); 5 (lib.mapAttrsToList (n: v: "${v.config.hostEnv.ips.main.ip4} ${n}") nodes);
6 6
7 users.extraUsers.root.openssh.authorizedKeys.keyFiles = [ "${config.myEnv.privateFiles}/id_ed25519.pub" ]; 7 users.extraUsers.root.openssh.authorizedKeys.keys = [ config.myEnv.sshd.rootKeys.nix_repository ];
8 secrets.deleteSecretsVars = true;
9 secrets.gpgKeys = [
10 ../../nixops/public_keys/Immae.pub
11 ];
12
8 services.openssh.enable = true; 13 services.openssh.enable = true;
9 14
10 services.duplyBackup.profiles.system = { 15 services.duplyBackup.profiles.system = {
diff --git a/modules/private/system/backup-2.nix b/modules/private/system/backup-2.nix
index d1064c7..1f226c0 100644
--- a/modules/private/system/backup-2.nix
+++ b/modules/private/system/backup-2.nix
@@ -1,4 +1,3 @@
1{ privateFiles }:
2{ config, pkgs, resources, name, ... }: 1{ config, pkgs, resources, name, ... }:
3{ 2{
4 deployment = { 3 deployment = {
@@ -6,8 +5,10 @@
6 targetHost = config.hostEnv.ips.main.ip4; 5 targetHost = config.hostEnv.ips.main.ip4;
7 substituteOnDestination = true; 6 substituteOnDestination = true;
8 }; 7 };
8 # ssh-keyscan backup-2 | nix-shell -p ssh-to-age --run ssh-to-age
9 secrets.ageKeys = [ "age1kk3nr27qu42j28mcfdag5lhq0zu2pky7gfanvne8l4z2ctevjpgskmw0sr" ];
9 boot.kernelPackages = pkgs.linuxPackages_latest; 10 boot.kernelPackages = pkgs.linuxPackages_latest;
10 myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; }; 11 myEnv = import ../../../nixops/secrets/environment.nix;
11 12
12 imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ] ++ builtins.attrValues (import ../..); 13 imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ] ++ builtins.attrValues (import ../..);
13 14
diff --git a/modules/private/system/dilion.nix b/modules/private/system/dilion.nix
index a59d607..b9be8b0 100644
--- a/modules/private/system/dilion.nix
+++ b/modules/private/system/dilion.nix
@@ -1,4 +1,3 @@
1{ privateFiles }:
2{ config, pkgs, name, lib, ... }: 1{ config, pkgs, name, lib, ... }:
3{ 2{
4 deployment = { 3 deployment = {
@@ -6,6 +5,8 @@
6 targetHost = config.hostEnv.ips.main.ip4; 5 targetHost = config.hostEnv.ips.main.ip4;
7 substituteOnDestination = true; 6 substituteOnDestination = true;
8 }; 7 };
8 # ssh-keyscan dilion | nix-shell -p ssh-to-age --run ssh-to-age
9 secrets.ageKeys = [ "age1x49n6qa0arkdpq8530s7umgm0gqkq90exv4jep97q30rfnzknpaqate06a" ];
9 nixpkgs.system = lib.mkOverride 900 "x86_64-linux"; 10 nixpkgs.system = lib.mkOverride 900 "x86_64-linux";
10 boot = { 11 boot = {
11 loader = { 12 loader = {
@@ -31,7 +32,7 @@
31 powerManagement.cpuFreqGovernor = "powersave"; 32 powerManagement.cpuFreqGovernor = "powersave";
32 hardware.enableRedistributableFirmware = true; 33 hardware.enableRedistributableFirmware = true;
33 34
34 myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; }; 35 myEnv = import ../../../nixops/secrets/environment.nix;
35 36
36 swapDevices = [ { label = "swap"; } ]; 37 swapDevices = [ { label = "swap"; } ];
37 fileSystems = { 38 fileSystems = {
@@ -88,10 +89,10 @@
88 isSystemUser = true; 89 isSystemUser = true;
89 group = "libvirtd"; 90 group = "libvirtd";
90 packages = [ pkgs.netcat-openbsd ]; 91 packages = [ pkgs.netcat-openbsd ];
91 openssh.authorizedKeys.keyFiles = [ 92 openssh.authorizedKeys.keys = [
92 "${privateFiles}/buildbot_ssh_key.pub" 93 config.myEnv.buildbot.ssh_key.public
94 config.myEnv.sshd.rootKeys.ismael_flony
93 ]; 95 ];
94 openssh.authorizedKeys.keys = [ config.myEnv.sshd.rootKeys.ismael_flony ];
95 }; 96 };
96 97
97 users.users.backup = { 98 users.users.backup = {
diff --git a/modules/private/system/eldiron.nix b/modules/private/system/eldiron.nix
index 4fb18a0..6c570c8 100644
--- a/modules/private/system/eldiron.nix
+++ b/modules/private/system/eldiron.nix
@@ -1,4 +1,3 @@
1{ privateFiles }:
2{ config, pkgs, lib, ... }: 1{ config, pkgs, lib, ... }:
3{ 2{
4 deployment = { 3 deployment = {
@@ -6,6 +5,8 @@
6 targetHost = config.hostEnv.ips.main.ip4; 5 targetHost = config.hostEnv.ips.main.ip4;
7 substituteOnDestination = true; 6 substituteOnDestination = true;
8 }; 7 };
8 # ssh-keyscan eldiron | nix-shell -p ssh-to-age --run ssh-to-age
9 secrets.ageKeys = [ "age1dxr5lhvtnjssfaqpnf6qx80h8gfwkxg3tdf35m6n9wljmk7wadfs3kmahj" ];
9 boot = { 10 boot = {
10 kernelModules = [ "kvm-intel" ]; 11 kernelModules = [ "kvm-intel" ];
11 blacklistedKernelModules = [ "nvidiafb" ]; 12 blacklistedKernelModules = [ "nvidiafb" ];
@@ -28,7 +29,7 @@
28 ''; 29 '';
29 nix.maxJobs = 8; 30 nix.maxJobs = 8;
30 powerManagement.cpuFreqGovernor = "powersave"; 31 powerManagement.cpuFreqGovernor = "powersave";
31 myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; }; 32 myEnv = import ../../../nixops/secrets/environment.nix;
32 33
33 fileSystems = { 34 fileSystems = {
34 # pools: 35 # pools:
diff --git a/modules/private/system/monitoring-1.nix b/modules/private/system/monitoring-1.nix
index 2198d09..e335080 100644
--- a/modules/private/system/monitoring-1.nix
+++ b/modules/private/system/monitoring-1.nix
@@ -1,4 +1,3 @@
1{ privateFiles }:
2{ config, pkgs, resources, ... }: 1{ config, pkgs, resources, ... }:
3{ 2{
4 deployment = { 3 deployment = {
@@ -6,8 +5,10 @@
6 targetHost = config.hostEnv.ips.main.ip4; 5 targetHost = config.hostEnv.ips.main.ip4;
7 substituteOnDestination = true; 6 substituteOnDestination = true;
8 }; 7 };
8 # ssh-keyscan monitoring-1 | nix-shell -p ssh-to-age --run ssh-to-age
9 secrets.ageKeys = [ "age1dn4lzhgxusqrpjjnzm7w8ml39ptf326htuzmpqdqs2gg3wq7cqzqxuvx8k" ];
9 boot.kernelPackages = pkgs.linuxPackages_latest; 10 boot.kernelPackages = pkgs.linuxPackages_latest;
10 myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; }; 11 myEnv = import ../../../nixops/secrets/environment.nix;
11 12
12 imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ] ++ builtins.attrValues (import ../..); 13 imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ] ++ builtins.attrValues (import ../..);
13 14
diff --git a/modules/private/system/quatresaisons.nix b/modules/private/system/quatresaisons.nix
index 03446e7..0148650 100644
--- a/modules/private/system/quatresaisons.nix
+++ b/modules/private/system/quatresaisons.nix
@@ -1,4 +1,3 @@
1{ privateFiles }:
2{ config, pkgs, lib, ... }: 1{ config, pkgs, lib, ... }:
3let 2let
4 serverSpecificConfig = config.myEnv.serverSpecific.quatresaisons; 3 serverSpecificConfig = config.myEnv.serverSpecific.quatresaisons;
@@ -164,6 +163,8 @@ in
164 targetHost = config.hostEnv.ips.main.ip4; 163 targetHost = config.hostEnv.ips.main.ip4;
165 substituteOnDestination = true; 164 substituteOnDestination = true;
166 }; 165 };
166 # ssh-keyscan quatresaison | nix-shell -p ssh-to-age --run ssh-to-age
167 secrets.ageKeys = [ "age1yz8u6xvh2fltvyp96ep8crce3qx4tuceyhun6pwddfe0uvcrkarscxl7e7" ];
167 168
168 programs.ssh.package = pkgs.openssh.overrideAttrs(old: { 169 programs.ssh.package = pkgs.openssh.overrideAttrs(old: {
169 PATH_PASSWD_PROG = "/run/wrappers/bin/passwd"; 170 PATH_PASSWD_PROG = "/run/wrappers/bin/passwd";
@@ -173,7 +174,7 @@ in
173 imports = builtins.attrValues (import ../..) ++ 174 imports = builtins.attrValues (import ../..) ++
174 [ ./quatresaisons/nextcloud.nix ./quatresaisons/databases.nix ]; 175 [ ./quatresaisons/nextcloud.nix ./quatresaisons/databases.nix ];
175 176
176 myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; }; 177 myEnv = import ../../../nixops/secrets/environment.nix;
177 178
178 fileSystems = { 179 fileSystems = {
179 "/" = { device = "/dev/disk/by-uuid/865931b4-c5cc-439f-8e42-8072c7a30634"; fsType = "ext4"; }; 180 "/" = { device = "/dev/disk/by-uuid/865931b4-c5cc-439f-8e42-8072c7a30634"; fsType = "ext4"; };
diff --git a/modules/secrets.nix b/modules/secrets.nix
index ecc1ebc..86d276a 100644
--- a/modules/secrets.nix
+++ b/modules/secrets.nix
@@ -6,11 +6,36 @@
6 default = []; 6 default = [];
7 description = "Keys to upload to server"; 7 description = "Keys to upload to server";
8 }; 8 };
9 gpgKeys = lib.mkOption {
10 type = lib.types.listOf lib.types.path;
11 default = [];
12 description = "GPG public keys files to encrypt to";
13 };
14 ageKeys = lib.mkOption {
15 type = lib.types.listOf lib.types.str;
16 default = [];
17 description = "AGE keys to encrypt to";
18 };
19 decryptKey = lib.mkOption {
20 type = lib.types.str;
21 default = "/etc/ssh/ssh_host_ed25519_key";
22 description = "ed25519 key used to decrypt with AGE";
23 };
9 location = lib.mkOption { 24 location = lib.mkOption {
10 type = lib.types.path; 25 type = lib.types.path;
11 default = "/var/secrets"; 26 default = "/var/secrets";
12 description = "Location where to put the keys"; 27 description = "Location where to put the keys";
13 }; 28 };
29 secretsVars = lib.mkOption {
30 type = lib.types.path;
31 default = "/run/keys/vars.yml";
32 description = "Location where the secrets variables are defined, to be used to fill the templates in secrets";
33 };
34 deleteSecretsVars = lib.mkOption {
35 type = lib.types.bool;
36 default = false;
37 description = "Delete secrets file after deployment";
38 };
14 # Read-only variables 39 # Read-only variables
15 fullPaths = lib.mkOption { 40 fullPaths = lib.mkOption {
16 type = lib.types.attrsOf lib.types.path; 41 type = lib.types.attrsOf lib.types.path;
@@ -33,56 +58,56 @@
33 ${v.user or "root"} ${v.group or "root"} ${v.permissions or "0600"} ${fpath v} 58 ${v.user or "root"} ${v.group or "root"} ${v.permissions or "0600"} ${fpath v}
34 EOF 59 EOF
35 ''; 60 '';
36 secrets = pkgs.runCommand "secrets.tar" {} '' 61 secrets = pkgs.runCommand "secrets.tar.enc" {
62 buildInputs = [ pkgs.gnupg pkgs.sops ];
63 } ''
37 touch mods 64 touch mods
38 tar --format=ustar --mtime='1970-01-01' -P --transform="s@${empty}@secrets@" -cf $out ${empty}/done 65 tar --format=ustar --mtime='1970-01-01' -P --transform="s@${empty}@secrets@" -cf $out ${empty}/done
39 ${builtins.concatStringsSep "\n" (map dumpKey keys)} 66 ${builtins.concatStringsSep "\n" (map dumpKey keys)}
40 cat mods | while read u g p k; do 67 cat mods | while read u g p k; do
41 tar --format=ustar --mtime='1970-01-01' --owner="$u" --group="$g" --mode="$p" --append -f $out "$k" 68 tar --format=ustar --mtime='1970-01-01' --owner="$u" --group="$g" --mode="$p" --append -f $out "$k"
42 done 69 done
70 export HOME=$(pwd)
71 fingerprints=
72 for key in ${builtins.concatStringsSep " " config.secrets.gpgKeys}; do
73 gpg --import $key 2>/dev/null
74 fingerprints=$fingerprints,$(cat $key | gpg --with-colons --import-options show-only --import 2>/dev/null | grep ^fpr | cut -d: -f10 | head -n1)
75 done
76
77 sops --age ${builtins.concatStringsSep "," config.secrets.ageKeys} --pgp ''${fingerprints#,} --input-type binary -i -e $out 2>/dev/null
43 ''; 78 '';
44 in lib.mkIf (builtins.length keys > 0) { 79 in lib.mkIf (builtins.length keys > 0) {
45 system.activationScripts.secrets = { 80 system.activationScripts.secrets = {
46 deps = [ "users" "wrappers" ]; 81 deps = [ "users" "wrappers" ];
47 text = '' 82 text = ''
48 install -m0750 -o root -g keys -d ${location} 83 install -m0750 -o root -g keys -d ${location}
49 if [ -f /run/keys/secrets.tar ]; then 84 TMP=$(${pkgs.coreutils}/bin/mktemp -d)
50 if [ ! -f ${location}/currentSecrets ] || ! sha512sum -c --status "${location}/currentSecrets"; then 85 TMPWORK=$(${pkgs.coreutils}/bin/mktemp -d)
51 echo "rebuilding secrets" 86 chmod go-rwx $TMPWORK
52 TMP=$(${pkgs.coreutils}/bin/mktemp -d) 87 if [ -n "$TMP" -a -n "$TMPWORK" ]; then
53 if [ -n "$TMP" ]; then 88 install -m0750 -o root -g keys -d $TMP
54 install -m0750 -o root -g keys -d $TMP 89 ${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i ${config.secrets.decryptKey} -o $TMPWORK/keys.txt
55 ${pkgs.gnutar}/bin/tar --strip-components 1 -C $TMP -xf /run/keys/secrets.tar 90 SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${secrets} | ${pkgs.gnutar}/bin/tar --strip-components 1 -C $TMP -x
56 if [ -f /run/keys/vars.yml ]; then 91 if [ -f ${config.secrets.secretsVars} ]; then
57 find $TMP -name "*.gucci.tpl" -exec \ 92 SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${config.secrets.secretsVars} > $TMPWORK/vars.yml
58 /bin/sh -c 'f="{}"; ${pkgs.gucci}/bin/gucci -f /run/keys/vars.yml "$f" > "''${f%.gucci.tpl}"; touch --reference "$f" ''${f%.gucci.tpl} ; chmod --reference="$f" ''${f%.gucci.tpl} ; chown --reference="$f" ''${f%.gucci.tpl}' \;
59 sha512sum /run/keys/secrets.tar /run/keys/vars.yml > $TMP/currentSecrets
60 else
61 sha512sum /run/keys/secrets.tar > $TMP/currentSecrets
62 fi
63 find $TMP -type d -exec chown root:keys {} \; -exec chmod o-rx {} \;
64 ${pkgs.rsync}/bin/rsync --exclude="*.gucci.tpl" -O -c -av --delete $TMP/ ${location}
65 rm -rf $TMP
66 fi
67 fi 93 fi
94 if [ -f $TMPWORK/vars.yml ]; then
95 find $TMP -name "*.gucci.tpl" -exec \
96 /bin/sh -c 'f="{}"; ${pkgs.gucci}/bin/gucci -f '$TMPWORK'/vars.yml "$f" > "''${f%.gucci.tpl}"; touch --reference "$f" ''${f%.gucci.tpl} ; chmod --reference="$f" ''${f%.gucci.tpl} ; chown --reference="$f" ''${f%.gucci.tpl}' \;
97 fi
98 find $TMP -type d -exec chown root:keys {} \; -exec chmod o-rx {} \;
99 ${pkgs.rsync}/bin/rsync --exclude="*.gucci.tpl" -O -c -av --delete $TMP/ ${location}
100 rm -rf $TMP $TMPWORK ${lib.optionalString config.secrets.deleteSecretsVars config.secrets.secretsVars}
68 fi 101 fi
69 ''; 102 '';
70 }; 103 };
71 104
72 system.extraDependencies = [ secrets ];
73 deployment.secrets."secret_vars.yml" = { 105 deployment.secrets."secret_vars.yml" = {
74 source = builtins.toString <privateFiles/vars.yml>; 106 source = builtins.toString ../nixops/secrets/vars.yml;
75 destination = "/run/keys/vars.yml"; 107 destination = config.secrets.secretsVars;
76 owner.user = "root"; 108 owner.user = "root";
77 owner.group = "root"; 109 owner.group = "root";
78 permissions = "0400"; 110 permissions = "0400";
79 }; 111 };
80 deployment.secrets."secrets.tar" = {
81 source = "${secrets}";
82 destination = "/run/keys/secrets.tar";
83 owner.user = "root";
84 owner.group = "root";
85 permissions = "0400";
86 };
87 }; 112 };
88} 113}
diff --git a/nixops/.sops.yaml b/nixops/.sops.yaml
new file mode 100644
index 0000000..04826a2
--- /dev/null
+++ b/nixops/.sops.yaml
@@ -0,0 +1,19 @@
1keys:
2 - &Immae F82806FDA1BF5B9A1B3014E7C9FCED6CA6B79454
3 # obtained with: ssh-keyscan eldiron | nix-shell -p ssh-to-age --run ssh-to-age
4 - &eldiron age1dxr5lhvtnjssfaqpnf6qx80h8gfwkxg3tdf35m6n9wljmk7wadfs3kmahj
5 - &monitoring-1 age1dn4lzhgxusqrpjjnzm7w8ml39ptf326htuzmpqdqs2gg3wq7cqzqxuvx8k
6 - &backup-2 age1kk3nr27qu42j28mcfdag5lhq0zu2pky7gfanvne8l4z2ctevjpgskmw0sr
7 - &dilion age1x49n6qa0arkdpq8530s7umgm0gqkq90exv4jep97q30rfnzknpaqate06a
8 - &quatresaisons age1yz8u6xvh2fltvyp96ep8crce3qx4tuceyhun6pwddfe0uvcrkarscxl7e7
9creation_rules:
10 - path_regex: vars.yml
11 key_groups:
12 - pgp:
13 - *Immae
14 age:
15 - *eldiron
16 - *monitoring-1
17 - *backup-2
18 - *dilion
19 - *quatresaisons
diff --git a/nixops/Makefile b/nixops/Makefile
index f3d06ef..fb9da4c 100644
--- a/nixops/Makefile
+++ b/nixops/Makefile
@@ -26,7 +26,14 @@ endif
26SSH_ARGS ?= 26SSH_ARGS ?=
27 27
28edit_env: 28edit_env:
29 pass edit Nixops/files/environment.nix || true 29 $(EDITOR) secrets/environment.nix || true
30 git -C secrets add environment.nix || true
31 git -C secrets commit -m "Edit environment.nix" environment.nix || true
32
33edit_vars:
34 sops secrets/vars.yml || true
35 git -C secrets add vars.yml || true
36 git -C secrets commit -m "Edit password for vars.yml using sops." vars.yml || true
30 37
31ssh-eldiron: 38ssh-eldiron:
32 ./scripts/with_env bash -c 'ssh -i $$SSH_IDENTITY_FILE root@eldiron $(SSH_ARGS)' 39 ./scripts/with_env bash -c 'ssh -i $$SSH_IDENTITY_FILE root@eldiron $(SSH_ARGS)'
@@ -77,8 +84,8 @@ list-generations:
77.PHONY: list-generations 84.PHONY: list-generations
78 85
79delete-generations: 86delete-generations:
80 echo "make sure you ran a complete build before cleaning up!" 87 @echo "making sure that a complete build is done before cleaning up"
81 false 88 $(MAKE) build MORPH_ARGS=--keep-result
82 nix-env -p $(PROFILE) --delete-generations $(GEN) 89 nix-env -p $(PROFILE) --delete-generations $(GEN)
83 $(MAKE) ssh-eldiron SSH_ARGS="nix-env -p /nix/var/nix/profiles/system --delete-generations $(GEN)" 90 $(MAKE) ssh-eldiron SSH_ARGS="nix-env -p /nix/var/nix/profiles/system --delete-generations $(GEN)"
84 $(MAKE) ssh-dilion SSH_ARGS="nix-env -p /nix/var/nix/profiles/system --delete-generations $(GEN)" 91 $(MAKE) ssh-dilion SSH_ARGS="nix-env -p /nix/var/nix/profiles/system --delete-generations $(GEN)"
diff --git a/nixops/default.nix b/nixops/default.nix
index f048c80..1241443 100644
--- a/nixops/default.nix
+++ b/nixops/default.nix
@@ -1,11 +1,8 @@
1let
2 privateFiles = <privateFiles>;
3in
4{ 1{
5 dilion = import ../modules/private/system/dilion.nix { inherit privateFiles; }; 2 dilion = import ../modules/private/system/dilion.nix;
6 eldiron = import ../modules/private/system/eldiron.nix { inherit privateFiles; }; 3 eldiron = import ../modules/private/system/eldiron.nix;
7 backup-2 = import ../modules/private/system/backup-2.nix { inherit privateFiles; }; 4 backup-2 = import ../modules/private/system/backup-2.nix;
8 monitoring-1 = import ../modules/private/system/monitoring-1.nix { inherit privateFiles; }; 5 monitoring-1 = import ../modules/private/system/monitoring-1.nix;
9 6
10 quatresaisons = import ../modules/private/system/quatresaisons.nix { inherit privateFiles; }; 7 quatresaisons = import ../modules/private/system/quatresaisons.nix;
11} 8}
diff --git a/nixops/public_keys/Immae.pub b/nixops/public_keys/Immae.pub
new file mode 100644
index 0000000..dd42b04
--- /dev/null
+++ b/nixops/public_keys/Immae.pub
@@ -0,0 +1,322 @@
1-----BEGIN PGP PUBLIC KEY BLOCK-----
2
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
320DRuKdymWEii1jA9KlmheLTFr
321=r9L+
322-----END PGP PUBLIC KEY BLOCK-----
diff --git a/nixops/scripts/setup b/nixops/scripts/setup
index 9bdb8df..db0f353 100755
--- a/nixops/scripts/setup
+++ b/nixops/scripts/setup
@@ -44,23 +44,21 @@ if [ "$(git config --get include.path)" != "../.gitconfig" ]; then
44 fi 44 fi
45fi 45fi
46 46
47gpg_keys=$(pass ls Nixops/GPGKeys | sed -e "1d" | cut -d" " -f2) 47for key in public_keys/*; do
48for key in $gpg_keys; do 48 fpr=$(cat "$key" | gpg --import-options show-only --import --with-colons | grep -e "^pub" | cut -d':' -f5)
49 content=$(pass show Nixops/GPGKeys/$key)
50 fpr=$(echo "$content" | gpg --import-options show-only --import --with-colons | grep -e "^pub" | cut -d':' -f5)
51 gpg --list-key "$fpr" >/dev/null 2>/dev/null && imported=yes || imported=no 49 gpg --list-key "$fpr" >/dev/null 2>/dev/null && imported=yes || imported=no
52 # /usr/share/doc/gnupg/DETAILS field 2 50 # /usr/share/doc/gnupg/DETAILS field 2
53 (echo "$content" | gpg --import-options show-only --import --with-colons | 51 (cat "$key" | gpg --import-options show-only --import --with-colons |
54 grep -E '^pub:' | 52 grep -E '^pub:' |
55 cut -d':' -f2 | 53 cut -d':' -f2 |
56 grep -q '[fu]') && signed=yes || signed=no 54 grep -q '[fu]') && signed=yes || signed=no
57 if [ "$signed" = no -o "$imported" = no ] ; then 55 if [ "$signed" = no -o "$imported" = no ] ; then
58 echo "The key for $key needs to be imported and signed (a local signature is enough)" 56 echo "The key for $key needs to be imported and signed (a local signature is enough)"
59 echo "$content" | gpg --import-options show-only --import 57 cat "$key" | gpg --import-options show-only --import
60 echo "Continue? [y/N]" 58 echo "Continue? [y/N]"
61 read y 59 read y
62 if [ "$y" = "y" -o "$y" = "Y" ]; then 60 if [ "$y" = "y" -o "$y" = "Y" ]; then
63 echo "$content" | gpg --import 61 cat "$key" | gpg --import
64 gpg --expert --edit-key "$fpr" lsign quit 62 gpg --expert --edit-key "$fpr" lsign quit
65 else 63 else
66 echo "Aborting" 64 echo "Aborting"
diff --git a/nixops/scripts/with_env b/nixops/scripts/with_env
index f8e5537..c570ccf 100755
--- a/nixops/scripts/with_env
+++ b/nixops/scripts/with_env
@@ -15,14 +15,8 @@ finish() {
15 15
16trap finish EXIT 16trap finish EXIT
17 17
18# pass cannot "just" list files in a directory without showing a tree :( 18sops -d secrets/vars.yml | yq -r .ssl_keys.nix_repository > $TEMP/id_ed25519
19files=$(pass ls Nixops/files | sed -e '1d' -e 's/^.* //')
20 19
21for file in $files; do
22 pass show "Nixops/files/$file" > $TEMP/$file
23done
24
25export NIX_PATH="privateFiles=$TEMP:$NIX_PATH"
26export SSH_IDENTITY_FILE="$TEMP/id_ed25519" 20export SSH_IDENTITY_FILE="$TEMP/id_ed25519"
27 21
28"$@" 22"$@"
diff --git a/nixops/secrets b/nixops/secrets
Subproject 3b4bfa3cddc7f0621d1bec042a388c32e38245f Subproject a1e6498139cc51a3d68e5655480542e6ccd3a45
diff --git a/shell.nix b/shell.nix
index 3aa03a7..2295f8c 100644
--- a/shell.nix
+++ b/shell.nix
@@ -14,5 +14,5 @@ let
14 }); 14 });
15in 15in
16pkgs.mkShell { 16pkgs.mkShell {
17 buildInputs = [ patchedNix pkgs.morph pkgs.niv pkgs.pass pkgs.curl pkgs.shellcheck pkgs.jq pkgs.gnumake ]; 17 buildInputs = [ patchedNix pkgs.sops pkgs.morph pkgs.niv pkgs.curl pkgs.shellcheck pkgs.jq pkgs.gnumake pkgs.yq ];
18} 18}