diff --git a/TODO.md b/TODO.md index 7a23e43..2f4edac 100755 --- a/TODO.md +++ b/TODO.md @@ -1,14 +1,12 @@ ## Next -- [ ] formalize how the snow flake system compiles outputs, this would remove the need for `mapNodes` -- [ ] groups should allow you to set node configuration defaults - +- [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` - [ ] add `options.experimental` for snowflake - [ ] add `legacyImports` support -- [ ] support hs system per dir, ie hosts//overlays or hosts//nixpkgs.nix - ## Queued -- [ ] per node home configuration is a lil jank rn +- [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 - [ ] deploy port should default to the first port given to `services.openssh` @@ -25,19 +23,29 @@ - [ ] go through all flake inputs (recursively) and ENSURE we remove all duplicates by using follows!! +- [X] rename nixos-modules/ to nixos/ +- [X] ensure all machines are in groups.all by default + +- [X] fix nixpkgs.nix not working (default not respected) +- [X] remove dependence on nixpkgs + - [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth ## Low Priority +- [X] rename extraModules to modules? +- [X] rename specialArgs to args? + - [ ] make an extension to the nix module system (different to mix) that allows transformations (ie a stop post config, ie outputs, which it then returns instead of config) -- [ ] support `legacyImports` (?) - [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72 cause otherwise 2GB causes a failure -- [ ] write the cerulean cli +- [ ] rewrite the ceru cli in rust +- [ ] make `ceru` do local and remote deployments +- [ ] support `legacyImports` ```nix # REF: foxora diff --git a/cerulean/default.nix b/cerulean/default.nix index 80240c0..47fcdfd 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -21,7 +21,7 @@ mix.newMixture args (mixture: { ./snow ]; - version = "0.2.5-alpha"; + version = "0.2.3"; # WARNING: legacy mkFlake = mixture.snow.flake; diff --git a/cerulean/home/default.nix b/cerulean/home/default.nix deleted file mode 100644 index e854221..0000000 --- a/cerulean/home/default.nix +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - username, - lib, - ... -}: { - # NOTE: you can access the system configuration via the `osConfig` arg - - # WARNING: required for home-manager to work - programs.home-manager.enable = true; # user must apply lib.mkForce - # Nicely reload systemd units when changing configs - systemd.user.startServices = lib.mkDefault "sd-switch"; - - home = { - username = lib.mkDefault username; - homeDirectory = lib.mkDefault "/home/${username}"; - - sessionVariables = { - NIX_SHELL_PRESERVE_PROMPT = lib.mkDefault 1; - }; - }; -} diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index a716c2f..8d96f08 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,34 +13,29 @@ # limitations under the License. { root, - system, - hostname, - node, pkgs, - lib, + system, _cerulean, ... } @ args: { - imports = + imports = with _cerulean.inputs; [ - _cerulean.inputs.sops-nix.nixosModules.sops - # _cerulean.inputs.microvm.nixosModules.microvm - # add support for `options.legacyImports` # ./legacy-imports.nix - # nixos options declarations + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) - # user's nixpkg configuration - (import /${root}/nixpkgs.nix) + sops-nix.nixosModules.sops + # microvm.nixosModules.microvm ] - # 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; + ++ ( + if _cerulean.homeManager != null + then [./home-manager.nix] + else [] + ); environment.systemPackages = (with pkgs; [ diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix new file mode 100644 index 0000000..8c1aa8b --- /dev/null +++ b/cerulean/nixos/home-manager.nix @@ -0,0 +1,49 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + root, + config, + lib, + _cerulean, + ... +} @ args: let + inherit + (builtins) + attrNames + filter + pathExists + ; +in { + imports = [ + _cerulean.homeManager.nixosModules.default + ]; + + home-manager = { + users = + config.users.users + |> attrNames + |> filter (x: pathExists (root + "/homes/${x}")) + |> (x: + lib.genAttrs x (y: + import (root + "/homes/${y}"))); + + extraSpecialArgs = _cerulean.specialArgs; + sharedModules = [ + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations + (import ./nixpkgs.nix (args // {contextName = "homes";})) + ]; + }; +} diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix deleted file mode 100644 index 82117d8..0000000 --- a/cerulean/nixos/home.nix +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - _cerulean, - config, - root, - lib, - ... -} @ args: let - inherit - (builtins) - pathExists - ; - - inherit - (lib) - filterAttrs - mapAttrs - ; -in { - imports = [ - _cerulean.homeManager.nixosModules.default - ]; - - options = { - users.users = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule { - options.manageHome = lib.mkOption { - type = lib.types.bool; - default = true; - example = false; - description = '' - Whether Cerulean should automatically enable home-manager for this user, - and manage their home configuration declaratively. - - Enabled by default, but can be disabled if necessary. - ''; - }; - }); - }; - }; - - config = { - home-manager = { - useUserPackages = lib.mkDefault false; - useGlobalPkgs = lib.mkDefault true; - - overwriteBackup = lib.mkDefault false; - backupFileExtension = lib.mkDefault "bak"; - - users = - config.users.users - |> filterAttrs (name: value: value.manageHome && pathExists /${root}/homes/${name}) - |> mapAttrs (name: _: {...}: { - imports = [/${root}/homes/${name}]; - - # per-user arguments - _module.args.username = name; - }); - - extraSpecialArgs = _cerulean.specialArgs; - sharedModules = [ - ../home - - (import /${root}/nixpkgs.nix) - # options declarations - (import ./nixpkgs.nix (args // {contextName = "homes";})) - ]; - }; - }; -} diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 946748b..03925c8 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -31,7 +31,7 @@ in { default = {}; description = "Declare package repositories"; example = { - "npkgs" = { + "pkgs" = { source = "inputs.nixpkgs"; system = "x86-64-linux"; config = { @@ -53,7 +53,7 @@ in { config = let repos = cfg - |> (xs: removeAttrs xs ["base"]) + |> (xs: removeAttrs xs ["default"]) |> mapAttrs ( name: args: lib.mkForce ( @@ -65,31 +65,30 @@ in { ) ); - basePkgs = cfg.base or {}; + # XXX: TODO: would it work to use `base` instead of having default? + defaultPkgs = + cfg.default or (throw '' + Your `nixpkgs.nix` file does not declare a default package source. + Ensure you set `nixpkgs.channels.*.default = ...;` + ''); in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. # WARNING: pkgs is a reserved specialArg - _module.args = removeAttrs repos ["pkgs" "base"]; + _module.args = removeAttrs repos ["pkgs" "default"]; - nixpkgs = let - nixpkgsConfig = { - config = lib.mkForce (basePkgs.config or {}); - overlays = lib.mkForce (basePkgs.overlays or []); - }; - - nixpkgsHostsConfig = - nixpkgsConfig - // { - flake.source = lib.mkForce base; - }; - - nixpkgsHomesConfig = lib.mkIf (!config.home-manager.useGlobalPkgs) nixpkgsConfig; - in + nixpkgs = if contextName == "hosts" - then nixpkgsHostsConfig + then { + flake.source = lib.mkForce base; # DEBUG: temp while getting base to work + overlays = lib.mkForce (defaultPkgs.overlays or {}); + config = lib.mkForce (defaultPkgs.config or {}); + } else if contextName == "homes" - then nixpkgsHomesConfig + then { + config = lib.mkForce (defaultPkgs.config or {}); + overlays = lib.mkForce (defaultPkgs.overlays or []); + } else {}; }; } diff --git a/cerulean/nixos/remote-deploy/default.nix b/cerulean/nixos/remote-deploy/default.nix deleted file mode 100644 index 4aa39fd..0000000 --- a/cerulean/nixos/remote-deploy/default.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ - 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" - "ALL" # XXX: WARNING: FIX: TODO: DO NOT FUCKING USE `ALL` - ]; - } - ]; - }; - - # XXX: WARNING: FIX: TODO: use `trusted-public-keys` instead - nix.settings.trusted-users = [user]; - - # ensure deployment user has SSH permissions - services.openssh.settings.AllowUsers = [user]; - - users = lib.mkIf isStandardDeployUser { - groups.${user} = {}; - - users.${user} = { - enable = true; - description = "Cerulean's user for building and remote deployment."; - - isSystemUser = true; - group = user; - - createHome = true; - home = "/var/lib/cerulean/cerubld"; - - useDefaultShell = false; - shell = pkgs.bash; - - openssh.authorizedKeys.keys = node.deploy.ssh.publicKeys; - }; - }; -} diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index 6993ff1..048572b 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -48,22 +48,16 @@ in class = "snowflake"; # TODO: abort if inputs contains reserved names specialArgs = - (flakeInputs - // { - inherit systems root; - inherit (this) snow; - inputs = flakeInputs; - }) - |> (x: builtins.removeAttrs x ["self" "nodes"]); + flakeInputs + // { + inherit root; + inherit systems; + inherit (this) snow; # please don't be infinite recursion... + inputs = flakeInputs; + }; modules = [ ./module.nix - ({config, ...}: { - _module.args = { - self = config; - nodes = config.nodes.nodes; - }; - }) ]; }; @@ -92,10 +86,9 @@ in userArgs = nodes.args // node.args; ceruleanArgs = { - inherit systems root base nodes node; + inherit systems root base; inherit (node) system; inherit (this) snow; - hostname = name; _cerulean = { inherit inputs userArgs ceruleanArgs homeManager; @@ -118,7 +111,7 @@ in modules = [ self.nixosModules.default - (findImport /${root}/hosts/${name}) + (findImport (root + "/hosts/${name}")) ] ++ (groupModules root) ++ node.modules @@ -135,6 +128,7 @@ in (node.deploy) ssh user + sudoCmd interactiveSudo remoteBuild rollback @@ -146,17 +140,14 @@ in nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos; in { - hostname = - if ssh.host != null - then ssh.host - else ""; + hostname = ssh.host; profilesOrder = ["default"]; # profiles priority profiles.default = { path = nixosFor node.system nixosConfigurations.${name}; user = user; - sudo = "sudo -u"; + sudo = sudoCmd; interactiveSudo = interactiveSudo; fastConnection = false; diff --git a/cerulean/snow/lib/nodes.nix b/cerulean/snow/lib/nodes.nix index 48a583d..7f1a21b 100644 --- a/cerulean/snow/lib/nodes.nix +++ b/cerulean/snow/lib/nodes.nix @@ -65,7 +65,7 @@ # flatten recursion result |> concatLists # find import location - |> map (group: nt.findImport /${root}/groups/${group._name}) + |> map (group: nt.findImport (root + "/groups/${group._name}")) # filter by uniqueness |> nt.prim.unique # ignore missing groups diff --git a/cerulean/snow/module.nix b/cerulean/snow/module.nix index 79b8804..d45b35a 100644 --- a/cerulean/snow/module.nix +++ b/cerulean/snow/module.nix @@ -18,6 +18,6 @@ }: { imports = [ ./nodes - (snow.findImport /${root}/snow) + (snow.findImport (root + "/snow")) ]; } diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index 6b4ae05..ea30c4f 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -59,32 +59,23 @@ default = "root"; example = "admin"; description = '' - The user that the system derivation will be built with. The command specified in + The user that the system derivation will be deployed to. The command specified in `.deploy.sudoCmd` will be used if `.deploy.user` is not the same as `.deploy.ssh.user` the same as above). ''; }; - warnNonstandardDeployUser = mkOption { - type = types.bool; - default = true; - example = false; + sudoCmd = mkOption { + type = types.str; + default = "sudo -u"; + example = "doas -u"; description = '' - Disables the warning that shows when `deploy.ssh.user` is set to a non-standard value. + 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 ''; }; - # 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; @@ -154,8 +145,8 @@ ssh = { host = mkOption { - type = types.nullOr types.str; - default = null; + type = types.str; + default = ""; example = "dobutterfliescry.net"; description = '' The host to connect to over ssh during deployment @@ -180,16 +171,6 @@ ''; }; - 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 = [];