Commit 42810db

HPCesia <me@hpcesia.com>
2026-06-10 04:53:42
Add Secret management by Vaultix
1 parent 67499e2
.helix/ignore
@@ -0,0 +1,1 @@
+/secrets/*
modules/core/nix/github-access-token.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> piv-p256 xCEwtQ A1uhE2zs8HRDwPmI6gfYiP80g5R0S3Iv3eXfpz4fhiRx
+fLB+VCjrZYZJNuo+t7VASqz8Vu7Nieg/oBDQ43c2+xU
+-> `LLrn-grease
+YtsqFt2Szrl5YK+6m2Lobmqamnn4QPtlQXYNK1l+78LQ3hTpZ3k
+--- 20FRUaR7JsqwU0z4OTDVg+7fa+2PRPYLmd8aXk3NcDk
+����K[ڶ�q�O3M��g{REu���������Cs�+w27?Hd��Qn|�T¼4N��p{f�R�0E_��TT��
+4��.��'��˽�q�������̄a���1;�ր�6�yw$L�w`��
\ No newline at end of file
modules/core/nix/tokens.nix
@@ -0,0 +1,18 @@
+{
+  den.aspects.core.nix.tokens = {
+    nixos = {config, ...}: {
+      nix.extraOptions = ''
+        !include ${config.vaultix.templates.nix-access-tokens.path}
+      '';
+
+      vaultix.secrets.github-access-token.file = ./github-access-token.age;
+      vaultix.templates.nix-access-tokens = {
+        content = ''
+          access-tokens = github.com=${config.vaultix.placeholder.github-access-token}
+        '';
+        mode = "0440";
+        group = "wheel";
+      };
+    };
+  };
+}
modules/hosts/hyacine/default.nix
@@ -8,6 +8,8 @@
     '';
 
     pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINy57YGTUNEbBVs1YHD6m/zhJnJKnicNEaLB5fEcJln6 root@hyacine";
+
+    rootHashedPasswordFileAged = ./hashed-password.age;
   };
 
   den.users.access.by-host.hyacine.groups = [
modules/hosts/hyacine/hashed-password.age
Binary file
modules/hosts/kevin/default.nix
@@ -7,6 +7,8 @@
       with Intel Core i7-14650HX, 32GB RAM and 2TB NVMe storage.
     '';
     pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOO9CyAqGo/WbJkncrt1a9jxS1E+hd550SC4A08I/l0/ root@kevin";
+
+    rootHashedPasswordFileAged = ./hashed-password.age;
   };
 
   den.users.access.by-host.kevin = {
modules/hosts/kevin/hashed-password.age
Binary file
modules/hosts/mobius/default.nix
@@ -8,6 +8,7 @@
     '';
 
     pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKSHlIARpQYDi744NML9QmFMxOEZwShfyj/ovQh6lCt7 root@mobius";
+    rootHashedPasswordFileAged = ./hashed-password.age;
   };
 
   den.users.access.by-host.mobius.groups = [
modules/hosts/mobius/hashed-password.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> piv-p256 xCEwtQ AkDA3lJDmcRiTrqgNPs74WwtwBdjnfST3kiZRqGFn4af
+utgxnKL2N/TeuM7Im9Dyd9f9zST+S9MsX+IaAEYEe8M
+-> #Ak+-grease wj` p_%xgDg MN;&
+OGQ2zAKwCikdzXMSvV6SqIDZiwoYZJf4xC+voQCJ2Dg
+--- n4+BxXEK3n9KPT5OexIkqGjT5o+SqecctiJIOe1Fnpc
+�V��x
���^2Ay���Y��QA�9s��p��Frc�'���=;#��]�X�������ɨ]�`C��/��Y��������������Gc?����g	��
���~���
\ No newline at end of file
modules/hosts/tribios/default.nix
@@ -7,6 +7,7 @@
       with Rockchip RK3588S, 4GB RAM and 64GB eMMC storage.
     '';
     pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQTx9RhZJa4WOAJyAUzlQLaFExcDv921sVsfM02vB9h root@tribios";
+    rootHashedPasswordFileAged = ./hashed-password.age;
   };
 
   den.users.access.by-host.tribios.groups = [
modules/hosts/tribios/hashed-password.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> piv-p256 xCEwtQ A/VgRdjiaPIxIjdTCYq7iNm/VSkySXnHvKvbpHYJTxQp
+G4n0yFMno/FUFfRQIUmLjTaBuB1jwVz2pK2EIY/UpY0
+-> +HGW~!I$-grease SN
+yvoWuedn6CdyaGhdkjm/lrcFLGZcV4gH2xKYT1uiKD+NfeXNXPKoFKXZpdPNPBYD
+ra9VDSlVgMuBss/h5Fnke2SpIEE8jcVpjT3Tj7eczAzF
+--- 4ZrMVAiVrZDI880IPPAbswMf91JF2tFIxQWCPG4Q6Wc
+�4�cWe�D�"��q•�#��Xɘ��p�>H�Ȁ�eu5��U�H���ǘ,������{�Az�bso�Q�F�\����ֲn���\X΀p|4�ġ�#ܭL��>$�G?
\ No newline at end of file
modules/hosts/default.nix
@@ -1,5 +1,22 @@
 {den, ...}: {
   den.schema.host.includes = [
     den.batteries.hostname
+
+    ({host, ...}: {
+      name = "${host.name}/root/password";
+
+      nixos = {
+        config,
+        lib,
+        ...
+      }:
+        lib.optionalAttrs (host.rootHashedPasswordFileAged != null) (let
+          secretName = "hashed-password-user-root";
+        in {
+          users.users.root.hashedPasswordFile = config.vaultix.secrets.${secretName}.path;
+          vaultix.secrets.${secretName}.file = host.rootHashedPasswordFileAged;
+          vaultix.beforeUserborn = [secretName];
+        });
+    })
   ];
 }
modules/hosts/schema.nix
@@ -96,6 +96,12 @@
         type = lib.types.nullOr lib.types.str;
         default = null;
       };
+
+      rootHashedPasswordFileAged = lib.mkOption {
+        description = "Age encrypted hashed password file path for host root user.";
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+      };
     };
   };
 }
modules/roles/default.nix
@@ -6,6 +6,7 @@
       core.nix.nh
       core.nix.nixpkgs
       core.nix.state-versions
+      core.nix.tokens
 
       core.hardware.cpu.intel
       core.hardware.graphic.intel
@@ -21,6 +22,8 @@
       core.systemd
       core.time-sync
       core.utils
+
+      secrets.vaultix
     ];
   };
 }
modules/secrets/age-yubikey-identity.pub
@@ -0,0 +1,7 @@
+#       Serial: 34752807, Slot: 1
+#         Name: age identity for Nix configuration
+#      Created: Mon, 25 May 2026 10:01:34 +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: age1yubikey1qf38em555plx705xy9x5nvdv5tuksendzwngm3n45fdkz4jcw33hjapxgmu
+AGE-PLUGIN-YUBIKEY-1YAY3YQ5ZCSSNPDGQUXFL7
modules/secrets/vaultix.nix
@@ -0,0 +1,77 @@
+{
+  den,
+  inputs,
+  config,
+  self,
+  withSystem,
+  lib,
+  ...
+}: {
+  # Use my fork version for some flake module improve
+  flake-file.inputs.vaultix = {
+    url = "github:HPCesia/vaultix";
+    inputs.nixpkgs.follows = "nixpkgs";
+    inputs.flake-parts.follows = "flake-parts";
+    inputs.pre-commit-hooks.inputs.flake-compat.follows = "flake-compat";
+  };
+
+  imports = [(inputs.vaultix.flakeModules.default or {})];
+
+  flake.vaultix = {
+    identity = ./age-yubikey-identity.pub;
+    extraPackages = pkgs: [pkgs.age-plugin-yubikey];
+    nodes = lib.filterAttrs (_: nixos: nixos.config ? vaultix) self.nixosConfigurations;
+  };
+  den.schema.flake-parts.includes = [den.aspects.secrets.vaultix];
+
+  den.aspects.secrets._.vaultix.includes = [
+    den.aspects.secrets.vaultix.flake
+    den.aspects.secrets.vaultix.os
+  ];
+
+  den.aspects.secrets._.vaultix._.flake = {
+    devshell = {system, ...}: {
+      commands = [
+        {
+          package = config.flake.vaultix.app.${system}.edit;
+          help = "view and edit secrets managed by vaultix";
+          category = "[secret management]";
+        }
+        {
+          help = "cache secrets managed by vaultix for all hosts";
+          name = "cache-secret";
+          command = "nix run .#vaultix.app.${system}.renc";
+          category = "[secret management]";
+        }
+      ];
+    };
+  };
+
+  den.aspects.secrets._.vaultix._.os = {host, ...}: let
+    hasImpermanence = host.hasAspect den.aspects.core.impermanence;
+    persistPrefix = lib.optionalString hasImpermanence "/persist";
+  in {
+    nixos = {
+      pkgs,
+      config,
+      ...
+    } @ args: {
+      imports = [
+        (import (inputs.vaultix.outPath + "/module") (args // {inherit self;}))
+      ];
+
+      vaultix = {
+        package = withSystem host.system (
+          {inputs', ...}: inputs'.vaultix.packages.default
+        );
+        settings = {
+          hostPubkey = host.pubKey;
+          hostKeys = map (key: key // {path = persistPrefix + key.path;}) config.services.openssh.hostKeys;
+        };
+      };
+
+      services.userborn.enable = true;
+      users.mutableUsers = false;
+    };
+  };
+}
modules/users/hpcesia/default.nix
@@ -20,5 +20,8 @@
         }
       ];
     };
+    system = {
+      hashedPasswordAged = ./hashed-password.age;
+    };
   };
 }
modules/users/hpcesia/hashed-password.age
Binary file
modules/users/default.nix
@@ -5,5 +5,15 @@
       name = "users/description";
       user.description = user.identity.displayName;
     })
+    ({user, ...}: {
+      name = "users/password";
+      nixos = {config, ...}: let
+        secretName = "hashed-password-user-${user.userName}";
+      in {
+        users.users.${user.userName}.hashedPasswordFile = config.vaultix.secrets.${secretName}.path;
+        vaultix.secrets.${secretName}.file = user.system.hashedPasswordAged;
+        vaultix.beforeUserborn = [secretName];
+      };
+    })
   ];
 }
modules/users/schema.nix
@@ -50,6 +50,10 @@ in {
         system = mkOption {
           type = types.submodule {
             options = {
+              hashedPasswordAged = mkOption {
+                type = types.path;
+                description = "Age encryped hashed password file for user";
+              };
               settings = mkOption {
                 type = types.attrsOf (types.attrsOf types.anything);
                 default = {};
secrets/cache/hyacine/4083f995cfe813448d451523942c7a562d6a58d0e75c8760f88d57579cdb6335
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 PtFVIA /USYvMesKQJ9CbQbTVmH6xZEBhLAm2L7r9cX5n7kGFg
+t5nV5mIpdCzAe4E26+1mPi+bL5/qafCGtPLJ/Q9Nik8
+-> [;yD?WV-grease !}ooK)(g UGZy c"wOFv6
+DaSSPfbp8TPHUQQyYZdOuIjuULdJQBpM
+--- DjpOjJacSE9+sMIbxex8XAu0HguLibDSIHSOemMWK4A
+T\�$7��EZ�y�lG�HDzi2��H��)���M�n�8���%S���`�fA^���������_\9�.�J�#�c�詠:�k^rSo�)�#��i��/L��t���)
\ No newline at end of file
secrets/cache/hyacine/6ccff82816241ee2c72c68160cfcf84b1f1ab5bf8413098476e276ed0c5f8819
Binary file
secrets/cache/hyacine/73d2d398df550d6bf3625792564725190abd8a5b5a0f2881e1a4372375fed947
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 PtFVIA vDZvLNZlp0rMssg+/OPN3coEmHiRdMSJ1tVn5Yeu/G0
+ssec5l3ij3L3AeZyS/pweBnDSrMGGxkKtF+jf82/TzU
+-> $^-grease QYgTt }D?q`9W^ ,xp; E+X
+f765wJyIpkyP67EDzjeD9eUZ97q0GTG8yqYW4XIiYrdAhKpuy4QBY/08hnDO16CG
+yYq36giIuSYh7Ifm8EFTIw9/WBtuIOQggVfEjNpqSwioMtruu0CdeC2w
+--- zwydhTzExtvuXP1XoJfAozBlUVx7S0196WNgLX1oN+M
+�>x�������x3?x:���~Z;Y�Ue`>4.B�k��k1�"Đ2�Ҋ��
��%� d�5 ��Ю.eNu�"��������a��JiUxy*����x�U��M��j��
\ No newline at end of file
secrets/cache/kevin/9d9d5b63e578329ff73a1033dc0fded97e832e51a202b3a9fb69945e04bb8450
Binary file
secrets/cache/kevin/b16a6dda679e392e2ea17a79b0ab7737f807f7c267ae04bedc275c87c4ac4fd6
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 WM7kiQ bSFrcnLyHCmFUMR5Uq88NODNeVkNNG9rPtuPfJCfpWg
+H+ALpWA/RTQ/QkiqeIWKWFQl1ELr+5ty6OPB2lMR5Rc
+-> hL8ZfCQm-grease 71R dw
+svaUn8WAOtafXERuAg/kTGbPbA
+--- A4RB9EfpxEbBL1CUbG0SCoSHsZ+Oj8Np/r1gNbwtHAw
+?d�Ϝ��'JB=�C]����_#�j��A��ks�y�楼���nӅ��>�G�TE}�R����F#c	���t^-�vTG���n�厬��T�*���W���2G�H'�B
\ No newline at end of file
secrets/cache/kevin/ed4826ab5cd420ac983c82fdd40b226007e33323907a4f54c1f32f2c16d8d086
Binary file
secrets/cache/mobius/16053f4c60da485591e51136babdf6653b9601b222cb661cc34b560652bb51bf
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 qzANJQ 55Hdi4zPfvJgwpzM2YQsEj8HSZZ8Zq8EneYBOitnbkQ
+gZfeoPLLJgwDskY1yd1oBcFU0BLUV9eliXf5YxDluh0
+-> ~zagg-grease R /2$N5<R
+
+--- YYi6isBjwkMFdmploSGpnK7o1RGDSNlzJrbdiySRYig
+Z�6�.�?��X�,Ҥ�{
+����r���و[-63��(X.`ݎL��~�o���}����+���B�KO�������(q�(���9���#����7��O�K���v�1I�~�:k�A�iN���
\ No newline at end of file
secrets/cache/mobius/59a931cf1c9c08590b0d403381a075e33ec7e96e1c96bd6d2d5483184cc0ffd1
Binary file
secrets/cache/mobius/eb320cc75148082b697f42700a57382be3143b316cebec258bd3aadbb4757505
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 qzANJQ 6IRccm1koe3Pva9xgZ/FJ/Mcj4lUbO8qvCnwhjyimBQ
+AJ9BfB7GdBQRbiE35dCjhk3bxWzFx3/fxH68/wwRf2Y
+-> j-_y`7-grease tr<Jcr<
+PT9Rebk2F7He5qtCbMDzdcdiU/R+73FrdViqErojVSI9Vf6AvjmEYOYGeRb5Ji2k
+WK3HFTf8mRyQjQEimqA4pViRfOQtJDJ/2Nnj0Y6YYtWoHXu4FZ1mgw
+--- TAqkGiBOq4EYxlXG9Gezn/K57yvVtlPwIDZTePzC/ys
+cSP��?kc���*���D����t��be��6'�}�j��oA
Šp��,�G�4��5�jt�Jٛm�D/��O�,aP��",]��*֬.:O����~�J_�?+)O�
\ No newline at end of file
secrets/cache/tribios/5d9514cf683942f70983700faf03771f8e94b53cc3dec5db9655b00b8e72d731
Binary file
secrets/cache/tribios/6c29bd3abf0990af816945d4650ef47dd2e34c49c3b8e4da2791c186598d5f72
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 1YGZAA 8154svAwgoIq6ehIuyVNbvV7W7raEdOQAOfbODW78mA
+tW9zartMsmlYO7BXbLQNkkviAKtLW+fBZrYIfyEGlfs
+-> 29;j#="X-grease
+q1KqZsjnV7YuAR6YWFcTUiFrkmitR2plnPZ2dIsqov+4EnbyNTGZEHe67rBEYaOk
+tJ0J2U6l/smMI3HI8V9euFJ11w
+--- nDApoV6wfyYthfYitJqGsQNejCLI0ajQxJuvUnBTJFk
+u��Ą7���#Fv>;������$C���$�sy�봊M�qi�C^��%��&�ƃ����������ԫFd�`����2q�͊"fO䅊I`��z7KM�(�
\ No newline at end of file
secrets/cache/tribios/b87ba904d835d51840f9c2efd9b4aacb19182269adfa477826850816ee1dafe7
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 1YGZAA ZEGxtcharu8IipjdqdojuR1ED2S62LXV8Iy2Qi4cXzs
+WPOHDzzlAXlj6MtXPULExVEMYf/foUK3ATUkR6Cc4Kg
+-> Anb,+-grease
+tUwnXII4kz/wxBVieWLrOc0d6m7TovT2eZbUY5fzfzgtNYJQojwwuwwZRChOtWZ3
+fEimvXiECQvho8/qGpT06ipijtW699CJQzIrqyE
+--- dDiK56e9r4GRjWK98/CkututbCrcxt1mpsdRpSpuJOw
+�o�|0�2+������b����$��rצ�+u�VaDž��:��Ӱ:�����Sa2�ޗ傱�(8)�z0���L�j�N��8��؃��S�S�}���b��$
\ No newline at end of file
flake.lock
@@ -1,5 +1,36 @@
 {
   "nodes": {
+    "advisory-db": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1781072009,
+        "narHash": "sha256-WhB6bdXmPVmuFPRLFDz2NTdW/oAwni7lmhIXJ3YWRpQ=",
+        "owner": "rustsec",
+        "repo": "advisory-db",
+        "rev": "02c8054e58f23460910a494cc1b068752338392e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "rustsec",
+        "repo": "advisory-db",
+        "type": "github"
+      }
+    },
+    "crane": {
+      "locked": {
+        "lastModified": 1780532242,
+        "narHash": "sha256-D+BsdpxmtUwtqGoY0IXPhHgTlmqgcZKCEo1oMyn7ep0=",
+        "owner": "ipetkov",
+        "repo": "crane",
+        "rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37",
+        "type": "github"
+      },
+      "original": {
+        "owner": "ipetkov",
+        "repo": "crane",
+        "type": "github"
+      }
+    },
     "den": {
       "locked": {
         "lastModified": 1781621963,
@@ -103,6 +134,28 @@
         "type": "github"
       }
     },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "vaultix",
+          "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"
+      }
+    },
     "home-manager": {
       "inputs": {
         "nixpkgs": [
@@ -177,6 +230,31 @@
         "type": "github"
       }
     },
+    "pre-commit-hooks": {
+      "inputs": {
+        "flake-compat": [
+          "flake-compat"
+        ],
+        "gitignore": "gitignore",
+        "nixpkgs": [
+          "vaultix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1778507602,
+        "narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=",
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
         "den": "den",
@@ -188,7 +266,34 @@
         "home-manager": "home-manager",
         "impermanence": "impermanence",
         "import-tree": "import-tree",
-        "nixpkgs": "nixpkgs"
+        "nixpkgs": "nixpkgs",
+        "vaultix": "vaultix"
+      }
+    },
+    "vaultix": {
+      "inputs": {
+        "advisory-db": "advisory-db",
+        "crane": "crane",
+        "flake-parts": [
+          "flake-parts"
+        ],
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "pre-commit-hooks": "pre-commit-hooks"
+      },
+      "locked": {
+        "lastModified": 1782644541,
+        "narHash": "sha256-IHoXhygrRubHNVAj8EQj19pPjJuQnROMz0KwS2cM4vk=",
+        "owner": "HPCesia",
+        "repo": "vaultix",
+        "rev": "5f9e16430df813ad440bda342b8972752f3bb72f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "HPCesia",
+        "repo": "vaultix",
+        "type": "github"
       }
     }
   },
flake.nix
@@ -34,5 +34,13 @@
     };
     import-tree.url = "github:denful/import-tree";
     nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+    vaultix = {
+      url = "github:HPCesia/vaultix";
+      inputs = {
+        flake-parts.follows = "flake-parts";
+        nixpkgs.follows = "nixpkgs";
+        pre-commit-hooks.inputs.flake-compat.follows = "flake-compat";
+      };
+    };
   };
 }