From ec492595582af18cecbf5ab8cdc84fc68dfd6434 Mon Sep 17 00:00:00 2001 From: oluceps Date: Fri, 1 Nov 2024 22:55:54 +0800 Subject: [PATCH] + + + --- TODO.md | 9 ++- apps/renc.nix | 4 +- flake-module.nix | 9 +-- flake.nix | 156 ++++++++++++++++++++++++--------------------- module/default.nix | 90 ++++---------------------- src/cmd/renc.rs | 3 +- src/profile.rs | 2 +- 7 files changed, 108 insertions(+), 165 deletions(-) diff --git a/TODO.md b/TODO.md index e926188..bfb5d9e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,14 @@ +- [ ] remote machine, compare hash, needs host priv key +- [ ] [edit] extra encrypt key +- [ ] deploy to other directory +- [ ] check command - [x] vaultix.d in ramfs - [x] vaultix{,.d} permission -- [x] age plugin -- [ ] check command +- [x] rage plugin support - [x] should get entire `Secret` when deploy - [x] [renc] calc hash and skip unchanged - [x] remove renced path while renc - [x] apply `Secret` metadata - [x] nix integration -- [ ] feed the toml after renced, thus store path changed +- [ ] ~~feed the toml after renced, thus store path changed~~ - [ ] ~~eval in vaultix to json, reduce requirement~~ diff --git a/apps/renc.nix b/apps/renc.nix index 78096fc..d1ffcd3 100644 --- a/apps/renc.nix +++ b/apps/renc.nix @@ -1,8 +1,8 @@ { nodes, - userFlake', pkgs, system, + package, ... }: let @@ -11,7 +11,7 @@ let inherit (builtins) attrValues; vaultixs = map (n: n.config.vaultix) (attrValues nodes); - bin = pkgs.lib.getExe userFlake'.packages.${system}.default; + bin = pkgs.lib.getExe package; in writeShellScriptBin "renc" ( diff --git a/flake-module.nix b/flake-module.nix index a0b6816..0b20b71 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -1,3 +1,4 @@ +localFlake: { lib, self, @@ -29,6 +30,7 @@ in app: import ./apps/${app}.nix { inherit (config'.vaultix) nodes pkgs; + package = localFlake.packages.${system}.default; userFlake' = self; inherit system; } @@ -60,13 +62,6 @@ in default = pkgs; defaultText = lib.literalExpression "pkgs # (module argument)"; }; - package = mkOption { - type = types.package; - default = config.vaultix.pkgs.callPackage self.packages.${pkgs.system}.default; - # defaultText = ""; - readOnly = true; - description = ''''; - }; }; } ); diff --git a/flake.nix b/flake.nix index 940516d..ef83c1b 100644 --- a/flake.nix +++ b/flake.nix @@ -22,87 +22,101 @@ crane, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = with inputs; [ - pre-commit-hooks.flakeModule + flake-parts.lib.mkFlake { inherit inputs; } ( + { flake-parts-lib, withSystem, ... }: + let + inherit (flake-parts-lib) importApply; + flakeModules.default = importApply ./flake-module.nix { + inherit (self) packages; + inherit withSystem; + }; + in + { + imports = with inputs; [ + pre-commit-hooks.flakeModule + flakeModules.default + ./test + ]; + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + perSystem = + { + # config, + self', + # inputs', + pkgs, + system, + ... + }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = with inputs; [ + rust-overlay.overlays.default + self.overlays.default + ]; + }; - ./flake-module.nix - ./test - ]; - systems = [ - "x86_64-linux" - "aarch64-linux" - ]; - perSystem = - { - # config, - self', - # inputs', - pkgs, - system, - ... - }: - { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; - overlays = with inputs; [ - rust-overlay.overlays.default - self.overlays.default - ]; - }; + vaultix = { + nodes = self.nixosConfigurations; + }; + apps = { + default = { + type = "app"; + program = pkgs.lib.getExe self'.packages.default; + }; + }; - vaultix = { - nodes = self.nixosConfigurations; - }; - apps.default = { - type = "app"; - program = pkgs.lib.getExe self'.packages.default; - }; + packages = rec { + default = + let + toolchain = pkgs.rust-bin.nightly.latest.minimal; + craneLib = (crane.mkLib pkgs).overrideToolchain toolchain; + inherit (craneLib) buildPackage; + in + (buildPackage { + src = craneLib.cleanCargoSource ./.; + nativeBuildInputs = [ + pkgs.rustPlatform.bindgenHook + ]; + meta.mainProgram = "vaultix"; + }); + vaultix = default; + }; - packages.default = - let - toolchain = pkgs.rust-bin.nightly.latest.minimal; - craneLib = (crane.mkLib pkgs).overrideToolchain toolchain; - inherit (craneLib) buildPackage; - in - (buildPackage { - src = craneLib.cleanCargoSource ./.; - nativeBuildInputs = [ - pkgs.rustPlatform.bindgenHook + formatter = pkgs.nixfmt-rfc-style; + + devShells.default = pkgs.mkShell { + inputsFrom = [ + pkgs.vaultix ]; - meta.mainProgram = "vaultix"; - }); - formatter = pkgs.nixfmt-rfc-style; + RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; + buildInputs = with pkgs; [ + just + nushell + ]; + }; - devShells.default = pkgs.mkShell { - inputsFrom = [ - pkgs.vaultix - ]; + pre-commit = { + check.enable = true; + settings.hooks = { + nixfmt-rfc-style.enable = true; + }; + }; - RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; - buildInputs = with pkgs; [ - just - nushell - ]; }; + flake = { + inherit flakeModules; - pre-commit = { - check.enable = true; - settings.hooks = { - nixfmt-rfc-style.enable = true; - }; + overlays.default = final: prev: { + vaultix = inputs.self.packages.${prev.system}.default; }; + nixosModules.default = import ./module self; }; - flake = { - flakeModule = ./flake-module.nix; - - overlays.default = final: prev: { - vaultix = inputs.self.packages.${prev.system}.default; - }; - nixosModules.default = ./module; - - }; - }; + } + ); } diff --git a/module/default.nix b/module/default.nix index 33f7135..dd39b1a 100644 --- a/module/default.nix +++ b/module/default.nix @@ -1,3 +1,4 @@ +vaultixSelf: { config, options, @@ -13,7 +14,6 @@ let isAttrs isPath readFile - mkPackageOption literalExpression mkEnableOption mkIf @@ -29,7 +29,7 @@ let ) "`systemd.sysusers` or `services.userborn` must be enabled."; storagePath = self + "/" + cfg.settings.storageDirRelative; - storageExist = assertMsg (builtins.pathExists (storagePath)) "${storagePath} doesn't exist plz manually create and add to git first (may need a placeholder for git to recognize it)"; + storageExist = assertMsg (builtins.pathExists storagePath) "${storagePath} doesn't exist plz manually create and add to git first (may need a placeholder for git to recognize it)"; settingsType = types.submodule (submod: { options = { @@ -61,7 +61,7 @@ let hostKeys = mkOption { type = types.listOf ( - types.submodule ({ + types.submodule { options = { path = mkOption { type = types.path; @@ -70,7 +70,7 @@ let type = types.str; }; }; - }) + } ); default = config.services.openssh.hostKeys; readOnly = true; @@ -139,53 +139,6 @@ let }; }) ); - description = '' - The list of age identities that will be presented to `rage` when decrypting the stored secrets - to rekey them for your host(s). If multiple identities are given, they will be tried in-order. - - The recommended options are: - - - Use a split-identity ending in `.pub`, where the private part is not contained (a yubikey identity) - - Use an absolute path to your key outside of the nix store ("/home/myuser/age-master-key") - - Or encrypt your age identity and use the extension `.age`. You can encrypt an age identity - using `rage -p -o privkey.age privkey` which protects it in your store. - - If you are using YubiKeys, you can specify multiple split-identities here and use them interchangeably. - You will have the option to skip any YubiKeys that are not available to you in that moment. - - To prevent issues with master keys that may be sometimes unavailable during encryption, - an alternate syntax is possible: - - ```nix - age.rekey.masterIdentities = [ - { - # This has the same type as the other ways to specify an identity. - identity = ./password-encrypted-identity.pub; - # Optional; This has the same type as `age.rekey.hostPubkey` - # and allows explicit association of a pubkey with the identity. - pubkey = "age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3290gq"; - } - ]; - ``` - - If a pubkey is explicitly specified, it will be used - in place of the associated identity during encryption. This prevents additional prompts - in the case of a password encrypted key file or prompts for identities that can only be accessed - by certain people in a multi-user scenario. For Yubikey identities the pubkey can be automatically - extracted from the identity file, if there is a comment of the form `Recipient: age1yubikey1` - present in the identity file. - This should be the case for identity files generated by the `age-plugin-yubikey` CLI. - See the description of [pull request #28](https://github.com/oddlama/agenix-rekey/pull/28) - for more information on the exact criteria for automatic pubkey extraction. - - For setups where the primary identity may change depending on the situation, e.g. in a multi-user setup, - where each person only has access to their own personal Yubikey, check out the - `AGENIX_REKEY_PRIMARY_IDENTITY` environment variable. - - Be careful when using paths here, as they will be copied to the nix store. Using - split-identities is fine, but if you are using plain age identities, make sure that they - are password protected. - ''; default = [ ]; example = [ ./secrets/my-public-yubikey-identity.txt.pub @@ -196,17 +149,9 @@ let ]; }; - extraEncryptionPubkeys = mkOption { + extraReceipients = mkOption { type = with types; listOf (coercedTo path toString str); - description = '' - When using `agenix edit FILE`, the file will be encrypted for all identities in - rekey.masterIdentities by default. Here you can specify an extra set of pubkeys for which - all secrets should also be encrypted. This is useful in case you want to have a backup indentity - that must be able to decrypt all secrets but should not be used when attempting regular decryption. - If the coerced string is an absolute path, it will be used as if it was a recipient file. - Otherwise, the string will be interpreted as a public key. - ''; default = [ ]; example = [ ./backup-key.pub @@ -216,22 +161,6 @@ let hostPubkey = mkOption { type = with types; coercedTo path (x: if isPath x then readFile x else x) 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. - HINT: If you want to use a path, make sure to use an actual nix path, so for example - `./host.pub`, otherwise it will be interpreted as the content and cause errors. - Alternatively you can use `readFile "/path/to/host.pub"` yourself. - - If you are managing a single host only, you can use `"/etc/ssh/ssh_host_ed25519_key.pub"` - here to allow the rekey app to directly read your pubkey from your system. - - If you are managing multiple hosts, it's recommended to either store a copy of each - host's pubkey in your flake and use refer to those here `./secrets/host1-pubkey.pub`, - or directly set the host's pubkey here by specifying `"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."`. - - Make sure to NEVER use a private key here, as it will end up in the public nix store! - ''; default = # This pubkey is just binary 0x01 in each byte, so you can be sure there is no known private key for this "age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3290gq"; @@ -311,7 +240,10 @@ in { options.vaultix = { - package = mkPackageOption pkgs "vaultix" { }; + package = mkOption { + type = types.package; + default = vaultixSelf.packages.${pkgs.system}.vaultix; + }; settings = mkOption { type = settingsType; @@ -333,12 +265,12 @@ in config = let - profile = (pkgs.formats.toml { }).generate "secretsMetadata" (cfg); + profile = (pkgs.formats.toml { }).generate "secretsMetadata" cfg; in mkIf (sysusers && storageExist) { test = profile; - systemd.services.agenix-install-secrets = { + systemd.services.vaultix-install-secrets = { wantedBy = [ "sysinit.target" ]; after = [ "systemd-sysusers.service" ]; unitConfig.DefaultDependencies = "no"; diff --git a/src/cmd/renc.rs b/src/cmd/renc.rs index 672be1e..85b5a17 100644 --- a/src/cmd/renc.rs +++ b/src/cmd/renc.rs @@ -25,7 +25,6 @@ use age::{x25519, Identity}; use super::stored_sec_path::StoredSecretPath; use crate::helper::parse_identity::ParsedIdentity; impl Profile { - // TODO: plugin compatibility pub fn get_key_pair_iter<'a>(&'a self) -> impl Iterator> + 'a { self.settings .master_identities @@ -127,7 +126,7 @@ impl Profile { .decrypt(iter::once( &self .settings - .host_keys + .host_keys // TODO: cannot compare on remote machine .get(0) .unwrap() .get_identity() diff --git a/src/profile.rs b/src/profile.rs index 4d03ab4..b8344d3 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -27,7 +27,7 @@ pub struct Settings { pub decrypted_dir: String, pub decrypted_mount_point: String, pub host_identifier: String, - pub extraEncryption_pubkeys: Vec, + pub extra_receipients: Vec, pub host_pubkey: String, pub host_keys: Vec, pub storage_dir_relative: String,