From da30ae4ffdd153a1eb32fb86f9ca9a65aa19e4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Bouya?= Date: Wed, 13 Oct 2021 02:26:54 +0200 Subject: Move secrets to flakes --- flakes/private/openarc/flake.lock | 14 +++- flakes/private/openarc/flake.nix | 9 ++- flakes/private/opendmarc/flake.lock | 14 +++- flakes/private/opendmarc/flake.nix | 9 ++- flakes/secrets/flake.nix | 124 ++++++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 flakes/secrets/flake.nix (limited to 'flakes') diff --git a/flakes/private/openarc/flake.lock b/flakes/private/openarc/flake.lock index f0f56c7..744d002 100644 --- a/flakes/private/openarc/flake.lock +++ b/flakes/private/openarc/flake.lock @@ -140,7 +140,19 @@ "files-watcher": "files-watcher", "my-lib": "my-lib", "nix-lib": "nix-lib", - "openarc": "openarc" + "openarc": "openarc", + "secrets": "secrets" + } + }, + "secrets": { + "locked": { + "narHash": "sha256-aRHKDVHDpnqpmgGhLGQxXwyTwmPuhUJTVcOLBYtY2ks=", + "path": "../../secrets", + "type": "path" + }, + "original": { + "path": "../../secrets", + "type": "path" } } }, diff --git a/flakes/private/openarc/flake.nix b/flakes/private/openarc/flake.nix index 5c4b73c..b4ab4c8 100644 --- a/flakes/private/openarc/flake.nix +++ b/flakes/private/openarc/flake.nix @@ -3,6 +3,10 @@ path = "../../openarc"; type = "path"; }; + inputs.secrets = { + path = "../../secrets"; + type = "path"; + }; inputs.files-watcher = { path = "../../files-watcher"; type = "path"; @@ -14,14 +18,13 @@ inputs.nix-lib.url = "github:NixOS/nixpkgs"; description = "Private configuration for openarc"; - outputs = { self, nix-lib, my-lib, files-watcher, openarc }: + outputs = { self, nix-lib, my-lib, files-watcher, openarc, secrets }: let cfg = name': { config, lib, pkgs, name, ... }: { imports = [ (my-lib.lib.withNarKey files-watcher "nixosModule") (my-lib.lib.withNarKey openarc "nixosModule") - #FIXME: - #(my-lib.lib.withNarKey secrets "nixosModule") + (my-lib.lib.withNarKey secrets "nixosModule") ]; config = lib.mkIf (name == name') { services.openarc = { diff --git a/flakes/private/opendmarc/flake.lock b/flakes/private/opendmarc/flake.lock index 121f51d..bd5019c 100644 --- a/flakes/private/opendmarc/flake.lock +++ b/flakes/private/opendmarc/flake.lock @@ -123,7 +123,19 @@ "files-watcher": "files-watcher", "my-lib": "my-lib", "nix-lib": "nix-lib", - "opendmarc": "opendmarc" + "opendmarc": "opendmarc", + "secrets": "secrets" + } + }, + "secrets": { + "locked": { + "narHash": "sha256-aRHKDVHDpnqpmgGhLGQxXwyTwmPuhUJTVcOLBYtY2ks=", + "path": "../../secrets", + "type": "path" + }, + "original": { + "path": "../../secrets", + "type": "path" } } }, diff --git a/flakes/private/opendmarc/flake.nix b/flakes/private/opendmarc/flake.nix index debcfbd..2b73070 100644 --- a/flakes/private/opendmarc/flake.nix +++ b/flakes/private/opendmarc/flake.nix @@ -3,6 +3,10 @@ path = "../../opendmarc"; type = "path"; }; + inputs.secrets = { + path = "../../secrets"; + type = "path"; + }; inputs.files-watcher = { path = "../../files-watcher"; type = "path"; @@ -14,14 +18,13 @@ inputs.nix-lib.url = "github:NixOS/nixpkgs"; description = "Private configuration for opendmarc"; - outputs = { self, nix-lib, opendmarc, my-lib, files-watcher }: + outputs = { self, nix-lib, opendmarc, my-lib, files-watcher, secrets }: let cfg = name': { config, lib, pkgs, name, ... }: { imports = [ (my-lib.lib.withNarKey files-watcher "nixosModule") (my-lib.lib.withNarKey opendmarc "nixosModule") - #FIXME: - #(my-lib.lib.withNarKey secrets "nixosModule") + (my-lib.lib.withNarKey secrets "nixosModule") ]; config = lib.mkIf (name == name') { users.users."${config.services.opendmarc.user}".extraGroups = [ "keys" ]; diff --git a/flakes/secrets/flake.nix b/flakes/secrets/flake.nix new file mode 100644 index 0000000..0ee6a40 --- /dev/null +++ b/flakes/secrets/flake.nix @@ -0,0 +1,124 @@ +{ + description = "Secrets handling"; + + outputs = { self }: { + nixosModule = { config, lib, pkgs, ... }: { + options.secrets = with lib; { + keys = mkOption { + type = types.listOf types.unspecified; + default = []; + description = "Keys to upload to server"; + }; + gpgKeys = mkOption { + type = types.listOf types.path; + default = []; + description = "GPG public keys files to encrypt to"; + }; + ageKeys = mkOption { + type = types.listOf types.str; + default = []; + description = "AGE keys to encrypt to"; + }; + decryptKey = mkOption { + type = types.str; + default = "/etc/ssh/ssh_host_ed25519_key"; + description = "ed25519 key used to decrypt with AGE"; + }; + location = mkOption { + type = types.path; + default = "/var/secrets"; + description = "Location where to put the keys"; + }; + secretsVars = mkOption { + type = types.path; + description = "Location where the secrets variables are defined, to be used to fill the templates in secrets"; + }; + deleteSecretsVars = mkOption { + type = types.bool; + default = false; + description = "Delete secrets file after deployment"; + }; + # Read-only variables + fullPaths = mkOption { + type = types.attrsOf types.path; + default = builtins.listToAttrs + (map (v: { name = v.dest; value = "${config.secrets.location}/${v.dest}"; }) config.secrets.keys); + readOnly = true; + description = "set of full paths to secrets"; + }; + }; + + config = let + location = config.secrets.location; + keys = config.secrets.keys; + empty = pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out && touch $out/done"; + fpath = v: "secrets/${v.dest}${lib.optionalString (v.isTemplated or true) ".gucci.tpl"}"; + dumpKey = v: + if v.isDir or false then + '' + mkdir -p secrets/${v.dest} + cat >> mods < ${fpath v} + cat >> mods </dev/null + fingerprints=$fingerprints,$(cat $key | gpg --with-colons --import-options show-only --import 2>/dev/null | grep ^fpr | cut -d: -f10 | head -n1) + done + + sops --age ${builtins.concatStringsSep "," config.secrets.ageKeys} --pgp ''${fingerprints#,} --input-type binary -i -e $out 2>/dev/null + ''; + pathChmodExcl = + let + dirs = builtins.filter (v: v.isDir or false) keys; + exclPath = builtins.concatStringsSep " -o " (map (d: " -path $TMP/${d.dest}") dirs); + in + lib.optionalString (builtins.length dirs > 0) " -not \\( ${exclPath} \\) "; + in lib.mkIf (builtins.length keys > 0) { + system.activationScripts.secrets = { + deps = [ "users" "wrappers" ]; + text = '' + install -m0750 -o root -g keys -d ${location} + TMP=$(${pkgs.coreutils}/bin/mktemp -d) + TMPWORK=$(${pkgs.coreutils}/bin/mktemp -d) + chmod go-rwx $TMPWORK + if [ -n "$TMP" -a -n "$TMPWORK" ]; then + install -m0750 -o root -g keys -d $TMP + ${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i ${config.secrets.decryptKey} -o $TMPWORK/keys.txt + SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${secrets} | ${pkgs.gnutar}/bin/tar --strip-components 1 -C $TMP -x + if [ -f ${config.secrets.secretsVars} ]; then + SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${config.secrets.secretsVars} > $TMPWORK/vars.yml + fi + if [ -f $TMPWORK/vars.yml ]; then + find $TMP -name "*.gucci.tpl" -exec \ + /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}' \; + fi + find $TMP -type d ${pathChmodExcl}-exec chown root:keys {} \; -exec chmod o-rx {} \; + ${pkgs.rsync}/bin/rsync --exclude="*.gucci.tpl" -O -c -av --delete $TMP/ ${location} + rm -rf $TMP $TMPWORK ${lib.optionalString config.secrets.deleteSecretsVars config.secrets.secretsVars} + fi + ''; + }; + + }; + }; + }; +} -- cgit v1.2.3