From 388af355bb32922e736cbb178c822ec12295c398 Mon Sep 17 00:00:00 2001 From: Eclypsed Date: Sun, 21 Dec 2025 00:36:01 -0500 Subject: [PATCH] Added agenix-rekey --- flake.lock | 169 ++++++++++++++++-- flake.nix | 19 +- modules/system/agenix.nix | 32 +++- modules/system/security.nix | 5 + modules/system/services.nix | 10 +- secrets/age-yubikey-identity-d9ed335b.pub | 7 + secrets/eclypse-password.age | Bin 319 -> 473 bytes secrets/eclypsecloud-eclypse.age | Bin 259 -> 328 bytes ...0efd258467853db2ff842cb-tailscale-auth.age | 8 + ...0836ce38bb5da13840b31-eclypse-password.age | 7 + ...827ffdc11d7c11533-eclypsecloud-eclypse.age | Bin 0 -> 404 bytes secrets/secrets.nix | 19 -- secrets/tailscale-auth.age | 12 +- 13 files changed, 241 insertions(+), 47 deletions(-) create mode 100644 secrets/age-yubikey-identity-d9ed335b.pub create mode 100644 secrets/rekeyed/vanta/40b96312f0efd258467853db2ff842cb-tailscale-auth.age create mode 100644 secrets/rekeyed/vanta/853ffda11ae0836ce38bb5da13840b31-eclypse-password.age create mode 100644 secrets/rekeyed/vanta/881aa01f75dfd34827ffdc11d7c11533-eclypsecloud-eclypse.age delete mode 100644 secrets/secrets.nix diff --git a/flake.lock b/flake.lock index 86403db..0c3cd54 100644 --- a/flake.lock +++ b/flake.lock @@ -23,6 +23,30 @@ "type": "github" } }, + "agenix-rekey": { + "inputs": { + "devshell": "devshell", + "flake-parts": "flake-parts", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1759699908, + "narHash": "sha256-kYVGY8sAfqwpNch706Fy2+/b+xbtfidhXSnzvthAhIQ=", + "owner": "oddlama", + "repo": "agenix-rekey", + "rev": "42362b12f59978aabf3ec3334834ce2f3662013d", + "type": "github" + }, + "original": { + "owner": "oddlama", + "repo": "agenix-rekey", + "type": "github" + } + }, "aquamarine": { "inputs": { "hyprutils": [ @@ -146,6 +170,27 @@ "type": "github" } }, + "devshell": { + "inputs": { + "nixpkgs": [ + "agenix-rekey", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1728330715, + "narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=", + "owner": "numtide", + "repo": "devshell", + "rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, "elephant": { "inputs": { "nixpkgs": [ @@ -184,6 +229,22 @@ } }, "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { "flake": false, "locked": { "lastModified": 1747046372, @@ -199,7 +260,7 @@ "type": "github" } }, - "flake-compat_2": { + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1751685974, @@ -216,6 +277,27 @@ } }, "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "agenix-rekey", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, @@ -233,7 +315,7 @@ "type": "github" } }, - "flake-parts_2": { + "flake-parts_3": { "inputs": { "nixpkgs-lib": [ "nur", @@ -254,7 +336,7 @@ "type": "github" } }, - "flake-parts_3": { + "flake-parts_4": { "inputs": { "nixpkgs-lib": [ "nvf", @@ -275,7 +357,7 @@ "type": "github" } }, - "flake-parts_4": { + "flake-parts_5": { "inputs": { "nixpkgs-lib": [ "stylix", @@ -331,6 +413,28 @@ } }, "gitignore": { + "inputs": { + "nixpkgs": [ + "agenix-rekey", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { "inputs": { "nixpkgs": [ "hyprland", @@ -500,7 +604,7 @@ "hyprutils": "hyprutils", "hyprwayland-scanner": "hyprwayland-scanner_2", "nixpkgs": "nixpkgs_2", - "pre-commit-hooks": "pre-commit-hooks", + "pre-commit-hooks": "pre-commit-hooks_2", "systems": "systems_4", "xdph": "xdph" }, @@ -743,7 +847,7 @@ }, "mango": { "inputs": { - "flake-parts": "flake-parts", + "flake-parts": "flake-parts_2", "nixpkgs": "nixpkgs_3", "scenefx": "scenefx" }, @@ -889,7 +993,7 @@ }, "nur": { "inputs": { - "flake-parts": "flake-parts_2", + "flake-parts": "flake-parts_3", "nixpkgs": [ "nixpkgs" ] @@ -935,8 +1039,8 @@ }, "nvf": { "inputs": { - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts_3", + "flake-compat": "flake-compat_3", + "flake-parts": "flake-parts_4", "mnw": "mnw", "nixpkgs": [ "nixpkgs" @@ -984,6 +1088,29 @@ "inputs": { "flake-compat": "flake-compat", "gitignore": "gitignore", + "nixpkgs": [ + "agenix-rekey", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1735882644, + "narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "a5a961387e75ae44cc20f0a57ae463da5e959656", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": "flake-compat_2", + "gitignore": "gitignore_2", "nixpkgs": [ "hyprland", "nixpkgs" @@ -1006,6 +1133,7 @@ "root": { "inputs": { "agenix": "agenix", + "agenix-rekey": "agenix-rekey", "elephant": "elephant", "home-manager": "home-manager_2", "hyprdynamicmonitors": "hyprdynamicmonitors", @@ -1069,7 +1197,7 @@ "base16-helix": "base16-helix", "base16-vim": "base16-vim", "firefox-gnome-theme": "firefox-gnome-theme", - "flake-parts": "flake-parts_4", + "flake-parts": "flake-parts_5", "gnome-shell": "gnome-shell", "nixpkgs": [ "nixpkgs" @@ -1282,6 +1410,27 @@ "type": "github" } }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "agenix-rekey", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1735135567, + "narHash": "sha256-8T3K5amndEavxnludPyfj3Z1IkcFdRpR23q+T0BVeZE=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "9e09d30a644c57257715902efbb3adc56c79cf28", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, "walker": { "inputs": { "elephant": [ diff --git a/flake.nix b/flake.nix index d62f9b6..6ae9fa7 100644 --- a/flake.nix +++ b/flake.nix @@ -21,6 +21,11 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + agenix-rekey = { + url = "github:oddlama/agenix-rekey"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nvf = { url = "github:notashelf/nvf"; inputs.nixpkgs.follows = "nixpkgs"; @@ -60,7 +65,12 @@ }; outputs = - { nixpkgs, ... }@inputs: + { + self, + nixpkgs, + agenix-rekey, + ... + }@inputs: { nixosConfigurations.vanta = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; @@ -68,11 +78,18 @@ inherit inputs; host = "vanta"; wallpaper = "twilight-village.png"; + # Host public SSH key (e.g. /etc/ssh/ssh_host_ed25519_key.pub). + hostPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAaDVBJdMDFL8r9NQCbaLe+DPHGhGzRv2N7+7m1/U8DP"; }; modules = [ ./modules/system ./hosts/vanta ]; }; + + agenix-rekey = agenix-rekey.configure { + userFlake = self; + nixosConfigurations = self.nixosConfigurations; + }; }; } diff --git a/modules/system/agenix.nix b/modules/system/agenix.nix index 679e331..f2fe2ac 100644 --- a/modules/system/agenix.nix +++ b/modules/system/agenix.nix @@ -1,20 +1,42 @@ { inputs, + config, pkgs, + lib, + host, + hostPubkey ? null, ... }: { imports = [ inputs.agenix.nixosModules.default + inputs.agenix-rekey.nixosModules.default ]; environment.systemPackages = [ - inputs.agenix.packages.${pkgs.stdenv.hostPlatform.system}.default # CLI Tool + # agenix-rekey's CLI tool replaces standard agenix's + inputs.agenix-rekey.packages.${pkgs.stdenv.hostPlatform.system}.default ]; - age.secrets = { - tailscale-auth.file = ../../secrets/tailscale-auth.age; - eclypsecloud-eclypse.file = ../../secrets/eclypsecloud-eclypse.age; - eclypse-password.file = ../../secrets/eclypse-password.age; + age = { + # Need to explicitly set identity paths because OpenSSH daemon is disabled + # but the host keys are still generated via services.openssh.generateHostKeys = true + identityPaths = map (key: key.path) config.services.openssh.hostKeys; + rekey = { + masterIdentities = [ "${inputs.self}/secrets/age-yubikey-identity-d9ed335b.pub" ]; + storageMode = "local"; + localStorageDir = ../../. + "/secrets/rekeyed/${host}"; + } + # We only set the hostPubkey if one is supplied. For new hosts the pub key will not + # exist until it is generated after the first rebuild. Runtime decryption will fail + # but then the ssh host key will be generated in /etc/ssh and can be supplied + // lib.optionalAttrs (hostPubkey != null) { + inherit hostPubkey; + }; + secrets = { + tailscale-auth.rekeyFile = ../../secrets/tailscale-auth.age; + eclypsecloud-eclypse.rekeyFile = ../../secrets/eclypsecloud-eclypse.age; + eclypse-password.rekeyFile = ../../secrets/eclypse-password.age; + }; }; } diff --git a/modules/system/security.nix b/modules/system/security.nix index 77c9b56..318a79a 100644 --- a/modules/system/security.nix +++ b/modules/system/security.nix @@ -1,4 +1,5 @@ { + pkgs, ... }: { @@ -7,6 +8,10 @@ yubikey-touch-detector.enable = true; }; + environment.systemPackages = with pkgs; [ + age-plugin-yubikey + ]; + services = { yubikey-agent.enable = true; }; diff --git a/modules/system/services.nix b/modules/system/services.nix index 006c8ee..7d0293a 100644 --- a/modules/system/services.nix +++ b/modules/system/services.nix @@ -52,14 +52,10 @@ upower.enable = true; - # Enable the OpenSSH daemon. (Look into Fail2Ban in the future) + # Disable SSH daemon but generate host keys anyway for secret rekeying openssh = { - enable = true; - settings = { - PasswordAuthentication = false; - PermitRootLogin = "prohibit-password"; - AllowUsers = [ "eclypse" ]; - }; + enable = false; + generateHostKeys = true; }; system76-scheduler.settings.cfsProfiles.enable = true; diff --git a/secrets/age-yubikey-identity-d9ed335b.pub b/secrets/age-yubikey-identity-d9ed335b.pub new file mode 100644 index 0000000..f119a45 --- /dev/null +++ b/secrets/age-yubikey-identity-d9ed335b.pub @@ -0,0 +1,7 @@ +# Serial: 27501992, Slot: 1 +# Name: agenix-rekey-alpha +# Created: Sat, 20 Dec 2025 06:01:41 +0000 +# PIN policy: Once (A PIN is required once per session, if set) +# Touch policy: Always (A physical touch is required for every decryption) +# Recipient: age1yubikey1qvq48l020xg9xtt5epdpnzp3kvkm2vvc57357p58pyfq557a8q8hv84c82e +AGE-PLUGIN-YUBIKEY-14ZJ6XQVZM8KNXKCT2PKLW diff --git a/secrets/eclypse-password.age b/secrets/eclypse-password.age index 54a2a6c5e8e9b845346ea475d4914b04d672bf3e..6f8b3b86af64501ee682b3a843987bfcc0452856 100644 GIT binary patch delta 458 zcmV;*0X6==0@(wQ8GmqTb}eu+H8vnJWiWbIcOXGIaad9}dP_)Xc6WC~MRsdYHFi=k zHA_=cJWp8RRSZO(I zO?Wn8XiEx0Pf25XRyavxbyPG|Zdh7lXE-rZQfqE!K|yXrYD!l_G*&QodQoF+NojR! zS#m2?Rbgr}Pk%H)GI}#X3N0-yAa7VqQfg^IS5!fIMk_*LZcS}+Mpa>JZc8yQS8a81 zW@0OKX){w;aWQgh3W$VaWAzt>w+kRy;`WNkH}VsAUa#LstNM3p8w=Ys*zu17T<(n4 z)yF1c1V>05uKvXPzCc5F?x(O77;5kT@w@IRp!Z&607IdI+|!}gJrT7LwJu~fV|C~3 zu*)Cqu~x`fe<(!R6>dSrR|EaC2D~ilI)$o@fft>y$8L7wWErR?df4+%-EKNPVuR(d AvH$=8 delta 303 zcmV+~0nq;01HS^08GmzgXf0)AGBq_ZIUqN1G*VD^Aa`11X=PCNRy)< zlA}b%UMfPCZc82~4R*4xeym`(3V+v=O}&BdGcawF&Ja@tDs?+5fb0Pss-E8UR)@B* zf&~xD&@wD=VJIYf%tx3qP!<$(Na_zcVF41~pIGa2BAdlMPH4Uds0^9T{K)-tSnXh( Bax(w` diff --git a/secrets/eclypsecloud-eclypse.age b/secrets/eclypsecloud-eclypse.age index 1a70592b6f8c0821c0b0b51dd3b22418130daa53..8ffda0dfaa3eb73c4de45a851fe7a185929b3720 100644 GIT binary patch delta 312 zcmV-80muG>0>}c88GmqTb}eu+H8vnJWiWbIcOXGIF=lLbOlL_@V@POsWN1z=c5E^* zNjeXVrfK3Z&GqlXmwC#a7i&oHZpT@Og2PAaZhP7T2TruEiE8wFfdkgcuqNFcv(ej zbY^d2T4ZrII8JdwS4vh;aCvZMYYh za(ZTFae7KhMs`;)RAOsrQ*BF2a%fXH3VKvyG)Q4aPcvv!SyoI-VGB-p@ zbY*ooSwmu0MlwupGzy2@to*;*^nKA#$?R;u;3wdSEKy9?k~L5h;8RU=pbW4);)1-E svwESWmAKk@Q4F7Nwhkb4TVWkA4~Y0xtrF0BKT?e%Cf}$-iR+bYv)z ssh-ed25519 7p4RPw HgBYYM/VqZ4KN4V4TrGmk86wPRhDgM+VaXfa3VlODRM +OdM//HvJTzB7/jw+c+6euiYz9ptUf/z22tzJSgxTD+w +-> B%P@9-grease +Zgr76aiZDhCWBdnbxoOptAfEuM1RWw1bN4rsUCec4VP0cDN856bCtaQjnWWbSTvv +YPHtmw +--- obv+bg63dTlnoke3tQdkAizcAqsYG2sUjYBZrhGZG68 +(2$Y@i7jar7X$U~|'oo`DƆ{#%< ssh-ed25519 7p4RPw FdmJ1odfweTU4HWPTeWuEcoIUq1V4ke28BWmlNNdNHg +9qi5QQHociRgSzZ97HifRDf+/Hh0cCZJzFsobpP1cpU +-> 4pq5-grease +yKZUs4lQM6BQgsyzMn3T1pvUt393/NvcRe7KwuTCDCU +--- N7NO5Ps2SG3SFNNnNNvYUSGgA0b5Dk7H6+x0rt6JtXA +Dl]e p(F0i3gijQX5t-Wq@SFgQDK~ynx7GwSy_?pUXf(I737-dU+kGwQkd!%TA*K$A7qe}RhAbNnQv-roRs0= z7Gx4o=H?n2T%?^>W|83-$fc{Rt6&mXnC|OW98?yTne3cjXz6QMl^X1tuI(F|kr^3T zRN?KGXO?0a!U7bA78{nelAz w73>F&?|ICB{9IYn{hYYZmal8rocF{vxNc1L54$YeBsfD-UdGhn)}N1i0f_{R3IG5A literal 0 HcmV?d00001 diff --git a/secrets/secrets.nix b/secrets/secrets.nix deleted file mode 100644 index ec9a85b..0000000 --- a/secrets/secrets.nix +++ /dev/null @@ -1,19 +0,0 @@ -# This file is NOT imported into the nix configuration, it is just for the agenix CLI -let - # System public ssh keys (/etc/ssh/) - vanta = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAaDVBJdMDFL8r9NQCbaLe+DPHGhGzRv2N7+7m1/U8DP"; -in -{ - # Tailscale auth key need to be up to date with a valid auth key in the tailscale - # dashboard. Single-use keys expire after a single machine connects, and even - # reusable keys expire after 90 days. - # Update tailscale-auth.age with `agenix -e tailscale-auth.age -i /path/to/private-ssh-key` - # Note: Only devices with the below public keys are allowed to edit tailscale-auth.age - "tailscale-auth.age".publicKeys = [ vanta ]; # Devices allowed to join the tailnet; - - # Devices that can connect to EclypseCloud with the eclypse user. - "eclypsecloud-eclypse.age".publicKeys = [ vanta ]; - - # Devices that have the eclypse user - "eclypse-password.age".publicKeys = [ vanta ]; -} diff --git a/secrets/tailscale-auth.age b/secrets/tailscale-auth.age index cd6d951..e56837f 100644 --- a/secrets/tailscale-auth.age +++ b/secrets/tailscale-auth.age @@ -1,6 +1,8 @@ age-encryption.org/v1 --> ssh-ed25519 7p4RPw 7GuZj43+NoyPXf//ZLM99vossbJXOpDQSkBi3w51Wl8 -FTMjlyml+T87LQffffY2AJL5IhTAJF2QlfFvhvZpvOs ---- iONf8B3bUxXtCiv0EAv5QO0ZyhE5A6YfRbcxUr/awFg -TwJ`~B -;lOh{2?PF>@moc~X3@.gھeKV7zphSد6.WO@F  \ No newline at end of file +-> piv-p256 2e0zWw ApoXPsP2VGfJnOt+dDk7DfssOkbM/3vkn4jwSfxD4UAj +jtn4DCA/EyrTl9DW1hs84yd3RgVuDU77ggM218HiUdc +-> *E(-grease Ull1npy_ >F7 *? +IM+85AtRNlMrFgqk/uAG +--- nxCTKF6R3E/qaTTgr7jZdz4ZLRE15NsJpyKHizEJnPw +>"lrsNV*FI|0X8 + |PF D\xZP]ʧt-"nm&| %.ӆ \ No newline at end of file