From 266945d2423c11662acb91af9b86f870eb99215e Mon Sep 17 00:00:00 2001 From: oddlama Date: Mon, 30 Jan 2023 22:03:39 +0100 Subject: [PATCH] feat: extract rekeying as separate repo --- apps/rekey-output-derivation.nix | 24 --------- apps/rekey.nix | 60 --------------------- flake.lock | 39 ++++++++++++-- flake.nix | 13 ++++- modules/core/default.nix | 1 - modules/core/rekey.nix | 90 -------------------------------- nix/nixos.nix | 8 +-- 7 files changed, 50 insertions(+), 185 deletions(-) delete mode 100644 apps/rekey-output-derivation.nix delete mode 100644 apps/rekey.nix delete mode 100644 modules/core/rekey.nix diff --git a/apps/rekey-output-derivation.nix b/apps/rekey-output-derivation.nix deleted file mode 100644 index 258a9ca..0000000 --- a/apps/rekey-output-derivation.nix +++ /dev/null @@ -1,24 +0,0 @@ -pkgs: config: -with pkgs.lib; - pkgs.stdenv.mkDerivation rec { - pname = "host-secrets"; - version = "1.0"; - description = "Rekeyed secrets for this host."; - - srcs = mapAttrsToList (_: x: x.file) config.rekey.secrets; - sourcePath = "."; - # Required as input to have the derivation rebuild if this changes - hostPubkey = let - pubkey = config.rekey.hostPubkey; - in - if isPath pubkey - then readFile pubkey - else pubkey; - - dontMakeSourcesWritable = true; - dontUnpack = true; - dontConfigure = true; - dontBuild = true; - - installPhase = ''cp -r "/tmp/nix-rekey/${builtins.hashString "sha1" hostPubkey}/." "$out"''; - } diff --git a/apps/rekey.nix b/apps/rekey.nix deleted file mode 100644 index bc69e39..0000000 --- a/apps/rekey.nix +++ /dev/null @@ -1,60 +0,0 @@ -{ - self, - nixpkgs, - ... -}: system: -with nixpkgs.lib; let - pkgs = self.pkgs.${system}; - toPubkey = pubkey: - if isPath pubkey - then readFile pubkey - else pubkey; - - rekeyCommandsForHost = hostName: hostAttrs: let - hostPubkeyStr = toPubkey hostAttrs.config.rekey.hostPubkey; - secretDir = "/tmp/nix-rekey/${builtins.hashString "sha1" hostPubkeyStr}"; - rekeyCommand = secretName: secretAttrs: let - masterIdentityArgs = concatMapStrings (x: ''-i "${x}" '') hostAttrs.config.rekey.masterIdentityPaths; - secretOut = "${secretDir}/${secretName}.age"; - in '' - echo "Rekeying ${secretName} for host ${hostName}" - ${pkgs.rage}/bin/rage ${masterIdentityArgs} -d ${secretAttrs.file} \ - | ${pkgs.rage}/bin/rage -r "${hostPubkeyStr}" -o "${secretOut}" -e \ - || { \ - echo "Failed to rekey secret ${secretName} for ${hostName}!" ; \ - echo "This is a dummy replacement value. The actual secret could not be rekeyed." \ - | ${pkgs.rage}/bin/rage -r "${hostPubkeyStr}" -o "${secretOut}" -e ; \ - } - ''; - in '' - mkdir -p "${secretDir}" - # Enable selected age plugins for this host - export PATH="$PATH${concatMapStrings (x: ":${x}/bin") hostAttrs.config.rekey.agePlugins}" - ${concatStringsSep "\n" (mapAttrsToList rekeyCommand hostAttrs.config.rekey.secrets)} - ''; - - rekeyScript = pkgs.writeShellScript "rekey" '' - set -euo pipefail - ${concatStringsSep "\n" (mapAttrsToList rekeyCommandsForHost self.nixosConfigurations)} - nix run --extra-sandbox-paths /tmp "${../.}#rekey-save-outputs"; - ''; - - rekeySaveOutputsScript = let - copyHostSecrets = hostName: hostAttrs: let - drv = import ./rekey-output-derivation.nix pkgs hostAttrs.config; - in ''echo "Stored rekeyed secrets for ${hostAttrs.config.networking.hostName} in ${drv}"''; - in - pkgs.writeShellScript "rekey-save-outputs" '' - set -euo pipefail - ${concatStringsSep "\n" (mapAttrsToList copyHostSecrets self.nixosConfigurations)} - ''; -in { - rekey = { - type = "app"; - program = "${rekeyScript}"; - }; - rekey-save-outputs = { - type = "app"; - program = "${rekeySaveOutputsScript}"; - }; -} diff --git a/flake.lock b/flake.lock index 7c75eda..239caa5 100644 --- a/flake.lock +++ b/flake.lock @@ -3,16 +3,15 @@ "agenix": { "inputs": { "nixpkgs": [ - "ragenix", "nixpkgs" ] }, "locked": { - "lastModified": 1665870395, - "narHash": "sha256-Tsbqb27LDNxOoPLh0gw2hIb6L/6Ow/6lIBvqcHzEKBI=", + "lastModified": 1675030834, + "narHash": "sha256-e1/7Z7rVRqy2NuEOxrRm560wc/Kn8NU7gz8CDfmu9F0=", "owner": "ryantm", "repo": "agenix", - "rev": "a630400067c6d03c9b3e0455347dc8559db14288", + "rev": "49798e535ebc07fec82256b283d35be36d8c6c9a", "type": "github" }, "original": { @@ -21,6 +20,32 @@ "type": "github" } }, + "agenix-rekey": { + "inputs": { + "agenix": [ + "agenix" + ], + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675112851, + "narHash": "sha256-PAyCKssbhSTZfIMgfRdHuECjR5Bzw74oK+zLSVHcVwU=", + "owner": "oddlama", + "repo": "agenix-rekey", + "rev": "cae8d9ffeaed228382f60a208f1447385720c137", + "type": "github" + }, + "original": { + "owner": "oddlama", + "repo": "agenix-rekey", + "type": "github" + } + }, "deploy-rs": { "inputs": { "flake-compat": [ @@ -214,7 +239,9 @@ }, "ragenix": { "inputs": { - "agenix": "agenix", + "agenix": [ + "agenix" + ], "flake-utils": [ "flake-utils" ], @@ -239,6 +266,8 @@ }, "root": { "inputs": { + "agenix": "agenix", + "agenix-rekey": "agenix-rekey", "deploy-rs": "deploy-rs", "flake-compat": "flake-compat", "flake-utils": "flake-utils", diff --git a/flake.nix b/flake.nix index 3c5ecaa..38248d7 100644 --- a/flake.nix +++ b/flake.nix @@ -33,8 +33,19 @@ inputs.flake-compat.follows = "flake-compat"; }; + agenix = { + url = "github:ryantm/agenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; ragenix = { url = "github:yaxitech/ragenix"; + inputs.agenix.follows = "agenix"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + agenix-rekey = { + url = "github:oddlama/agenix-rekey"; + inputs.agenix.follows = "agenix"; inputs.flake-utils.follows = "flake-utils"; inputs.nixpkgs.follows = "nixpkgs"; }; @@ -76,7 +87,5 @@ ]; config.allowUnfree = true; }; - - apps = {} // import ./apps/rekey.nix inputs localSystem; }); } diff --git a/modules/core/default.nix b/modules/core/default.nix index 02ab213..808780e 100644 --- a/modules/core/default.nix +++ b/modules/core/default.nix @@ -10,7 +10,6 @@ ''; in { imports = [ - ./rekey.nix ./inputrc.nix ./issue.nix ./nix.nix diff --git a/modules/core/rekey.nix b/modules/core/rekey.nix deleted file mode 100644 index e343be8..0000000 --- a/modules/core/rekey.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ - lib, - options, - config, - pkgs, - ... -}: -with lib; { - config = let - drv = import ../../apps/rekey-output-derivation.nix pkgs config; - in - mkIf (config.rekey.secrets != {}) { - # Produce a rekeyed age secret for each of the secrets defined in our secrets - age.secrets = mapAttrs (secretName: - flip mergeAttrs { - file = "${drv}/${secretName}"; - }) - config.rekey.secrets; - - assertions = [ - { - assertion = pathExists config.rekey.hostPubkey; - message = '' - The path config.rekey.hostPubkey (${toString config.rekey.hostPubkey}) doesn't exist, but is required to rekey secrets for this host. - If this is the first deploy, use a mock key until you know the real one. - ''; - } - { - assertion = config.rekey.masterIdentityPaths != []; - message = "rekey.masterIdentityPaths must be set."; - } - ]; - - warnings = let - hasGoodSuffix = x: strings.hasSuffix ".age" x || strings.hasSuffix ".pub" x; - in - # drv.drvPath doesn't force evaluation, which allows the warning to be displayed - # in case the derivation is not built before deploying - optional (!pathExists (removeSuffix ".drv" drv.drvPath)) '' - The secrets for host ${config.networking.hostName} have not yet been rekeyed! - Be sure to run `nix run ".#rekey"` after changing your secrets! - '' - ++ optional (!all hasGoodSuffix config.rekey.masterIdentityPaths) '' - It seems like at least one of your rekey.masterIdentityPaths contains an - unencrypted age identity. These files will be copied to the nix store, so - make sure they don't contain any secret information! - - To silence this warning, encrypt your keys and name them *.pub or *.age. - ''; - }; - - options = { - rekey.secrets = options.age.secrets; - rekey.hostPubkey = mkOption { - type = types.either types.path types.str; - description = '' - The age public key to use as a recipient when rekeying. - This either has to be the path to an age public key file, - or the public key itself in string form. - - Make sure to NEVER use a private key here, as it will end - up in the public nix store! - ''; - #example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEyH9Vx7WJZWW+6tnDsF7JuflcxgjhAQHoCWVrjLXQ2U my-host"; - #example = "age159tavn5rcfnq30zge2jfq4yx60uksz8udndp0g3njzhrns67ca5qq3n0tj"; - example = /etc/ssh/ssh_host_ed25519_key.pub; - }; - rekey.masterIdentityPaths = mkOption { - type = types.listOf types.path; - description = '' - The age identity used to decrypt the secrets stored in the repository, so they can be rekeyed for a specific host. - This identity will be stored in the nix store, so be sure to use a split-identity (like a yubikey identity, which is public), - or an encrypted age identity. You can encrypt an age identity using `rage -p -o privkey.age privkey` to protect it in your store. - - All identities given here will be passed to age, which will select one of them for decryption. - ''; - default = []; - example = [./secrets/my-yubikey-identity.txt]; - }; - rekey.agePlugins = mkOption { - type = types.listOf types.package; - default = []; - description = '' - A list of plugins that should be available to rage while rekeying. - They will be added to the PATH before rage is invoked. - ''; - example = [pkgs.age-plugin-yubikey]; - }; - }; -} diff --git a/nix/nixos.nix b/nix/nixos.nix index 023bc09..7d2e01d 100644 --- a/nix/nixos.nix +++ b/nix/nixos.nix @@ -5,6 +5,7 @@ nixos-hardware, nixpkgs, ragenix, + agenix-rekey, templates, ... }: let @@ -32,9 +33,10 @@ nixpkgs.hostPlatform = hostPlatform; } nixRegistry - home-manager.nixosModules.home-manager - #impermanence.nixosModules.impermanence - ragenix.nixosModules.age + home-manager.nixosModules.default + #impermanence.nixosModules.default + ragenix.nixosModules.default + agenix-rekey.nixosModules.default ]; specialArgs = { #impermanence = impermanence.nixosModules;