From 6666d56b65ba291ae04be5c889597d7dc4087fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Gro=C3=9Fmann?= Date: Sat, 28 Jan 2023 18:41:31 +0100 Subject: [PATCH] feat: rekey funktioniert. --- apps/rekey.nix | 79 +++++++++++++++++++++++++++++++++++ configuration.nix | 12 +----- flake.lock | 16 +++++++ flake.nix | 51 +++++++++++++---------- modules/rekey-drv.nix | 20 +++++++++ modules/rekey.nix | 97 ++++--------------------------------------- 6 files changed, 154 insertions(+), 121 deletions(-) create mode 100644 apps/rekey.nix create mode 100644 modules/rekey-drv.nix diff --git a/apps/rekey.nix b/apps/rekey.nix new file mode 100644 index 0000000..67bfe73 --- /dev/null +++ b/apps/rekey.nix @@ -0,0 +1,79 @@ +{ + self, + nixpkgs, + ... +}: system: +with nixpkgs.lib; let + pkgs = import nixpkgs {inherit system;}; + + rekeyCommandForHost = hostName: hostAttrs: let + secretPath = "/tmp/nix-rekey.d/${hostName}/"; + masterIdentities = strings.concatMapStrings (x: "-i ${x} ") hostAttrs.config.rekey.masterIdentityPaths; + + rekeyCommand = secretName: secretAttrs: let + pubKeyStr = let + key = hostAttrs.config.rekey.pubKey; + in + if isPath key + then readFile key + else key; + in '' + echo "Rekeying secret ${secretName} for host ${hostName}" + echo "${secretAttrs.file}" + ${pkgs.rage}/bin/rage ${masterIdentities} -d ${secretAttrs.file} \ + | ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "${secretPath}/${secretName}.age" -e \ + || { echo "Could not rekey secrets. Inserting dummy values" \ + ; echo "Invalide due to failure when rekeying." \ + | ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "${secretPath}/${secretName}.age" -e ;} + ''; + in + if masterIdentities == "" + then '' + echo "No Identities set for host ${hostName}. Can not decrypt.\n\ + Make sure you set 'config.rekey.masterIdentityPaths'" + '' + else if + let + key = hostAttrs.config.rekey.pubKey; + in + isPath key && (! pathExists key) + then '' + echo "No public keys available for host ${hostName}. Can not decrypt.\n\ + Make sure the public keys are reachable by the building system'" + '' + else '' + mkdir -p ${secretPath} + # TODO + ${concatStringsSep "\n" (mapAttrsToList rekeyCommand (hostAttrs.config.rekey.secrets))} + + nix run --extra-sandbox-paths /tmp/nix-rekey.d/ "${../.}#rekey-copy-secrets" + ''; + + rekeyScript = '' + set -euo pipefail + + ${concatStringsSep "\n" (mapAttrsToList rekeyCommandForHost self.nixosConfigurations)} + + ''; + + rekey-exe = pkgs.writeShellScript "rekey.sh" rekeyScript; + + rekey-copy-secretsForHost = hostName: hostAttrs: let + secretFiles = mapAttrsToList (_: x: x.file) hostAttrs.config.rekey.secrets; + drv = import ../modules/rekey-drv.nix pkgs secretFiles; + in '' + echo ${drv} + ''; + rekey-copy-secrets-exe = pkgs.writeShellScript "rekey-copy-secrets.sh" '' + ${concatStringsSep "\n" (mapAttrsToList rekey-copy-secretsForHost self.nixosConfigurations)} + ''; +in { + rekey = { + type = "app"; + program = "${rekey-exe}"; + }; + rekey-copy-secrets = { + type = "app"; + program = "${rekey-copy-secrets-exe}"; + }; +} diff --git a/configuration.nix b/configuration.nix index e64567f..8917663 100644 --- a/configuration.nix +++ b/configuration.nix @@ -28,7 +28,6 @@ rekey.masterIdentityPaths = [./secrets/NIXOSc.key ./secrets/NIXOSa.key]; rekey.pubKey = ./keys + "/${config.networking.hostName}.pub"; - rekey.privKey = "/etc/ssh/ssh_host_ed25519_key"; rekey.plugins = [pkgs.age-plugin-yubikey]; networking.wireless.iwd.enable = true; @@ -87,17 +86,8 @@ #TODO sollte nur bestimmte packages sein nicht alle nixpkgs.config.allowUnfree = true; - # services.xserver.xkbOptions = { - # "eurosign:e"; - # "caps:escape" # map caps to escape. - # }; - # Enable CUPS to print documents. - # services.printing.enable = true; - - # Enable sound. - # sound.enable = true; - # hardware.pulseaudio.enable = true; + powerManagement.powertop.enable = true; # Define a user account. Don't forget to set a password with ‘passwd’. users.users.patrick = { diff --git a/flake.lock b/flake.lock index e4551fa..379abce 100644 --- a/flake.lock +++ b/flake.lock @@ -20,6 +20,21 @@ "type": "github" } }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -60,6 +75,7 @@ "root": { "inputs": { "agenix": "agenix", + "flake-utils": "flake-utils", "home-manager": "home-manager", "nixpkgs": "nixpkgs" } diff --git a/flake.nix b/flake.nix index 89f3777..7f95fca 100644 --- a/flake.nix +++ b/flake.nix @@ -10,6 +10,8 @@ url = "github:ryantm/agenix"; inputs.nixpkgs.follows = "nixpkgs"; }; + + flake-utils.url = "github:numtide/flake-utils"; }; outputs = { @@ -17,28 +19,33 @@ nixpkgs, home-manager, agenix, + flake-utils, ... - }: let + } @ inputs: let system = "x86_64-linux"; - in { - nixosConfigurations.patricknix = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ - ./configuration.nix - home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - } - agenix.nixosModule - { - nix.registry = { - nixpkgs.flake = nixpkgs; - p.flake = nixpkgs; - pkgs.flake = nixpkgs; - }; - } - ]; - }; - }; + in + { + nixosConfigurations.patricknix = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./configuration.nix + home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + } + agenix.nixosModule + { + nix.registry = { + nixpkgs.flake = nixpkgs; + p.flake = nixpkgs; + pkgs.flake = nixpkgs; + }; + } + ]; + }; + } + // flake-utils.lib.eachSystem [system] (localSystem: { + apps = import ./apps/rekey.nix inputs localSystem; + }); } diff --git a/modules/rekey-drv.nix b/modules/rekey-drv.nix new file mode 100644 index 0000000..0ca035c --- /dev/null +++ b/modules/rekey-drv.nix @@ -0,0 +1,20 @@ +pkgs: secretFiles: +( + pkgs.stdenv.mkDerivation rec { + pname = "host-secrets"; + version = "1"; + description = "Rekeyed secrets for this host"; + + srcs = secretFiles; + sourceRoot = "."; + + dontMakeSourcesWriteable = true; + dontUnpack = true; + dontConfigure = true; + dontBuild = true; + + installPhase = '' + cp -r /tmp/nix-rekey.d/ $out + ''; + } +) diff --git a/modules/rekey.nix b/modules/rekey.nix index f433267..17c33ca 100644 --- a/modules/rekey.nix +++ b/modules/rekey.nix @@ -6,88 +6,19 @@ options, ... }: { - # TODO add a with lib um mir die ganzen lib. zu ersparen - config = let - masterIdentities = lib.strings.concatMapStrings (x: "-i ${x} ") config.rekey.masterIdentityPaths; - rekeyedSecrets = pkgs.stdenv.mkDerivation rec { - pname = "age-rekey"; - version = "1.0.0"; - allSecrets = lib.mapAttrsToList (_: x: x.file) config.rekey.secrets; - pubKeyStr = - if builtins.isPath config.rekey.pubKey - then builtins.readFile config.rekey.pubKey - else config.rekey.pubKey; - dontMakeSourceWriteable = 1; - dontUnpack = true; - dontPatch = true; - dontConfigure = true; - dontBuild = true; - installPhase = let - pluginPaths = lib.strings.concatMapStrings (x: ":${x}/bin") config.rekey.plugins; - - rekeyCommand = secret: '' - echo "Rekeying secret ${secret}" >&2 - ${pkgs.rage}/bin/rage ${masterIdentities} -d ${secret} \ - | ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "$out/${builtins.baseNameOf secret}" -e \ - || { echo 1 > "$out"/status; echo "disabled due to failure in rekey.nix" | ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "$out/${builtins.baseNameOf secret}" -e ;} - ''; - in '' - set -euo pipefail - mkdir $out - echo 0 > "$out"/status - - export PATH=$PATH${pluginPaths} - ${lib.concatStringsSep "\n" (map rekeyCommand allSecrets)} - - ''; - }; + config = with lib; let + secretFiles = mapAttrsToList (_: x: x.file) config.rekey.secrets; + drv = import ./rekey-drv.nix pkgs secretFiles; in - lib.mkIf (config.rekey.secrets != {}) { - # Polkit rule to enable the build process to access the keys saved on a yubikey - # This rule allows any user named nixbld to accesst pcscd - security.polkit.extraConfig = lib.mkIf (lib.elem pkgs.age-plugin-yubikey config.rekey.plugins) '' - polkit.addRule(function(action, subject) { - if ((action.id == "org.debian.pcsc-lite.access_pcsc" || action.id == "org.debian.pcsc-lite.access_card") && - subject.user.match(/^nixbld\d+$/)) { - return polkit.Result.YES; - } - }); - ''; - - environment.systemPackages = with pkgs; [ - rage - ]; - + mkIf (config.rekey.secrets != {}) { age = { secrets = let - newPath = x: "${rekeyedSecrets}/${builtins.baseNameOf x}"; + hostName = config.networking.hostName; + secretPath = "${drv}/${hostName}/"; + newPath = x: "${secretPath}/${x}.age"; in - builtins.mapAttrs (_: - builtins.mapAttrs (name: value: - if name == "file" - then "${newPath value}" - else value)) - config.rekey.secrets; + mapAttrs (name: value: value // {file = newPath name;}) config.rekey.secrets; }; - assertions = [ - { - assertion = builtins.pathExists config.rekey.pubKey; - message = "Did not find key file: ${config.rekey.pubKey}. - Make sure your public key is available for rekeying."; - } - { - assertion = config.rekey.masterIdentityPaths != []; - message = "rekey.masterIdentityPaths must be set!"; - } - ]; - warnings = - lib.optional (builtins.any (x: !(lib.strings.hasSuffix ".pub" x || lib.strings.hasSuffix ".age" x)) config.rekey.masterIdentityPaths) '' - It seems at least one of your master masterIdentities files is not encrypted or not a public handle. - Please make sure it does not contain any secret Information. - '' - ++ lib.optional (lib.toInt (builtins.readFile "${rekeyedSecrets}/status") == 1) '' - Could not rekey. Might be due to a chicken/egg problem, then a retry will fix this. - ''; }; options = with lib; { @@ -103,22 +34,12 @@ example = /etc/ssh/ssh_host_ed25519_key.pub; }; - rekey.privKey = mkOption { - type = types.str; - description = '' - The age private key part, corresponding to the public key set in "rekey.pubKey". - Used by agenix for decryption. - Preferably set this to your ed25519 host key. - ''; - example = "/etc/ssh/ssh_host_ed25519_key"; - }; - rekey.masterIdentityPaths = mkOption { type = types.listOf types.path; description = '' A list of Identities used for decrypting your secrets before rekeying. **WARING this will end up in the nix-store** - Only use yubikeys or encrypted age keys + Only use yubikeys or password encrypted age keys ''; };