From b486ee8cb765f967fc0067356a13d453e7ab768b Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:46:02 +1000 Subject: [PATCH 1/3] propagate node config and hostname to nixos modules --- TODO.md | 3 --- cerulean/snow/default.nix | 11 +++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index a9f40e1..5fff9a2 100755 --- a/TODO.md +++ b/TODO.md @@ -5,9 +5,6 @@ - [ ] support hs system per dir, ie hosts//overlays or hosts//nixpkgs.nix ## Queued -- [X] base should automatically be set as the default (dont do anything with the default) -- [X] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules - since cerulean ALREADY provides these - [ ] per node home configuration is a lil jank rn - [ ] deploy port should default to the first port given to `services.openssh` diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index bad3c1b..c1199a6 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -86,9 +86,10 @@ in userArgs = nodes.args // node.args; ceruleanArgs = { - inherit systems root base; + inherit systems root base node; inherit (node) system; inherit (this) snow; + hostname = name; _cerulean = { inherit inputs userArgs ceruleanArgs homeManager; @@ -128,7 +129,6 @@ in (node.deploy) ssh user - sudoCmd interactiveSudo remoteBuild rollback @@ -140,14 +140,17 @@ in nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos; in { - hostname = ssh.host; + hostname = + if ssh.host != null + then ssh.host + else ""; profilesOrder = ["default"]; # profiles priority profiles.default = { path = nixosFor node.system nixosConfigurations.${name}; user = user; - sudo = sudoCmd; + sudo = "sudo -u"; interactiveSudo = interactiveSudo; fastConnection = false; From 6b579dff1e78038c7c62851854e0d95ba38c172f Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:46:51 +1000 Subject: [PATCH 2/3] set hostname by default --- cerulean/nixos/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 664a10c..09975e5 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -37,6 +37,8 @@ else [] ); + networking.hostName = lib.mkDefault hostname; + environment.systemPackages = (with pkgs; [ sops From 630389a5989c15d92d883d62862850316322f8fa Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:48:38 +1000 Subject: [PATCH 3/3] migrate to cerubld user --- cerulean/nixos/default.nix | 27 +++++---- cerulean/nixos/remote-deploy/default.nix | 72 ++++++++++++++++++++++++ cerulean/snow/nodes/submodule.nix | 39 +++++++++---- 3 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 cerulean/nixos/remote-deploy/default.nix diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 09975e5..a716c2f 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,29 +13,32 @@ # limitations under the License. { root, - pkgs, system, + hostname, + node, + pkgs, + lib, _cerulean, ... } @ args: { - imports = with _cerulean.inputs; + imports = [ + _cerulean.inputs.sops-nix.nixosModules.sops + # _cerulean.inputs.microvm.nixosModules.microvm + # add support for `options.legacyImports` # ./legacy-imports.nix - # user configuration - (import /${root}/nixpkgs.nix) - # options declarations + # nixos options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) - sops-nix.nixosModules.sops - # microvm.nixosModules.microvm + # user's nixpkg configuration + (import /${root}/nixpkgs.nix) ] - ++ ( - if _cerulean.homeManager != null - then [./home.nix] - else [] - ); + # homemanager options declarations + ++ (lib.optional (_cerulean.homeManager != null) ./home.nix) + # remote deployment configuration + ++ (lib.optional (node.deploy.ssh.host != null) ./remote-deploy); networking.hostName = lib.mkDefault hostname; diff --git a/cerulean/nixos/remote-deploy/default.nix b/cerulean/nixos/remote-deploy/default.nix new file mode 100644 index 0000000..f2f4a90 --- /dev/null +++ b/cerulean/nixos/remote-deploy/default.nix @@ -0,0 +1,72 @@ +{ + config, + node, + lib, + pkgs, + hostname, + ... +}: let + user = node.deploy.ssh.user; + cfg = config.users.users.${user}; + + DEFAULT_USER = "cerubld"; + + isStandardDeployUser = user == DEFAULT_USER; +in { + assertions = [ + { + assertion = builtins.length node.deploy.ssh.publicKeys != 0; + message = '' + The Cerulean deployment user `${user}` for node `${hostname}` must have at least + one publicKey authorized for ssh deployment! Try setting `nodes.nodes..deploy.ssh.publicKeys = [ ... ]` <3 + ''; + } + { + assertion = cfg.isSystemUser && !cfg.isNormalUser; + message = '' + The Cerulean deployment user `${user}` for node `${hostname}` has been configured incorrectly. + Ensure `users.users.${user}.isSystemUser == true` and `users.users.${user}.isNormalUser == false`. + ''; + } + ]; + + warnings = lib.optional (node.deploy.warnNonstandardDeployUser && !isStandardDeployUser) '' + The Cerulean deplyment user `${user}` for node `${hostname}` has been overriden. + It is recommended to leave this user as `${DEFAULT_USER}` unless you TRULY understand what you are doing! + This message can be disabled by setting `.deploy.warnNonstandardBuildUser = false`. + ''; + + # prefer sudo-rs over sudo + security.sudo-rs = { + enable = true; + wheelNeedsPassword = true; + + # allow the build user to run nix commands + extraRules = [ + { + users = [user]; + runAs = "${node.deploy.user}:ALL"; + commands = [ + "${pkgs.nix}/bin/nix" + ]; + } + ]; + }; + + # ensure deployment user has SSH permissions + services.openssh.settings.AllowUsers = [user]; + + users = lib.mkIf isStandardDeployUser { + groups.${user} = {}; + + users.${user} = { + enable = true; + isSystemUser = true; + group = user; + description = "Cerulean's user for building and remote deployment."; + + shell = pkgs.bash; + openssh.authorizedKeys.keys = node.deploy.ssh.publicKeys; + }; + }; +} diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index ea30c4f..6b4ae05 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -59,23 +59,32 @@ default = "root"; example = "admin"; description = '' - The user that the system derivation will be deployed to. The command specified in + The user that the system derivation will be built with. The command specified in `.deploy.sudoCmd` will be used if `.deploy.user` is not the same as `.deploy.ssh.user` the same as above). ''; }; - sudoCmd = mkOption { - type = types.str; - default = "sudo -u"; - example = "doas -u"; + warnNonstandardDeployUser = mkOption { + type = types.bool; + default = true; + example = false; description = '' - Which sudo command to use. Must accept at least two arguments: - 1. the user name to execute commands as - 2. the rest is the command to execute + Disables the warning that shows when `deploy.ssh.user` is set to a non-standard value. ''; }; + # sudoCmd = mkOption { + # type = types.str; + # default = "sudo -u"; + # example = "doas -u"; + # description = '' + # Which sudo command to use. Must accept at least two arguments: + # 1. the user name to execute commands as + # 2. the rest is the command to execute + # ''; + # }; + interactiveSudo = mkOption { type = types.bool; default = false; @@ -145,8 +154,8 @@ ssh = { host = mkOption { - type = types.str; - default = ""; + type = types.nullOr types.str; + default = null; example = "dobutterfliescry.net"; description = '' The host to connect to over ssh during deployment @@ -171,6 +180,16 @@ ''; }; + publicKeys = mkOption { + type = types.listOf types.str; + default = []; + example = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeyZuUUmyUYrYaEJwEMvcXqZFYm1NaZab8klOyK6Imr me@puter"]; + description = '' + SSH public keys that will be authorized to the deployment user. + This key is intended solely for deployment, allowing for fine-grained permission control. + ''; + }; + opts = mkOption { type = types.listOf types.str; default = [];