diff --git a/SwarselSystems.org b/SwarselSystems.org index 7e208e1..70ac1ed 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -6431,13 +6431,31 @@ Setup timezone and locale. I want to use the US layout, but have the rest adapte } #+end_src -**** PII management +**** TODO PII management :PROPERTIES: :CUSTOM_ID: h:82b8ede2-02d8-4c43-8952-7200ebd4dc23 :END: This is also exposed to home-manager configurations, in case this ever breaks, I can also go back to importing =nixosConfig= as an attribute in the input attribute set and call the secrets using =nixosConfig.repo.secrets=. +Two modes of operation are supported: +- loading in a secret as a plain attribute set ={a = 3;}= +- loading in a function ={ nodes, ...}: {mac = nodes.xxx.interface.mac;}= + +Both cases should return the proper values - in the second case the parent set must be passed to the inherit under =options.repo.secrets=. + +In general, there are three types of pii file: +- Per-node secrets: found under the nodes =secretDir= as =pii.nix.enc= (exposed as =config.repo.secrets.local=) +- Common secrets: found in =secrets/repo/pii.nix.enc= (exposed as =config.repo.secrets.common=) +- Global definitions of networks and domains: found in =secrets/repo/globals.nix.enc= (not exposed to nodes, but only loaded in in [[#h:af83893d-c0f9-4b45-b816-4849110d41b3][Globals]]) + +This system, while highly pleasant to work with during everyday use, sometimes has quirks: +- =nixos-rebuild= cannot be used + - this is because we need to call =nix build= in a separate step where [[#h:315e6ef6-27d5-4cd8-85ff-053eabe60ddb][sops-decrypt-and-cache]] will be cached. Once we have a finished build we can switch to that (all of this is handled by [[#h:c3362d4e-d3a8-43e8-9ef7-272b6de0572e][swarsel-deploy]]) + - this is a bit cumbersome for hosts that are not supported by [[#h:c3362d4e-d3a8-43e8-9ef7-272b6de0572e][swarsel-deploy]] (currently this is mostly home-manager only configurations). In principal, building their config locally should work without issue, however, sometimes the decrypt step hiccups. In that case I usually resort to scp'ing the decrypted secrets to the host in question using [[#h:788937cf-8816-466b-8e57-1b695cb50f52][justfile]]'s =just secrets= command. After that, the secrets in =/var/tmp/nix-import-encrypted= need to be moved to the correct dir depending on the build user uid (0/root or 1000/swarsel). After that I delete the cached secrets again. TODO: fix this behaviour. +- the used nix version needs to be kept in sync with the version of nix that nix-plugins is compiled against + - currently, this mostly poses an issue when provisioning new hosts - the version of nixos-anywhere that I am using uses nix =2.28.x=, so I wrote a dedicated [[#h:4d0548db-99b2-4e07-b762-6d86fbb26d4c][Devshell (checks)]] (called =deploy=) that is used to set this environment up. This devshell is automatically used by the [[#h:788937cf-8816-466b-8e57-1b695cb50f52][justfile]] command =just bootstrap=. + #+begin_src nix-ts :tangle modules/nixos/common/pii.nix # largely based on https://github.com/oddlama/nix-config/blob/main/modules/secrets.nix { config, inputs, lib, nodes, ... }: @@ -9029,6 +9047,12 @@ lspci -k -d 14c3:0616 | | Kernel | driver | in | use: | mt7921e | | | | | | | | | Kernel | modules: | mt7921e | | | | | | | | | +A little note about the secrets part: + +systemd-initrd provides a lightweight SSH server in form of dropbear - in order to not crash we need to have a hostkey ready in the initrd. I achieve this by generating a hostkey in the build process in casy I am doing an initial install (another way - and safer - would be to push that in [[#h:74db57ae-0bb9-4257-84be-eddbc85130dd][swarsel-bootstrap]] I guess) - this results in the hostkey landing in the nix store. However, I only ever spend like 5 minutes in this state before I rebuild to the full system, where this hostkey is no longer used. This is because upon first activation, we will then run another =ssh-keygen= that will then write to persisted storage. All "unlock" SSH hosts are to be reached over port 2222, and =systemctl default= will be run immediately upon login, which will guide towards attaining a working system (in normal operation, it will simply ask for the password). + +I also take some precautions in how I get networking information during stage 1. For the most part, I just use [[#h:12370671-7892-4a74-a804-84f871acde06][systemd-networkd (server)]], however, for hosts in my local network, I take another step to define the network in the =kernelParams=, to make extra sure I can reach it. + #+begin_src nix-ts :tangle modules/nixos/server/disk-encrypt.nix { self, pkgs, lib, config, globals, minimal, ... }: let @@ -9107,6 +9131,7 @@ lspci -k -d 14c3:0616 **** Attic setup +By default, attic only provides a cli client to authenticate to caches. I want all my servers to use my main binary cache, which is what I set up here. #+begin_src nix-ts :tangle modules/nixos/server/attic-setup.nix { lib, config, pkgs, globals, ... }: @@ -9163,11 +9188,25 @@ lspci -k -d 14c3:0616 } #+end_src -**** Wireguard +**** TODO Wireguard :PROPERTIES: :CUSTOM_ID: h:8cf0018d-00ba-4616-87d9-f91c614face9 :END: +I use wireguard for two things: +- proxying of my services (both internal and external) to my proxy node +- proxying of my internal services to an internal [[#h:302468d2-106a-41c8-b2bc-9fdc40064a9c][NGINX]] in order to save on bandwidth + +At current, this means that I am running two wireguard interfaces - the following configuration allows me to define an arbitrary number of wireguard interfaces that each host can be part of (either as "client" or "server"). All of these connections are really point-to-point. On the client side, I set =persistentKeepalive= unconditionally, which is lazy (and a bit of a security issue). Also, I noticed that I lose 12 bits of MTU somewhere - I would have expected to be able to set MTU 1420, but that does not seem to be the case. TODO:fix both + +In order to define a new wireguard interface, I have to: + +- add another - network to globals + - add its members under hosts + - add the =wgInterfaceName= together with id to the server nodes local pii + - make sure all members have their private keys in their secrets file and their public key under =secrets/public/wg= + - make sure that all preshared keys exist in =secrets/repo/wg.yaml= TODO: maybe split wg.yaml into per-interface files with finer-grained acl + #+begin_src nix-ts :tangle modules/nixos/server/wireguard.nix { self, lib, pkgs, config, confLib, nodes, globals, ... }: let @@ -19798,7 +19837,9 @@ Sets up a systemd user service for anki that does not stall the shutdown process } #+end_src -***** attic store push service +***** TODO attic store push service + +Normally, I want to push all nix build artifacts to my main cache automatically, which is realized here. Note that authentication to the cache must be done manually once on client nodes. TODO: fix that #+begin_src nix-ts :tangle modules/home/common/attic-store-push.nix { lib, config, pkgs, ... }: diff --git a/index.html b/index.html index 40c054a..5ffc3c6 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - + SwarselSystems: NixOS + Emacs Configurationo @@ -226,14 +226,14 @@
  • 2.4. Packages (pkgs)
  • 2.5. Globals
  • 2.6. Hosts
  • -
  • 2.7. Topology
  • +
  • 2.7. Topology (nix-topology generated network diagram)
  • 2.8. Devshell (checks)
  • 2.9. Templates
  • 2.10. Formatter (treefmt-nix)
  • -
  • 2.11. TODO Modules
  • +
  • 2.11. Modules
  • 2.12. Apps
  • 2.13. Overlays
  • -
  • 2.14. Installer iso
  • +
  • 2.14. Installer images (iso, kexec)
  • 2.15. Installer flake
  • @@ -309,30 +309,30 @@
  • 3.1.3.2.3. disko
  • -
  • 3.1.3.3. Stoicclub (OCI) +
  • 3.1.3.3. Stoicclub (OCI)
  • -
  • 3.1.3.4. Liliputsteps (OCI) +
  • 3.1.3.4. Liliputsteps (OCI)
  • -
  • 3.1.3.5. Twothreetunnel (OCI) +
  • 3.1.3.5. Twothreetunnel (OCI)
  • 3.1.3.6. Eagleland (Hetzner) @@ -375,7 +375,7 @@
  • 3.2.1.8. User setup, Make users non-mutable
  • 3.2.1.9. Setup login keymap
  • 3.2.1.10. Time, locale settings
  • -
  • 3.2.1.11. PII management
  • +
  • 3.2.1.11. TODO PII management
  • 3.2.1.12. Lanzaboote (secure boot)
  • 3.2.1.13. Boot
  • 3.2.1.14. Impermanence
  • @@ -393,7 +393,7 @@
  • 3.2.2.8. Pipewire
  • 3.2.2.9. Common network settings
  • 3.2.2.10. sops
  • -
  • 3.2.2.11. Remote building
  • +
  • 3.2.2.11. Remote building
  • 3.2.2.12. Theme (stylix)
  • 3.2.2.13. Programs (including zsh setup)
  • @@ -576,6 +579,7 @@
  • 3.3.2.32.10. element service for tray
  • 3.3.2.32.11. vesktop service for tray
  • 3.3.2.32.12. syncthing service for tray
  • +
  • 3.3.2.32.13. TODO attic store push service
  • 3.3.2.33. Sway
  • @@ -634,7 +638,7 @@
  • 3.4.6.4. hm-specialisation
  • 3.4.6.5. cdw
  • 3.4.6.6. cdb
  • -
  • 3.4.6.7. prstatus
  • +
  • 3.4.6.7. prstatus
  • 3.4.6.8. bak
  • 3.4.6.9. timer
  • 3.4.6.10. e
  • @@ -670,6 +674,8 @@
  • 3.4.7. Packages (config)
  • @@ -903,29 +909,56 @@
  • 7. Appendix C: Explanations to nix functions and operators
  • -This file has 118389 words spanning 31287 lines and was last revised on 2025-12-18 17:25:42 +0100. +This file has 121689 words spanning 31923 lines and was last revised on 2025-12-23 03:19:19 +0100.

    @@ -993,7 +1026,7 @@ This section defines my Emacs configuration. For a while, I considered to use ry

    -My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-12-18 17:25:42 +0100) +My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-12-23 03:19:19 +0100)

    @@ -1888,7 +1921,10 @@ A breakdown for the functions that have a non-obvious purpose:
  • uses nixpkgs.lib.genAttrs
  • Also, in that function I am defining the pkgs that should be used when I reference pkgs in the actual configuration. I want to make sure that the correct system is used (keep in mind this is for home-manager configurations, which need that info! As a remark, you would not set this for a NixOS host), that I load my Overlays (extra packages and modifications that I add to pkgs), as well as a setting that allows me to install unfree software. As a base package set I choose nixpkgs from my inputs (and so does nearly every configuration out there. Keep in mind however that you could use any package set here! nixpkgs however also comes with a lot of useful lib functions (that are not builtins to the nix language!))
  • -
  • mkTrueOption: Defines a nixos module option that is by default enables (as opposed to mkEnableOption which are per default disabled).
  • +
  • mkTrueOption: Defines a nixos module option that is by default enables (as opposed to mkEnableOption which are per default disabled). +
      +
    • uses [BROKEN LINK: h:3ce2e7ef-0f6c-4137-a978-ba190b26dcac] to create the defaulted option.
    • +
  • mkStrong:
    • An alias for (nixpkgs.lib.mkOverride 60), which is higher than setting an option normally (i.e. option = value; which has priority 100), but being of lower priority than using nixpkgs.lib.mkForce, which has priority 50 (lower priority takes precedence). For completeness' sake, the priority set when using nixpkgs.lib.mkDefault is 1000 (a very low value).
    • @@ -1911,8 +1947,34 @@ A breakdown for the functions that have a non-obvious purpose:
      • uses nixpkgs.lib.map to actually import the list of modules that are passed to mkImports in names.
      +
    • cidrToSubnetMask: this takes in an IP address in cidr notation (for example 192.168.1.0/24) and returns the matching subnet mask (here: 255.255.255.0) +
    • +
    • mkIfElseList generates either one or another list based on a conditional +
        +
      • uses nixpkgs.lib.mkMerge to merge the results of the de-facto if and else, yielding a single list in the end.
      • +
    • +
    • getBaseDomain takes in a domain (like sub.about.com) and extracts only the base domain (here: about.com) +
    • +
    • getSubDomain works in a similar way on a domain subdomain (for the above example: sub) +
    • +
    • toCapitalized: returns the capitalized version of a string ("about" -> "About") +
    +

    Concerning the flake = _: part:

    @@ -2027,11 +2089,19 @@ in

    2.4. Packages (pkgs)

    -This does not use perSystem from flake-parts since some of my custom packages are not able to be built on darwin systems, and I was not yet interested in writing logic for handling that. Instead I use forEachLinuxSystem as described in Library functions in roder to only build this for linux hosts. +This does not use perSystem from flake-parts for package outputs, since some of my custom packages are not able to be built on darwin systems, and I was not yet interested in writing logic for handling that. Instead I use forEachLinuxSystem as described in Library functions in roder to only build this for linux hosts.

    -Other nix users can make use of these packages either by installing them directly in their config (using my flake as an input and then installing <packages.<systems>.<name>) or by making use of the overlay that I provide in Overlays. In the latter case all packages will be made available to the consuming flake +Other nix users can make use of these packages either by installing them directly in their config (using my flake as an input and then installing <packages.<systems>.<name>) or by making use of the overlay that I provide in Overlays. In the latter case all packages will be made available to the consuming flake. +

    + +

    +You might now be wondering why I then have a perSystem in this file. This has to do with the nixos-extra-modules flake output that I pulled in in flake.nix skeleton (inputs): it provides a few library functions that I want to use in the Globals system. Since however these globals are evaluated to a flake output I need to make sure that these library functions are not only available from within a configuration, but also as a callable expression in the flake - and of course they also should be available in configurations. Generally, lib exists under pkgs, which means that it is built for an architecture. So, if I want to expand lib, I need to make sure this is done for all architectures. For that, I extend the flake-parts perSystem options by a pkgs option using mkTransposedPerSystemModule. This points to a file that will specify the correct pkgs - in this case, this is the same file. We then add a module arg named pkgs which can no make use of the system parameter thanks to what we did above, and set the correct overlay - the self.overlay.defaults includes the overlay from nixos-extra-modules that we need (see Overlays). Finally, we make this pkgs available as an output. +

    + +

    +The _module.args part is needed because we need to set/override the flake-parts pkgs as per https://flake.parts/module-arguments.html?highlight=modulewith#pkgs.

    @@ -2099,29 +2169,43 @@ I also have a file for global values that cannot be attributed to one nixo Lastly, in order make this actually available to my configurations, i use the inherit (globalsSystem.config.globals) [...] which produces the globals output which I will pass to the specialArgs of my nixosConfigurations, which is when I will be finally able to use these definitions in my config.

    +

    +Similar to Packages (pkgs), we again create a perSystem module for globals. We want this because we need to ingest the right lib with the extensions from nixos-extra-modules as discussed in Packages (pkgs). One side effect is that instead of a single globals output, we instead create outputs of the form globals.<arch>. This is not a problem as long as we pass one of these in Hosts, but it needs to be kept in mind. In effect, because we overrode pkgs, we can now use the perSystem module argument pkgs which will fetch the right pkgs. Anther method would be using inputs' together with inputs'.pkgs.lib as per https://flake.parts/module-arguments.html?highlight=modulewith#inputs. +

    +
    # adapted from https://github.com/oddlama/nix-config/blob/main/nix/globals.nix
    -{ inputs, ... }:
    +{ self, inputs, ... }:
     {
    -  flake = { config, lib, ... }:
    +
    +  imports = [
    +    (
    +      { lib, flake-parts-lib, ... }:
    +      flake-parts-lib.mkTransposedPerSystemModule {
    +        name = "globals";
    +        file = ./globals.nix;
    +        option = lib.mkOption {
    +          type = lib.types.unspecified;
    +        };
    +      }
    +    )
    +  ];
    +  perSystem = { lib, pkgs, ... }:
         {
           globals =
             let
               globalsSystem = lib.evalModules {
                 prefix = [ "globals" ];
                 specialArgs = {
    -              inherit (inputs.self.pkgs.x86_64-linux ) lib; # fuck
    -              # inherit (self.outputs) lib;
    +              inherit (pkgs) lib;
    +              inherit (self.outputs) nodes;
                   inherit inputs;
    -              inherit (config) nodes;
                 };
                 modules = [
                   ../modules/nixos/common/globals.nix
                   (
                     { lib, ... }:
                     let
    -                  # Try to access the extra builtin we loaded via nix-plugins.
    -                  # Throw an error if that doesn't exist.
                       sopsImportEncrypted =
                         assert lib.assertMsg (builtins ? extraBuiltins.sopsImportEncrypted)
                           "The extra builtin 'sopsImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?";
    @@ -2140,7 +2224,7 @@ Lastly, in order make this actually available to my configurations, i use the flake.nix skeleton), I told you how I used to use noweb-ref blocks to achieve this task. You see, a single nixosConfiguration uses nixpkgs.lib.nixosSystem, passing modules and arguments to define itself. I have automated this process by reading all directories in the hosts/ directory and then applying nixpkgs.lib.nixosSystem as a function on these returns. I also provide a nixosConfigurationsMinimal output which is ingested by the flake in install/flake.nix to be used during the initial deployment of a new system (it basically just disables most modules).
     

    +

    +Note that the config top-level module attribute includes the entire flake config (this is not the same behaviour as for perSystem). +

    +
    • mkNixosHost: Very much akin to a simple call of nixpkgs.lib.nixosSystem, I simply define specialArgs and modules that I want to use for every configuration. Here, I load all the extra modules from my other input flakes. Also, I add the globals output from Globals and the nodes output that I define right here (it simply mirrors all "full" configurations - nixOS and darwin. I like to refer to home-manager only and nix-on-droid as a "half" configurations). It is also here that I set the node name for the configuration (I prefer this explicit call over referencing networking.hostName or such) and the directory that should be used for secrets of a configuration.
    • mkDarwinHost works in the same way but for darwin machines.
    • -
    • mkHalfHost is a function that either creates a pure home-manager configuration or a nix-on-droid one. The type must be explicitly passed when calling the function. Here, again, we make use of pkgsFor that we defined in Library functions. Also, we make sure to pass extraSpecialArgs (the pendant to specialArgs, just for home-manager configurations).
    • diskoConfigurations: specifies a default disko configuration that is to be used if someone pulls in my flake as a disko configuration. This is not used by me, but I think it is kind of neat.
    • nodes: As stated above, a shorthand for my configurations. Is built using the The '//' operator
    • + +
    • guestConfigurations: This holds all microvm hosts.

    -The rest of the outputs either define or help define the actual configurations: +The rest of the functions are used to build full NixOS systems as well as halfConfigurations regardless of system architecture:

      -
    • mkHalfHostConfigs works by calling the nixpkgs.lib.foldl function to build an attribute set of home-manager/nix-on-droidd configurations -
        -
      • uses nixpkgs.lib.foldl on the list created by nixpkgs.lib.map over mkHalfHost. In each step of the fold, I receive an attribute set. This attribute set is then merged to the resulting set using the (acc: set: acc // set) expression
      • -
      • uses The '//' operator to merge the sets of the fold operation.
      • -
    • +
    • readHostDirs simply gets the config directories
    • +
    • mkHalfHostsForArch generates attribute sets for every halfHost found through readHostDirs
    • +
    • mkHostsForArch does the same for full NixOS configurations
    • +
    • mkConfigurationsPerArch is the wrapper that calls mkHalfHostsForArch or mkHostsForArch depending on the config it is called for, holding all configurations in principal
    • +
    • halfConfigurationsPerArch= returns all halfConfigurations of a certain type (android or home-manager only)
    • +
    • ConfigurationsPerArch= does the same for full NixOS systems (NixOS or darwin). These can further be specialized by passing in the corresponding minimal arg that is used during bootstrapping.
    @@ -2213,7 +2301,8 @@ The rest of the outputs either define or help define the actual configurations: specialArgs = { inherit inputs outputs self minimal homeLib configName arch; inherit (config.pkgs.${arch}) lib; - inherit (config) globals nodes; + inherit (config) nodes; + globals = config.globals.${arch}; type = "nixos"; }; modules = [ @@ -2270,7 +2359,8 @@ The rest of the outputs either define or help define the actual configurations: inputs.nix-darwin.lib.darwinSystem { specialArgs = { inherit inputs lib outputs self minimal configName; - inherit (config) globals nodes; + inherit (config) nodes; + globals = config.globals.${arch}; }; modules = [ # inputs.disko.nixosModules.disko @@ -2305,7 +2395,8 @@ The rest of the outputs either define or help define the actual configurations: inherit pkgs; extraSpecialArgs = { inherit inputs lib outputs self configName arch type; - inherit (config) globals nodes; + inherit (config) nodes; + globals = config.globals.${arch}; minimal = false; }; modules = [ @@ -2398,7 +2489,7 @@ The rest of the outputs either define or help define the actual configurations:
    -

    2.7. Topology

    +

    2.7. Topology (nix-topology generated network diagram)

    This defines some topology for the nix-topology modole that can not otherwise be parsed from the config (or is global). For example, this is used to define a number of switches, printers and routers. The topology graph is built from left to right, meaning that nodes.internet = mkInternet { connections = [ (mkConnection "moonside" "wan") ]; }; means that the node internet 'initiates' the connection to the node moonside (internet will be on the left). @@ -2408,7 +2499,7 @@ This defines some topology for the flake-parts:

      -
    • perSystem is a mechanism that tells flake-parts to build the following attribute set for all systems. This replaces the need to handle myself any system or pkgs variables, this is done by flake-parts.
    • +
    • perSystem is a mechanism that tells flake-parts to build the following attribute set for all systems. This replaces the need to handle myself any system or pkgs variables, this is done by flake-parts. In this case this is needed so that the topology diagram can be built locally.
    @@ -2421,6 +2512,7 @@ Another note concerning flake-parts: perSystem.topology.modules = [ ({ config, ... }: let + inherit (self.outputs) globals; inherit (config.lib.topology) mkInternet mkDevice @@ -2435,95 +2527,89 @@ Another note concerning flake-parts: networks = { home-lan = { name = "Home LAN"; - cidrv4 = "192.168.1.0/24"; + inherit (globals.networks.home-lan) cidrv4; + }; + fritz-wg = { + name = "Wireguard Tunnel for Fritzbox net access"; + inherit (globals.networks.twothreetunnel-wg) cidrv4; }; wg = { - name = "Wireguard Tunnel"; - cidrv4 = "192.168.3.0/24"; + name = "Wireguard Tunnel for proxy access"; + inherit (globals.networks.twothreetunnel-wg) cidrv4; }; }; nodes = { internet = mkInternet { connections = [ + (mkConnection "fritzbox" "dsl") (mkConnection "moonside" "wan") - (mkConnection "pfsense" "wan") - (mkConnection "milkywell" "wan") + (mkConnection "belchsfactory" "wan") + (mkConnection "twothreetunnel" "wan") + (mkConnection "stoicclub" "wan") + (mkConnection "liliputsteps" "wan") + (mkConnection "eagleland" "wan") (mkConnection "magicant" "wifi") (mkConnection "toto" "bootstrapper") (mkConnection "hotel" "demo host") ]; }; - hotel.interfaces."demo host" = { }; - toto.interfaces."bootstrapper" = { }; - milkywell.interfaces.wan = { }; - moonside.interfaces.wan = { }; - pfsense = mkRouter "pfSense" { - info = "HUNSN RM02"; + fritzbox = mkRouter "FRITZ!Box" { + info = "FRITZ!Box 7682"; image = "${self}/files/topology-images/hunsn.png"; interfaceGroups = [ [ + "eth1" "eth2" "eth3" - "eth4" - "eth5" - "eth6" + "eth-wan" + "wifi" ] - [ "wan" ] + [ "dsl" ] ]; - interfaces.wg = { - addresses = [ "192.168.3.1" ]; - network = "wg"; - virtual = true; - type = "wireguard"; - }; connections = { - eth2 = mkConnection "switch-livingroom" "eth1"; - eth4 = mkConnection "winters" "eth1"; - eth3 = mkConnection "switch-bedroom" "eth1"; - eth6 = mkConnection "wifi-ap" "eth1"; - wg = mkConnection "moonside" "wg"; + eth1 = mkConnection "winters" "eth1"; + eth2 = mkConnection "switch-bedroom" "eth1"; + eth3 = mkConnection "switch-livingroom" "eth1"; + eth-wan = mkConnection "hintbooth" "eth6"; + wgPyramid = mkConnection "pyramid" "fritz-wg"; + wgMagicant = mkConnection "magicant" "fritz-wg"; + wifiPyramid = mkConnection "pyramid" "wifi"; + wifiMagicant = mkConnection "magicant" "wifi"; + wifiBakery = mkConnection "bakery" "wifi"; + wifiMachpizza = mkConnection "machpizza" "wifi"; }; interfaces = { + eth1 = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + network = "home-lan"; + }; eth2 = { - addresses = [ "192.168.1.1" ]; + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; network = "home-lan"; }; eth3 = { - addresses = [ "192.168.1.1" ]; + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; network = "home-lan"; }; - eth4 = { - addresses = [ "192.168.1.1" ]; + eth-wan = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; network = "home-lan"; }; - eth6 = { - addresses = [ "192.168.1.1" ]; + wifi = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + virtual = true; network = "home-lan"; }; - }; - }; - - winters.interfaces."eth1" = { }; - bakery.interfaces = { - "eth1" = { }; - "wifi" = { }; - }; - - wifi-ap = mkSwitch "Wi-Fi AP" { - info = "Huawei"; - image = "${self}/files/topology-images/huawei.png"; - interfaceGroups = [ - [ - "eth1" - "wifi" - ] - ]; - connections = { - wifi = mkConnection "bakery" "wifi"; + fritz-wg = { + addresses = [ globals.networks.fritz-wg.hosts.fritzbox.ipv4 ]; + network = "wg"; + virtual = true; + type = "wireguard"; + }; }; }; @@ -2549,33 +2635,6 @@ Another note concerning flake-parts: }; }; - nswitch = mkDevice "Nintendo Switch" { - info = "Nintendo Switch"; - image = "${self}/files/topology-images/nintendo-switch.png"; - interfaces.eth1 = { }; - }; - - magicant = mkDevice "magicant" { - icon = "${self}/files/topology-images/phone.png"; - info = "Samsung Z Flip 6"; - image = "${self}/files/topology-images/zflip6.png"; - interfaces.wifi = { }; - }; - - machpizza = mkDevice "machpizza" { - info = "MacBook Pro 2016"; - icon = "${self}/files/topology-images/mac.png"; - interfaces."eth1" = { }; - }; - - pc = mkDevice "Windows Gaming Server" { - info = "i7-4790k, GTX970, 32GB RAM"; - image = "${self}/files/topology-images/pc.png"; - interfaces.eth1 = { }; - }; - - pyramid.interfaces.eth1 = { }; - switch-bedroom = mkSwitch "Switch Bedroom" { info = "TL-SG1005D"; image = "${self}/files/topology-images/TL-SG1005D.png"; @@ -2592,6 +2651,37 @@ Another note concerning flake-parts: connections.eth3 = mkConnection "machpizza" "eth1"; }; + nswitch = mkDevice "Nintendo Switch" { + info = "Nintendo Switch"; + image = "${self}/files/topology-images/nintendo-switch.png"; + interfaces.eth1 = { }; + }; + + magicant = mkDevice "magicant" { + icon = "${self}/files/topology-images/phone.png"; + info = "Samsung Z Flip 6"; + image = "${self}/files/topology-images/zflip6.png"; + interfaces = { + wifi = { }; + fritz-wg = { }; + }; + }; + + machpizza = mkDevice "machpizza" { + info = "MacBook Pro 2016"; + icon = "${self}/files/topology-images/mac.png"; + interfaces = { + eth1 = { }; + wifi = { }; + }; + }; + + pc = mkDevice "Windows Gaming Server" { + info = "i7-4790k, GTX970, 32GB RAM"; + image = "${self}/files/topology-images/pc.png"; + interfaces.eth1 = { }; + }; + printer = mkDevice "Printer" { info = "DELL C2665dnf"; image = "${self}/files/topology-images/DELL-C2665dnf.png"; @@ -2622,7 +2712,7 @@ The devshell also provides a number of useful shorthand commands, as well as a '

    -Lastly, in the perSystem attribute set, we see that it is actually passed some values here. These expose to the scoped expressions the enclosed values, which makes it possible to still reference e.g. system and pkgs even when they are actually handled by flake-parts. +Aside from the default devShell which is the one that should usually be called interactively, I also define a deploy devshell: this one compiles nix-plugins against an earlier version of nix, which is needed so that the version nixos-anywhere that I am using works. However, that version is a little annoying since it had a bug in nix-plugins that is here fixed using a patch file. I guess it also serves as another fallback should problems with the current nix-plugins version arise.

    @@ -2869,7 +2959,11 @@ Otherwise, I define the function mkTemplates here which builds a na

    2.10. Formatter (treefmt-nix)

    -Defines a formatter that can be called using nix flake format. While a nice utility, I have stronger tools to perform this job. +Defines a formatter that can be called using nix fmt. +

    + +

    +Usually all formatting in this repo is done automatically while editing in emacs. However, it is nice to have a backup formatter to rely on and treefmt is extermely nice to work with, as it allows setting formatters for all kinds of aspects of the flake.

    @@ -2880,7 +2974,6 @@ Defines a formatter that can be called using nix flake format. Whil ]; perSystem = { pkgs, ... }: { - # formatter = pkgs.nixpkgs-fmt; # formatter is set by treefmt to: # formatter = lib.mkIf config.treefmt.flakeFormatter (lib.mkDefault config.treefmt.build.wrapper); treefmt = { @@ -2923,10 +3016,10 @@ Defines a formatter that can be called using nix flake format. Whil
    -

    2.11. TODO Modules

    +

    2.11. Modules

    -This exposes all of my modular configuration as modules. Other people can use them in their flake using imports = [ inputs.<name>.nixosModules ];. Per default, this enables some mechanisms like config sharing between nodes and the globals system. TODO: make it so that nothing is enabled upon initial import. +This exposes all of my modular configuration as modules. Other people can use them in their flake using imports = [ inputs.<name>.nixosModules ];. Per default, this enables some mechanisms like config sharing between nodes and the globals system.

    @@ -2934,7 +3027,7 @@ This exposes all of my modular configuration as modules. Other people can use th

    -homeModules on the other hand is not standardized in this way (for example, many flakes refere to homeManagerModules instead); in order not to unnecessarily break things, I leave it as is. +homeModules on the other hand is not standardized in this way (for example, many flakes refer to homeManagerModules instead); in order not to unnecessarily break things, I leave it as is.

    @@ -3011,7 +3104,7 @@ In this section I define packages that I manually add to nixpkgs, or that I want

    -As such, I also define three additional overlays: +As such, I also define three additional local overlays:

      @@ -3025,18 +3118,14 @@ These are simply mirrors of other branches of nixpkgs (mostly past stable branch

    -Also, this is where I define all of my own modules. These are mostly used for setting some host-specifics directly than opposed to through multiple options. -

    - -

    -Lastly, I add some of my own library functions to be used alongside the functions provided by nixpkgs and home-manager. +As part of the modifications, I add some of my own library functions to be used alongside the functions provided by nixpkgs and home-manager.

    On the structure of overlays: as you notice, all of the attributes within overlays are functions which take final and prev as arguments. This is a convention (sometimes you also see super instead of final) that aims to tell you that final represents the pkgs set after it has gone over all modifications, while prev is the pkgs set before the current modification.

      -
    • So, in additions, the final set is the same as in modifications, but their prev sets might differ (in this case, I believe they will be the same since all modifications are done at the same step). #TODO: fact check
    • +
    • So, in additions, the final set is the same as in modifications, but their prev sets might differ (in this case, I believe they will be the same since all modifications are done at the same step).
    • This starts to make a difference when you use multiple overlays and have one overlay depend on the modifications in another overlay.
    @@ -3147,20 +3236,20 @@ in
    -

    2.14. Installer iso

    +

    2.14. Installer images (iso, kexec)

    -This sections makes use of nixos-generators in order to easily allow me to build a live ISO of my installer system. It can be built using nix build --print-out-paths --no-link <flake path>#images.<target-system>.live-iso, and can then be copied to a USB drive using, for example, dd. +This sections makes use of nixos-generators in order to easily allow me to build a live ISO of my installer system. It can be built using nix build --print-out-paths --no-link <flake path>#live-iso, and can then be copied to a USB drive using, for example, dd. +

    + +

    +In a similar way, nix build --print-out-paths --no-link <flake-path>.#pnap-kexec --system <system> will build the kexec tarball that I need when using disko to deploy to a low-RAM systems.

    This is an improvement to what I did earlier, where I did not use nixos-generators but instead manually imported the needed modules to make this configration into a bootable USB image. Now, I can just write this in the same way that I would to write any other configuration.

    -
      -
    • perSystem is again a flake-parts construct.
    • -
    -
    { self, inputs, ... }:
     {
    @@ -3211,7 +3300,7 @@ When I define my configurations, I am actually defining two versions for each ac
     

    • One 'regular' config that should be used by all rebuild tools such as nixos-rebuild
    • -
    • One 'minimal' config that should be used by nixos=anywhere during initial deployment of a system
    • +
    • One 'minimal' config that should be used by nixos-anywhere during initial deployment of a system

    @@ -3229,7 +3318,7 @@ Hence, what I instead do is to define another output nixosConfigurationsMi

    {
    -  description = "Minimal installer flake - not to be used manually";
    +  description = "Minimal installer flake - automatically generated by SwarselSystems.org";
     
       inputs.swarsel.url = "./..";
     
    @@ -3511,6 +3600,14 @@ in
     
       ];
     
    +  topology.self = {
    +    interfaces = {
    +      "eth1" = { };
    +      "wifi" = { };
    +      "fritz-wg" = { };
    +    };
    +  };
    +
       swarselsystems = {
         lowResolution = "1280x800";
         highResolution = "2560x1600";
    @@ -3766,6 +3863,11 @@ in
     
       ];
     
    +  topology.self.interfaces = {
    +    "eth1" = { };
    +    "wifi" = { };
    +  };
    +
       swarselsystems = {
         isLaptop = true;
         isNixos = true;
    @@ -3987,25 +4089,27 @@ This is my main server that I run at home. It handles most tasks that require bi
     
    3.1.2.3.1. Main Configuration
    -
    { self, lib, minimal, ... }:
    +
    { self, lib, minimal, globals, ... }:
     {
     
       imports = [
         ./hardware-configuration.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
    +  topology.self.interfaces."eth1" = { };
    +
       boot = {
         loader.systemd-boot.enable = true;
         loader.efi.canTouchEfiVariables = true;
       };
     
    -  # globals.hosts.${config.node.name}.ipv4 = config.repo.secrets.local.ipv4;
    -  # globals.networks.home.hosts.${config.node.name} = {
    -  #   ipv4 = config.repo.secrets.local.home-ipv4;
    -  #   mac = config.repo.secrets.local.home-mac;
    -  # };
    +  networking.hosts = {
    +    ${globals.networks.home-lan.hosts.hintbooth.ipv4} = [ "server.hintbooth.${globals.domains.main}" ];
    +    ${globals.networks.home-lan.hosts.hintbooth.ipv6} = [ "server.hintbooth.${globals.domains.main}" ];
    +  };
     
       swarselsystems = {
         info = "ASRock J4105-ITX, 32GB RAM";
    @@ -4018,9 +4122,15 @@ This is my main server that I run at home. It handles most tasks that require bi
         isNixos = true;
         proxyHost = "twothreetunnel";
         server = {
    -      wireguard = {
    -        isClient = true;
    -        serverName = "twothreetunnel";
    +      wireguard.interfaces = {
    +        wgProxy = {
    +          isClient = true;
    +          serverName = "twothreetunnel";
    +        };
    +        wgHome = {
    +          isClient = true;
    +          serverName = "hintbooth";
    +        };
           };
           restic = {
             bucketName = "SwarselWinters";
    @@ -4053,37 +4163,36 @@ This is my main server that I run at home. It handles most tasks that require bi
     
       swarselmodules.server = {
         diskEncryption = lib.mkForce false;
    -    wireguard = lib.mkDefault true;
    -    nfs = lib.mkDefault true;
    -    nginx = lib.mkDefault true;
    -    kavita = lib.mkDefault true;
    -    restic = lib.mkDefault true;
    -    jellyfin = lib.mkDefault true;
    -    navidrome = lib.mkDefault true;
    -    spotifyd = lib.mkDefault true;
    -    mpd = lib.mkDefault true;
    -    postgresql = lib.mkDefault true;
    -    matrix = lib.mkDefault true;
    -    nextcloud = lib.mkDefault true;
    -    immich = lib.mkDefault true;
    -    paperless = lib.mkDefault true;
    -    transmission = lib.mkDefault true;
    -    syncthing = lib.mkDefault true;
    -    grafana = lib.mkDefault true;
    -    emacs = lib.mkDefault true;
    -    freshrss = lib.mkDefault true;
    -    jenkins = lib.mkDefault false;
    -    kanidm = lib.mkDefault true;
    -    firefly-iii = lib.mkDefault true;
    -    koillection = lib.mkDefault true;
    -    radicale = lib.mkDefault true;
    -    atuin = lib.mkDefault true;
    -    forgejo = lib.mkDefault true;
    -    ankisync = lib.mkDefault true;
    -    # snipeit = lib.mkDefault false;
    -    homebox = lib.mkDefault true;
    -    opkssh = lib.mkDefault true;
    -    garage = lib.mkDefault false;
    +    nginx = true; # for php stuff
    +    acme = false; # cert handled by proxy
    +    wireguard = true;
    +
    +    nfs = true;
    +    kavita = true;
    +    restic = true;
    +    jellyfin = true;
    +    navidrome = true;
    +    spotifyd = true;
    +    mpd = true;
    +    postgresql = true;
    +    matrix = true;
    +    nextcloud = true;
    +    immich = true;
    +    paperless = true;
    +    transmission = true;
    +    syncthing = true;
    +    grafana = true;
    +    emacs = true;
    +    freshrss = true;
    +    kanidm = true;
    +    firefly-iii = true;
    +    koillection = true;
    +    radicale = true;
    +    atuin = true;
    +    forgejo = true;
    +    ankisync = true;
    +    homebox = true;
    +    opkssh = true;
       };
     
     }
    @@ -4164,6 +4273,13 @@ This is my main server that I run at home. It handles most tasks that require bi
         "${self}/modules/nixos/optional/microvm-host.nix"
       ];
     
    +  topology.self = {
    +    interfaces = {
    +      "eth1" = { };
    +      "eth2" = { };
    +    };
    +  };
    +
       boot = {
         loader.systemd-boot.enable = true;
         loader.efi.canTouchEfiVariables = true;
    @@ -4192,10 +4308,6 @@ This is my main server that I run at home. It handles most tasks that require bi
         server = true;
       };
     
    -  swarselmodules.server = {
    -    nginx = lib.mkForce false;
    -  };
    -
       microvm.vms =
         let
           mkMicrovm = guestName: {
    @@ -4449,6 +4561,17 @@ in
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
       ];
     
    +  topology.self = {
    +    interfaces = {
    +      "eth1" = { };
    +      "eth2" = { };
    +      "eth3" = { };
    +      "eth4" = { };
    +      "eth5" = { };
    +      "eth6" = { };
    +    };
    +  };
    +
       swarselsystems = {
         info = "HUNSN RM02, 8GB RAM";
         flakePath = "/root/.dotfiles";
    @@ -4461,6 +4584,16 @@ in
         rootDisk = "/dev/sda";
         swapSize = "8G";
         networkKernelModules = [ "igb" ];
    +    server = {
    +      wireguard.interfaces = {
    +        wgHome = {
    +          isServer = true;
    +          peers = [
    +            "winters"
    +          ];
    +        };
    +      };
    +    };
       };
     
     } // lib.optionalAttrs (!minimal) {
    @@ -4472,7 +4605,7 @@ in
     
       swarselmodules = {
         server = {
    -      nginx = lib.mkForce false; # we get this from the server profile
    +      wireguard = true;
         };
       };
     
    @@ -4817,18 +4950,9 @@ in
         ./disk-config.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
    -  topology.self = {
    -    icon = "devices.cloud-server";
    -    interfaces.wg = {
    -      addresses = [ "192.168.3.4" ];
    -      renderer.hidePhysicalConnections = true;
    -      virtual = true;
    -      type = "wireguard";
    -    };
    -  };
    -
       system.stateVersion = "23.11";
     
       services.syncthing = {
    @@ -4894,9 +5018,11 @@ in
         isCloud = true;
         proxyHost = "twothreetunnel";
         server = {
    -      wireguard = {
    -        isClient = true;
    -        serverName = "twothreetunnel";
    +      wireguard.interfaces = {
    +        wgProxy = {
    +          isClient = true;
    +          serverName = "twothreetunnel";
    +        };
           };
           restic = {
             bucketName = "SwarselMoonside";
    @@ -5104,6 +5230,7 @@ in
         ./disk-config.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
       node.lockFromBootstrapping = lib.mkForce false;
    @@ -5111,7 +5238,6 @@ in
       topology.self = {
         icon = "devices.cloud-server";
       };
    -  swarselmodules.server.nginx = false;
     
       swarselsystems = {
         flakePath = "/root/.dotfiles";
    @@ -5127,9 +5253,11 @@ in
         isCloud = true;
         proxyHost = "twothreetunnel";
         server = {
    -      wireguard = {
    -        isClient = true;
    -        serverName = "twothreetunnel";
    +      wireguard.interfaces = {
    +        wgProxy = {
    +          isClient = true;
    +          serverName = "twothreetunnel";
    +        };
           };
           garage = {
             data_dir = {
    @@ -5158,7 +5286,7 @@ in
         postgresql = true;
         attic = true;
         garage = true;
    -    hydra = true;
    +    hydra = false;
         dns-hostrecord = true;
       };
     
    @@ -5322,13 +5450,13 @@ in
     
    -
    -
    3.1.3.3. Stoicclub (OCI)
    -
    +
    +
    3.1.3.3. Stoicclub (OCI)
    +
    -
    -
    3.1.3.3.1. Main Configuration
    -
    +
    +
    3.1.3.3.1. Main Configuration
    +
    { self, lib, minimal, ... }:
     {
    @@ -5342,8 +5470,6 @@ in
       topology.self = {
         icon = "devices.cloud-server";
       };
    -  swarselmodules.server.nginx = false;
    -
     
       swarselsystems = {
         flakePath = "/root/.dotfiles";
    @@ -5366,7 +5492,6 @@ in
     
       swarselmodules.server = {
         nsd = true;
    -    nginx = false;
         dns-hostrecord = true;
       };
     }
    @@ -5375,9 +5500,9 @@ in
     
    -
    -
    3.1.3.3.2. hardware-configuration
    -
    +
    +
    3.1.3.3.2. hardware-configuration
    +
    { lib, modulesPath, ... }:
     {
    @@ -5398,9 +5523,9 @@ in
     
    -
    -
    3.1.3.3.3. disko
    -
    +
    +
    3.1.3.3.3. disko
    +
    { lib, pkgs, config, ... }:
     let
    @@ -5529,13 +5654,13 @@ in
     
    -
    -
    3.1.3.4. Liliputsteps (OCI)
    -
    +
    +
    3.1.3.4. Liliputsteps (OCI)
    +
    -
    -
    3.1.3.4.1. Main Configuration
    -
    +
    +
    3.1.3.4.1. Main Configuration
    +
    { self, lib, minimal, ... }:
     {
    @@ -5544,6 +5669,7 @@ in
         ./disk-config.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
       topology.self = {
    @@ -5570,7 +5696,6 @@ in
       };
     
       swarselmodules.server = {
    -    nginx = false;
         bastion = true;
         dns-hostrecord = true;
         # ssh = false;
    @@ -5584,9 +5709,9 @@ in
     
    -
    -
    3.1.3.4.2. hardware-configuration
    -
    +
    +
    3.1.3.4.2. hardware-configuration
    +
    { lib, modulesPath, ... }:
     {
    @@ -5607,9 +5732,9 @@ in
     
    -
    -
    3.1.3.4.3. disko
    -
    +
    +
    3.1.3.4.3. disko
    +
    { lib, pkgs, config, ... }:
     let
    @@ -5738,13 +5863,13 @@ in
     
    -
    -
    3.1.3.5. Twothreetunnel (OCI)
    -
    +
    +
    3.1.3.5. Twothreetunnel (OCI)
    +
    -
    -
    3.1.3.5.1. Main Configuration
    -
    +
    +
    3.1.3.5.1. Main Configuration
    +
    { self, lib, minimal, ... }:
     {
    @@ -5753,6 +5878,7 @@ in
         ./disk-config.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
       topology.self = {
    @@ -5772,15 +5898,17 @@ in
         isLinux = true;
         isCloud = true;
         server = {
    -      wireguard = {
    -        ifName = "wg";
    -        isServer = true;
    -        peers = [
    -          "moonside"
    -          "winters"
    -          "belchsfactory"
    -          "eagleland"
    -        ];
    +      wireguard.interfaces = {
    +        wgProxy = {
    +          # ifName = "wg";
    +          isServer = true;
    +          peers = [
    +            "moonside"
    +            "winters"
    +            "belchsfactory"
    +            "eagleland"
    +          ];
    +        };
           };
         };
       };
    @@ -5790,8 +5918,8 @@ in
       };
     
       swarselmodules.server = {
    -    nginx = true; # for now
    -    oauth2-proxy = true; # for now
    +    nginx = true;
    +    oauth2-proxy = true;
         dns-hostrecord = true;
         wireguard = true;
       };
    @@ -5802,9 +5930,9 @@ in
     
    -
    -
    3.1.3.5.2. hardware-configuration
    -
    +
    +
    3.1.3.5.2. hardware-configuration
    +
    { lib, modulesPath, ... }:
     {
    @@ -5825,9 +5953,9 @@ in
     
    -
    -
    3.1.3.5.3. disko
    -
    +
    +
    3.1.3.5.3. disko
    +
    { lib, pkgs, config, ... }:
     let
    @@ -5960,9 +6088,13 @@ in
     
    3.1.3.6. Eagleland (Hetzner)
    -
    -
    3.1.3.6.1. Main Configuration
    -
    +
    +
    3.1.3.6.1. Main Configuration
    +
    +

    +:CUSTOMID: h:96540b9c-1610-45f2-ba19-916051ab5e10 +

    +
    { self, lib, minimal, ... }:
     {
    @@ -5971,6 +6103,7 @@ in
         ./disk-config.nix
     
         "${self}/modules/nixos/optional/systemd-networkd-server.nix"
    +    "${self}/modules/nixos/optional/nix-topology-self.nix"
       ];
     
       topology.self = {
    @@ -5991,7 +6124,15 @@ in
         isBtrfs = true;
         isNixos = true;
         isLinux = true;
    -    proxyHost = "eagleland";
    +    proxyHost = "twothreetunnel"; # mail shall not be proxied through twothreetunnel
    +    server = {
    +      wireguard.interfaces = {
    +        wgProxy = {
    +          isClient = true;
    +          serverName = "twothreetunnel";
    +        };
    +      };
    +    };
       };
     } // lib.optionalAttrs (!minimal) {
     
    @@ -5999,6 +6140,8 @@ in
         mailserver = true;
         dns-hostrecord = true;
         postgresql = true;
    +    nginx = true;
    +    wireguard = true;
       };
     
       swarselprofiles = {
    @@ -6192,6 +6335,8 @@ This is a slim setup for developing base configuration. I do not track the hardw
         ./hardware-configuration.nix
       ];
     
    +  topology.self.interfaces."bootstrapper" = { };
    +
       networking = {
         hostName = "toto";
         firewall.enable = false;
    @@ -6694,6 +6839,8 @@ in
         WLR_RENDERER_ALLOW_SOFTWARE = 1;
       };
     
    +  topology.self.interfaces."demo host" = { };
    +
       services.qemuGuest.enable = true;
     
       boot = {
    @@ -7236,7 +7383,13 @@ in
                   description = "List of external dns nameservers";
                 };
               };
    +
    +
    +
             };
    +
    +
    +
           };
         };
     
    @@ -7671,15 +7824,51 @@ Setup timezone and locale. I want to use the US layout, but have the rest adapte
     
    -
    3.2.1.11. PII management
    +
    3.2.1.11. TODO PII management

    This is also exposed to home-manager configurations, in case this ever breaks, I can also go back to importing nixosConfig as an attribute in the input attribute set and call the secrets using nixosConfig.repo.secrets.

    +

    +Two modes of operation are supported: +

    +
      +
    • loading in a secret as a plain attribute set {a = 3;}
    • +
    • loading in a function { nodes, ...}: {mac = nodes.xxx.interface.mac;}
    • +
    + +

    +Both cases should return the proper values - in the second case the parent set must be passed to the inherit under options.repo.secrets. +

    + +

    +In general, there are three types of pii file: +

    +
      +
    • Per-node secrets: found under the nodes secretDir as pii.nix.enc (exposed as config.repo.secrets.local)
    • +
    • Common secrets: found in secrets/repo/pii.nix.enc (exposed as config.repo.secrets.common)
    • +
    • Global definitions of networks and domains: found in secrets/repo/globals.nix.enc (not exposed to nodes, but only loaded in in Globals)
    • +
    + +

    +This system, while highly pleasant to work with during everyday use, sometimes has quirks: +

    +
      +
    • nixos-rebuild cannot be used +
        +
      • this is because we need to call nix build in a separate step where sops-decrypt-and-cache will be cached. Once we have a finished build we can switch to that (all of this is handled by swarsel-deploy)
      • +
      • this is a bit cumbersome for hosts that are not supported by swarsel-deploy (currently this is mostly home-manager only configurations). In principal, building their config locally should work without issue, however, sometimes the decrypt step hiccups. In that case I usually resort to scp'ing the decrypted secrets to the host in question using justfile's just secrets command. After that, the secrets in /var/tmp/nix-import-encrypted need to be moved to the correct dir depending on the build user uid (0/root or 1000/swarsel). After that I delete the cached secrets again. TODO: fix this behaviour.
      • +
    • +
    • the used nix version needs to be kept in sync with the version of nix that nix-plugins is compiled against +
        +
      • currently, this mostly poses an issue when provisioning new hosts - the version of nixos-anywhere that I am using uses nix 2.28.x, so I wrote a dedicated Devshell (checks) (called deploy) that is used to set this environment up. This devshell is automatically used by the justfile command just bootstrap.
      • +
    • +
    +
    # largely based on https://github.com/oddlama/nix-config/blob/main/modules/secrets.nix
    -{ config, inputs, lib, ... }:
    +{ config, inputs, lib, nodes, ... }:
     let
       # If the given expression is a bare set, it will be wrapped in a function,
       # so that the imported file can always be applied to the inputs, similar to
    @@ -7733,7 +7922,7 @@ in
     
           secrets = lib.mkOption {
             readOnly = true;
    -        default = lib.mapAttrs (_: x: importEncrypted x inputs) config.repo.secretFiles;
    +        default = lib.mapAttrs (_: x: importEncrypted x { inherit lib nodes inputs; }) config.repo.secretFiles;
             type = lib.types.unspecified;
             description = "Exposes the loaded repo secrets. This option is read-only.";
           };
    @@ -8636,9 +8825,9 @@ I use sops-nix to handle secrets that I want to have available on my machines at
     
    -
    -
    3.2.2.11. Remote building
    -
    +
    +
    3.2.2.11. Remote building
    +
    { lib, config, globals, ... }:
     let
    @@ -9788,7 +9977,7 @@ in
     
    -
    3.2.3.3. System Packages
    +
    3.2.3.3. System Packages (Server Programs)
    { lib, config, pkgs, ... }:
    @@ -9804,9 +9993,11 @@ in
           emacs
           vim
           sops
    -      swarsel-deploy
           tmux
           busybox
    +      swarsel-deploy
    +      swarsel-gens
    +      swarsel-switch
         ];
       };
     }
    @@ -9877,14 +10068,65 @@ in
     
    -
    -
    3.2.3.5. NGINX
    -
    +
    +
    3.2.3.5. acme
    +
    -
    { pkgs, lib, config, globals, ... }:
    +
    { self, pkgs, lib, config, globals, ... }:
     let
       inherit (config.repo.secrets.common) dnsProvider dnsBase dnsMail;
     
    +  sopsFile = self + "/secrets/nginx/acme.json";
    +in
    +{
    +  options.swarselmodules.server.acme = lib.mkEnableOption "enable acme on server";
    +  config = lib.mkIf config.swarselmodules.server.acme {
    +    environment.systemPackages = with pkgs; [
    +      lego
    +    ];
    +
    +    sops = {
    +      secrets = {
    +        acme-creds = { format = "json"; key = ""; group = "acme"; inherit sopsFile; mode = "0660"; };
    +      };
    +      templates."certs.secret".content = ''
    +        ACME_DNS_API_BASE = ${dnsBase}
    +        ACME_DNS_STORAGE_PATH=${config.sops.secrets.acme-creds.path}
    +      '';
    +    };
    +
    +    users.groups.acme.members = lib.mkIf config.swarselmodules.server.nginx [ "nginx" ];
    +
    +    security.acme = {
    +      acceptTerms = true;
    +      defaults = {
    +        inherit dnsProvider;
    +        email = dnsMail;
    +        environmentFile = "${config.sops.templates."certs.secret".path}";
    +        reloadServices = [ "nginx" ];
    +        dnsPropagationCheck = true;
    +      };
    +      certs."${globals.domains.main}" = {
    +        domain = "*.${globals.domains.main}";
    +      };
    +    };
    +
    +    environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
    +      directories = [{ directory = "/var/lib/acme"; }];
    +    };
    +
    +  };
    +}
    +
    +
    +
    +
    +
    +
    3.2.3.6. NGINX
    +
    +
    +
    { pkgs, lib, config, ... }:
    +let
       serviceUser = "nginx";
       serviceGroup = serviceUser;
     
    @@ -9964,40 +10206,12 @@ in
         };
       };
       config = lib.mkIf config.swarselmodules.server.nginx {
    -    environment.systemPackages = with pkgs; [
    -      lego
    -    ];
     
    -    sops = lib.mkIf (config.node.name == config.swarselsystems.proxyHost) {
    -      secrets = {
    -        acme-creds = { format = "json"; key = ""; group = "acme"; sopsFile = config.node.secretsDir + "/acme.json"; mode = "0660"; };
    -      };
    -      templates."certs.secret".content = ''
    -        ACME_DNS_API_BASE = ${dnsBase}
    -        ACME_DNS_STORAGE_PATH=${config.sops.secrets.acme-creds.path}
    -      '';
    -    };
    -
    -    users.groups.acme.members = [ "nginx" ];
    -
    -    security.acme = lib.mkIf (config.node.name == config.swarselsystems.proxyHost) {
    -      acceptTerms = true;
    -      defaults = {
    -        inherit dnsProvider;
    -        email = dnsMail;
    -        environmentFile = "${config.sops.templates."certs.secret".path}";
    -        reloadServices = [ "nginx" ];
    -        dnsPropagationCheck = true;
    -      };
    -      certs."${globals.domains.main}" = {
    -        domain = "*.${globals.domains.main}";
    -      };
    -    };
    +    swarselmodules.server.acme = lib.mkDefault true;
     
         networking.firewall.allowedTCPPorts = [ 80 443 ];
     
         environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
    -      directories = [{ directory = "/var/lib/acme"; }];
           files = [ dhParamsPathBase ];
         };
     
    @@ -10076,7 +10290,7 @@ in
     
    -
    3.2.3.6. ssh
    +
    3.2.3.7. ssh

    Here I am forcing startWhenNeeded to false so that the value will not be set to true in containers = this would be a problem because it would delay ssh host key generation. @@ -10125,9 +10339,9 @@ Here I am forcing startWhenNeeded to false so that the value will n

    -
    -
    3.2.3.7. Bastion
    -
    +
    +
    3.2.3.8. Bastion
    +
    { self, lib, config, ... }:
     {
    @@ -10200,9 +10414,9 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
    -
    -
    3.2.3.8. ssh builder config
    -
    +
    +
    3.2.3.9. ssh builder config
    +

    Restricts access to the system by the nix build user as per https://discourse.nixos.org/t/wrapper-to-restrict-builder-access-through-ssh-worth-upstreaming/25834.

    @@ -10256,7 +10470,7 @@ in
    -
    3.2.3.9. Network settings
    +
    3.2.3.10. Network settings

    Generate hostId using head -c4 /dev/urandom | od -A none -t x4 @@ -10332,7 +10546,7 @@ in

    -
    3.2.3.10. Disk encryption
    +
    3.2.3.11. Disk encryption

    The hostkey can be generated with ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key. @@ -10348,75 +10562,11 @@ Use lspci -nn | grep -i network to find out manufacturer info:

    - +
    +04:00.0 Network controller [0280]: MEDIATEK Corp. MT7922 802.11ax PCI Express Wireless Network Adapter [14c3:0616]
    +
    --- -- -- -- -- -- -- -- -- -- -- -- -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    04:00.0Networkcontroller[0280]:MEDIATEKCorp.MT7922802.11axPCIExpressWirelessNetworkAdapter[14c3:0616]
    6a:00.0Ethernetcontroller[0200]:IntelCorporationI210GigabitNetworkConnection[8086:1533](rev03) 
    -

    From the last bracket you then find out the correct kernel module:

    @@ -10517,6 +10667,18 @@ From the last bracket you then find out the correct kernel module: +

    +A little note about the secrets part: +

    + +

    +systemd-initrd provides a lightweight SSH server in form of dropbear - in order to not crash we need to have a hostkey ready in the initrd. I achieve this by generating a hostkey in the build process in casy I am doing an initial install (another way - and safer - would be to push that in swarsel-bootstrap I guess) - this results in the hostkey landing in the nix store. However, I only ever spend like 5 minutes in this state before I rebuild to the full system, where this hostkey is no longer used. This is because upon first activation, we will then run another ssh-keygen that will then write to persisted storage. All "unlock" SSH hosts are to be reached over port 2222, and systemctl default will be run immediately upon login, which will guide towards attaining a working system (in normal operation, it will simply ask for the password). +

    + +

    +I also take some precautions in how I get networking information during stage 1. For the most part, I just use systemd-networkd (server), however, for hosts in my local network, I take another step to define the network in the kernelParams, to make extra sure I can reach it. +

    +
    { self, pkgs, lib, config, globals, minimal, ... }:
     let
    @@ -10530,6 +10692,11 @@ let
           "/persist/${hostKeyPathBase}"
         else
           "${hostKeyPathBase}";
    +
    +  # this key is only used only for ssh to stage 1 in initial provisioning (in nix store)
    +  generatedHostKey = pkgs.runCommand "initrd-hostkey" { } ''
    +  ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f $out
    +  '';
     in
     {
       options.swarselmodules.server.diskEncryption = lib.mkEnableOption "enable disk encryption config";
    @@ -10540,15 +10707,15 @@ in
       config = lib.mkIf (config.swarselmodules.server.diskEncryption && config.swarselsystems.isCrypted) {
     
     
    -    system.activationScripts."createPersistentStorageDirs" = lib.mkIf config.swarselsystems.isImpermanence {
    -      deps = [ "ensureInitrdHostkey" ];
    -    };
    -    system.activationScripts.ensureInitrdHostkey = lib.mkIf (config.swarselprofiles.server || minimal) {
    +    # as soon as we hit a stable system, we will use a persisted key
    +    # @future me: dont mkIf this to minimal, we need to create this as soon as possible
    +    system.activationScripts.ensureInitrdHostkey = {
           text = ''
             [[ -e ${hostKeyPath} ]] || ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f ${hostKeyPath}
           '';
           deps = [
    -        "etc"
    +        "users"
    +        "createPersistentStorageDirs"
           ];
         };
     
    @@ -10561,7 +10728,7 @@ in
             "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none"
           ];
           initrd = {
    -        secrets."${hostKeyPathBase}" = lib.mkIf (!minimal) hostKeyPathBase;
    +        secrets."/tmp${hostKeyPathBase}" = if minimal then (lib.mkForce generatedHostKey) else (lib.mkForce hostKeyPath); # need to mkForce this or it behaves stateful
             availableKernelModules = config.swarselsystems.networkKernelModules;
             network = {
               enable = true;
    @@ -10573,34 +10740,13 @@ in
                   ''command="/bin/systemctl default" ${builtins.readFile "${self}/secrets/public/ssh/yubikey.pub"}''
                   ''command="/bin/systemctl default" ${builtins.readFile "${self}/secrets/public/ssh/magicant.pub"}''
                 ];
    -            hostKeys = [ hostKeyPathBase ];
    +            hostKeys = [ "/tmp${hostKeyPathBase}" ]; # use a tmp file otherwise persist mount will be unhappy
               };
    -          # postCommands = ''
    -          #   echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1' >> /root/.profile
    -          # '';
             };
             systemd = {
               initrdBin = with pkgs; [
                 cryptsetup
               ];
    -          # NOTE: the below does put the text into /root/.profile, but the command will not be run
    -          # services = {
    -          #   unlock-luks = {
    -          #     wantedBy = [ "initrd.target" ];
    -          #     after = [ "network.target" ];
    -          #     before = [ "systemd-cryptsetup@cryptroot.service" ];
    -          #     path = [ "/bin" ];
    -
    -          #     serviceConfig = {
    -          #       Type = "oneshot";
    -          #       RemainAfterExit = true;
    -          #     };
    -
    -          #     script = ''
    -          #       echo "systemctl default" >> /root/.profile
    -          #     '';
    -          #   };
    -          # };
             };
           };
         };
    @@ -10611,192 +10757,324 @@ in
     
    -
    -
    3.2.3.11. Wireguard
    -
    -
    -
    { self, lib, pkgs, config, confLib, nodes, globals, ... }:
    -let
    -  wgInterface = "wg0";
    -  inherit (confLib.gen { name = "wireguard"; port = 52829; user = "systemd-network"; group = "systemd-network"; }) servicePort serviceName serviceUser serviceGroup;
    +
    +
    3.2.3.12. Attic setup
    +
    +

    +By default, attic only provides a cli client to authenticate to caches. I want all my servers to use my main binary cache, which is what I set up here. +

    + +
    +
    { lib, config, pkgs, globals, ... }:
     
    -  inherit (config.swarselsystems) sopsFile;
    -  wgSopsFile = self + "/secrets/repo/wg.yaml";
    -  inherit (config.swarselsystems.server.wireguard) peers isClient isServer serverName serverNetConfigPrefix ifName;
    -in
     {
    -  options = {
    -    swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} settings";
    -    swarselsystems.server.wireguard = {
    -      isServer = lib.mkEnableOption "set this as a wireguard server";
    -      isClient = lib.mkEnableOption "set this as a wireguard client";
    -      serverName = lib.mkOption {
    -        type = lib.types.str;
    -        default = "";
    +  options.swarselmodules.server.attic-setup = lib.mkEnableOption "enable attic setup";
    +  config = lib.mkIf config.swarselmodules.server.attic-setup {
    +
    +    environment.systemPackages = with pkgs; [
    +      attic-client
    +    ];
    +
    +    sops = {
    +      secrets = {
    +        attic-cache-key = { };
           };
    -      serverNetConfigPrefix = lib.mkOption {
    -        type = lib.types.str;
    -        default = "${if nodes.${serverName}.config.swarselsystems.isCloud then nodes.${serverName}.config.node.name else "home"}";
    -        readOnly = true;
    -      };
    -      ifName = lib.mkOption {
    -        type = lib.types.str;
    -        default = wgInterface;
    -      };
    -      peers = lib.mkOption {
    -        type = lib.types.listOf lib.types.str;
    -        default = [ ];
    -        description = "Wireguard peer config names";
    +      templates = {
    +        "attic-env".content = ''
    +          DOMAIN=https://${globals.services.attic.domain}
    +          TOKEN=${config.sops.placeholder.attic-cache-key}
    +        '';
           };
         };
     
    +    systemd.services.attic-cache-setup = {
    +      description = "Ensure attic is authenticated to cache";
    +      wantedBy = [ "multi-user.target" ];
    +      after = [ "network-online.target" ];
    +      wants = [ "network-online.target" ];
    +
    +      serviceConfig = {
    +        Type = "oneshot";
    +        EnvironmentFile = [
    +          config.sops.templates.attic-env.path
    +        ];
    +      };
    +      script = let
    +        attic = lib.getExe pkgs.attic-client;
    +      in ''
    +        set -eu
    +        if ${attic} cache info ${config.swarselsystems.mainUser} >/dev/null 2>&1; then
    +          echo "cache already authenticated"
    +          exit 0
    +        fi
    +        echo "cache not authenticated, attempting login..."
    +        ${attic} login ${config.swarselsystems.mainUser} "$DOMAIN" "$TOKEN" --set-default
    +        ${attic} use ${config.swarselsystems.mainUser}
    +      '';
    +
    +    };
    +
       };
    +
    +}
    +
    +
    +
    +
    +
    +
    3.2.3.13. TODO Wireguard
    +
    +

    +I use wireguard for two things: +

    +
      +
    • proxying of my services (both internal and external) to my proxy node
    • +
    • proxying of my internal services to an internal NGINX in order to save on bandwidth
    • +
    + +

    +At current, this means that I am running two wireguard interfaces - the following configuration allows me to define an arbitrary number of wireguard interfaces that each host can be part of (either as "client" or "server"). All of these connections are really point-to-point. On the client side, I set persistentKeepalive unconditionally, which is lazy (and a bit of a security issue). Also, I noticed that I lose 12 bits of MTU somewhere - I would have expected to be able to set MTU 1420, but that does not seem to be the case. TODO:fix both +

    + +

    +In order to define a new wireguard interface, I have to: +

    + +
      +
    • add another <nodeName>-<wgInterfaceName> network to globals +
        +
      • add its members under hosts
      • +
      • add the wgInterfaceName together with id to the server nodes local pii
      • +
      • make sure all members have their private keys in their secrets file and their public key under secrets/public/wg
      • +
      • make sure that all preshared keys exist in secrets/repo/wg.yaml TODO: maybe split wg.yaml into per-interface files with finer-grained acl
      • +
    • +
    + +
    +
    { self, lib, pkgs, config, confLib, nodes, globals, ... }:
    +let
    +  inherit (confLib.gen {
    +    name = "wireguard";
    +    port = 52829;
    +    user = "systemd-network";
    +    group = "systemd-network";
    +  }) servicePort serviceName serviceUser serviceGroup;
    +
    +  inherit (config.swarselsystems) sopsFile;
    +  wgSopsFile = self + "/secrets/repo/wg.yaml";
    +
    +  cfg = config.swarselsystems.server.wireguard;
    +  inherit (cfg) interfaces;
    +  ifaceList = builtins.attrValues interfaces;
    +in
    +{
    +  options = {
    +    swarselmodules.server.${serviceName} =
    +      lib.mkEnableOption "enable ${serviceName} settings";
    +
    +    swarselsystems.server.wireguard = {
    +      interfaces = lib.mkOption {
    +        type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: {
    +          options = {
    +            isServer = lib.mkEnableOption "set this interface as a wireguard server";
    +            isClient = lib.mkEnableOption "set this interface as a wireguard client";
    +
    +            serverName = lib.mkOption {
    +              type = lib.types.str;
    +              default = "";
    +              description = "Hostname of the WireGuard server this interface connects to (when isClient = true).";
    +            };
    +
    +            serverNetConfigPrefix = lib.mkOption {
    +              type = lib.types.str;
    +              default =
    +                let
    +                  serverCfg = nodes.${config.serverName}.config;
    +                in
    +                if serverCfg.swarselsystems.isCloud
    +                then serverCfg.node.name
    +                else "home";
    +              readOnly = true;
    +              description = "Prefix used to look up the server network in globals.networks.\"<prefix>-wg\".";
    +            };
    +
    +            ifName = lib.mkOption {
    +              type = lib.types.str;
    +              default = name;
    +              description = "Name of the WireGuard interface.";
    +            };
    +
    +            peers = lib.mkOption {
    +              type = lib.types.listOf lib.types.str;
    +              default = [ ];
    +              description = "WireGuard peer config names (clients when this host is server, or additional peers).";
    +            };
    +          };
    +        }));
    +        default = { };
    +        description = "WireGuard interfaces defined on this host.";
    +      };
    +    };
    +  };
    +
       config = lib.mkIf config.swarselmodules.server.${serviceName} {
     
         environment.systemPackages = with pkgs; [
           wireguard-tools
         ];
     
    -    sops = {
    -      secrets = {
    -        wireguard-private-key = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
    -        # create this secret only if this is a simple client with only one peer (the server)
    -        "wireguard-${serverName}-${config.node.name}-presharedKey" = lib.mkIf (isClient && peers == [ ]) { sopsFile = wgSopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
    -      }
    -      # create these secrets only if this host has multiple peers
    -      // lib.optionalAttrs (peers != [ ]) (builtins.listToAttrs (map
    -        (clientName: {
    -          name = "wireguard-${config.node.name}-${clientName}-presharedKey";
    -          value = { sopsFile = wgSopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
    -        })
    -        peers));
    -    };
    +    sops.secrets =
    +      lib.mkMerge (
    +        [
    +          {
    +            # shared host private key
    +            wireguard-private-key = {
    +              inherit sopsFile;
    +              owner = serviceUser;
    +              group = serviceGroup;
    +              mode = "0600";
    +            };
    +          }
    +        ] ++ (map
    +          (i:
    +            let
    +              simpleClientSecrets =
    +                lib.optionalAttrs (i.isClient && i.peers == [ ]) {
    +                  "wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey" = {
    +                    sopsFile = wgSopsFile;
    +                    owner = serviceUser;
    +                    group = serviceGroup;
    +                    mode = "0600";
    +                  };
    +                };
    +
    +              multiPeerSecrets =
    +                lib.optionalAttrs (i.peers != [ ]) (builtins.listToAttrs (map
    +                  (clientName: {
    +                    name = "wireguard-${config.node.name}-${clientName}-${i.ifName}-presharedKey";
    +                    value = {
    +                      sopsFile = wgSopsFile;
    +                      owner = serviceUser;
    +                      group = serviceGroup;
    +                      mode = "0600";
    +                    };
    +                  })
    +                  i.peers));
    +            in
    +            simpleClientSecrets // multiPeerSecrets
    +          )
    +          ifaceList)
    +      );
     
         networking = {
    -      firewall.checkReversePath = lib.mkIf isClient "loose";
    -      firewall.allowedUDPPorts = [ servicePort ];
    -      # nat = lib.mkIf (config.swarselsystems.isCloud && isServer) {
    -      #   enable = true;
    -      #   enableIPv6 = true;
    -      #   externalInterface = "enp0s6";
    -      #   internalInterfaces = [ ifName ];
    -      # };
    -      # interfaces.${ifName}.mtu = 1280; # the default (1420) is not enough!
    +      firewall.checkReversePath =
    +        lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose";
    +
    +      firewall.allowedUDPPorts =
    +        lib.mkIf (lib.any (i: i.isServer) ifaceList) [ servicePort ];
         };
     
         systemd.network = {
           enable = true;
     
    -      networks."50-${ifName}" = {
    -        matchConfig.Name = ifName;
    -        linkConfig = {
    -          MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???)
    -        };
    -
    -        # networkConfig = lib.mkIf (config.swarselsystems.isCloud && isServer) {
    -        #   IPv4Forwarding = true;
    -        #   IPv6Forwarding = true;
    -        # };
    -
    -        address = if isServer then [
    -          globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".hosts.${config.node.name}.cidrv4
    -          globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".hosts.${config.node.name}.cidrv6
    -        ] else [
    -          globals.networks."${serverNetConfigPrefix}-wg".hosts.${config.node.name}.cidrv4
    -          globals.networks."${serverNetConfigPrefix}-wg".hosts.${config.node.name}.cidrv6
    -        ];
    -      };
    -
    -      netdevs."50-${ifName}" = {
    -        netdevConfig = {
    -          Kind = "wireguard";
    -          Name = ifName;
    -        };
    -
    -        wireguardConfig = {
    -          ListenPort = lib.mkIf isServer servicePort;
    -
    -          # ensure file is readable by `systemd-network` user
    -          PrivateKeyFile = config.sops.secrets.wireguard-private-key.path;
    -
    -          # To automatically create routes for everything in AllowedIPs,
    -          # add RouteTable=main
    -          RouteTable = lib.mkIf isClient "main";
    -
    -          # FirewallMark marks all packets send and received by wg0
    -          # with the number 42, which can be used to define policy rules on these packets.
    -          # FirewallMark = 42;
    -        };
    -        wireguardPeers = lib.optionals isClient [
    +      networks = lib.mkMerge (map
    +        (i:
    +          let
    +            inherit (i) ifName;
    +          in
               {
    -            PublicKey = builtins.readFile "${self}/secrets/public/wg/${serverName}.pub";
    -            PresharedKeyFile = config.sops.secrets."wireguard-${serverName}-${config.node.name}-presharedKey".path;
    -            Endpoint = "server.${serverName}.${globals.domains.main}:${toString servicePort}";
    -            # Access to the whole network is routed through our entry node.
    -            PersistentKeepalive = 25;
    -            AllowedIPs =
    -              let
    -                wgNetwork = globals.networks."${serverNetConfigPrefix}-wg";
    -              in
    -                (lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4)
    -                   ++ (lib.optional (wgNetwork.cidrv6 != null) wgNetwork.cidrv6);
    -          }
    -        ] ++ lib.optionals isServer (map
    -          (clientName: {
    -            PublicKey = builtins.readFile "${self}/secrets/public/wg/${clientName}.pub";
    -            PresharedKeyFile = config.sops.secrets."wireguard-${config.node.name}-${clientName}-presharedKey".path;
    -            # PersistentKeepalive = 25;
    -            AllowedIPs =
    -              let
    -                clientInWgNetwork = globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".hosts.${clientName};
    -              in
    -                (lib.optional (clientInWgNetwork.ipv4 != null) (lib.net.cidr.make 32 clientInWgNetwork.ipv4))
    -                ++ (lib.optional (clientInWgNetwork.ipv6 != null) (lib.net.cidr.make 128 clientInWgNetwork.ipv6));
    +            "50-${ifName}" = {
    +              matchConfig.Name = ifName;
    +              linkConfig = {
    +                MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???)
    +              };
    +
    +              address =
    +                if i.isServer then [
    +                  globals.networks."${config.swarselsystems.server.netConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4
    +                  globals.networks."${config.swarselsystems.server.netConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6
    +                ] else [
    +                  globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4
    +                  globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6
    +                ];
    +            };
               })
    -          peers);
    +        ifaceList);
     
    -      };
    +      netdevs = lib.mkMerge (map
    +        (i:
    +          let
    +            inherit (i) ifName;
    +          in
    +          {
    +            "50-${ifName}" = {
    +              netdevConfig = {
    +                Kind = "wireguard";
    +                Name = ifName;
    +              };
    +
    +              wireguardConfig = {
    +                ListenPort = lib.mkIf i.isServer servicePort;
    +
    +                PrivateKeyFile = config.sops.secrets.wireguard-private-key.path;
    +
    +                RouteTable = lib.mkIf i.isClient "main";
    +              };
    +
    +              wireguardPeers =
    +                lib.optionals i.isClient [
    +                  {
    +                    PublicKey =
    +                      builtins.readFile "${self}/secrets/public/wg/${i.serverName}.pub";
    +
    +                    PresharedKeyFile =
    +                      config.sops.secrets."wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey".path;
    +
    +                    Endpoint =
    +                      "server.${i.serverName}.${globals.domains.main}:${toString servicePort}";
    +
    +                    PersistentKeepalive = 25;
    +
    +                    AllowedIPs =
    +                      let
    +                        wgNetwork = globals.networks."${i.serverNetConfigPrefix}-${i.ifName}";
    +                      in
    +                      (lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4)
    +                      ++ (lib.optional (wgNetwork.cidrv6 != null) wgNetwork.cidrv6);
    +                  }
    +                ]
    +                ++ lib.optionals i.isServer (map
    +                  (clientName: {
    +                    PublicKey =
    +                      builtins.readFile "${self}/secrets/public/wg/${clientName}.pub";
    +
    +                    PresharedKeyFile =
    +                      config.sops.secrets."wireguard-${config.node.name}-${clientName}-${i.ifName}-presharedKey".path;
    +
    +                    AllowedIPs =
    +                      let
    +                        clientInWgNetwork =
    +                          globals.networks."${config.swarselsystems.server.netConfigPrefix}-${i.ifName}".hosts.${clientName};
    +                      in
    +                      (lib.optional (clientInWgNetwork.ipv4 != null)
    +                        (lib.net.cidr.make 32 clientInWgNetwork.ipv4))
    +                      ++ (lib.optional (clientInWgNetwork.ipv6 != null)
    +                        (lib.net.cidr.make 128 clientInWgNetwork.ipv6));
    +                  })
    +                  i.peers);
    +            };
    +          })
    +        ifaceList);
         };
    -
    -    # networking = {
    -    #   wireguard = {
    -    #     enable = true;
    -    #     interfaces = {
    -    #       wg1 = {
    -    #         privateKeyFile = config.sops.secrets.wireguard-private-key.path;
    -    #         ips = [ "192.168.178.201/24" ];
    -    #         peers = [
    -    #           {
    -    #             publicKey = "PmeFInoEJcKx+7Kva4dNnjOEnJ8lbudSf1cbdo/tzgw=";
    -    #             presharedKeyFile = config.sops.secrets.wireguard-home-preshared-key.path;
    -    #             name = "moonside";
    -    #             persistentKeepalive = 25;
    -    #             # endpoint = "${config.repo.secrets.common.ipv4}:51820";
    -    #             endpoint = "${config.repo.secrets.common.wireguardEndpoint}";
    -    #             # allowedIPs = [
    -    #             #   "192.168.3.0/24"
    -    #             #   "192.168.1.0/24"
    -    #             # ];
    -    #             allowedIPs = [
    -    #               "192.168.178.0/24"
    -    #             ];
    -    #           }
    -    #         ];
    -    #       };
    -    #     };
    -    #   };
    -    # };
    -
    -
       };
     }
     
    -
    -
    3.2.3.12. BTRFS
    -
    +
    +
    3.2.3.14. BTRFS
    +
    { lib, config, ... }:
     {
    @@ -10812,7 +11090,7 @@ in
     
    -
    3.2.3.13. Router
    +
    3.2.3.15. Router
    { lib, config, ... }:
    @@ -10876,7 +11154,7 @@ in
     
    -
    3.2.3.14. kavita
    +
    3.2.3.16. kavita
    { self, lib, config, pkgs, globals, dns, confLib, ... }:
    @@ -10955,7 +11233,7 @@ in
     
    -
    3.2.3.15. jellyfin
    +
    3.2.3.17. jellyfin
    { pkgs, lib, config, globals, dns, confLib, ... }:
    @@ -11034,7 +11312,7 @@ in
     
    -
    3.2.3.16. navidrome
    +
    3.2.3.18. navidrome
    { pkgs, config, lib, globals, dns, confLib, ... }:
    @@ -11205,7 +11483,7 @@ in
     
    -
    3.2.3.17. spotifyd
    +
    3.2.3.19. spotifyd
    { lib, config, confLib, ... }:
    @@ -11261,7 +11539,7 @@ in
     
    -
    3.2.3.18. mpd
    +
    3.2.3.20. mpd
    { self, lib, config, pkgs, confLib, ... }:
    @@ -11331,7 +11609,7 @@ in
     
    -
    3.2.3.19. pipewire
    +
    3.2.3.21. pipewire
    { lib, config, ... }:
    @@ -11359,7 +11637,7 @@ in
     
    -
    3.2.3.20. postgresql
    +
    3.2.3.22. postgresql
    { config, lib, pkgs, confLib, ... }:
    @@ -11389,7 +11667,7 @@ in
     
    -
    3.2.3.21. matrix
    +
    3.2.3.23. matrix
    { lib, config, pkgs, globals, dns, confLib, ... }:
    @@ -11749,7 +12027,7 @@ in
     
    -
    3.2.3.22. nextcloud
    +
    3.2.3.24. nextcloud
    { pkgs, lib, config, globals, dns, confLib, ... }:
    @@ -11836,7 +12114,7 @@ in
     
    -
    3.2.3.23. immich
    +
    3.2.3.25. immich
    { lib, pkgs, config, globals, dns, confLib, ... }:
    @@ -11919,7 +12197,7 @@ in
     
    -
    3.2.3.24. paperless (tika, gotenberg)
    +
    3.2.3.26. paperless (tika, gotenberg)

    This is my personal document management system. It automatically pulls documents from several sources, the only manual step for physical documents is to put them in my scanner and use email delivery. @@ -12068,7 +12346,7 @@ in

    -
    3.2.3.25. transmission
    +
    3.2.3.27. transmission
    { self, pkgs, lib, config, confLib, ... }:
    @@ -12256,7 +12534,7 @@ in
     
    -
    3.2.3.26. syncthing
    +
    3.2.3.28. syncthing
    { lib, config, globals, dns, confLib, ... }:
    @@ -12408,7 +12686,7 @@ in
     
    -
    3.2.3.27. restic
    +
    3.2.3.29. restic

    This manages backups for my pictures and obsidian files. @@ -12482,7 +12760,7 @@ in

    -
    3.2.3.28. monitoring (Grafana, Prometheus)
    +
    3.2.3.30. monitoring (Grafana, Prometheus)

    This section exposes several metrics that I use to check the health of my server. I need to expand on the exporters section at some point, but for now I have everything I need. @@ -12747,7 +13025,7 @@ in

    -
    3.2.3.29. Jenkins (currently unused)
    +
    3.2.3.31. Jenkins (currently unused)

    This is a WIP Jenkins instance. It is used to automatically build a new system when pushes to the main repository are detected. I have turned this service off for now however, as I actually prefer to start my builds manually. @@ -12813,7 +13091,7 @@ in

    -
    3.2.3.30. Emacs elfeed (RSS Server)
    +
    3.2.3.32. Emacs elfeed (RSS Server)

    This was an approach of hosting an RSS server from within emacs. That would have been useful as it would have allowed me to allow my feeds from any device. However, it proved impossible to do bidirectional syncing, so I abandoned this configuration in favor of FreshRSS. @@ -12844,7 +13122,7 @@ in

    -
    3.2.3.31. FreshRSS
    +
    3.2.3.33. FreshRSS

    FreshRSS is a more 'classical' RSS aggregator that I can just host as a distinct service. This also has its upsides because I jave more control over the state this way. @@ -12977,7 +13255,7 @@ in

    -
    3.2.3.32. forgejo (git server)
    +
    3.2.3.34. forgejo (git server)
    { lib, config, pkgs, globals, dns, confLib, ... }:
    @@ -13145,7 +13423,7 @@ in
     
    -
    3.2.3.33. Anki Sync Server
    +
    3.2.3.35. Anki Sync Server
    { self, lib, config, globals, dns, confLib, ... }:
    @@ -13224,7 +13502,7 @@ in
     
    -
    3.2.3.34. kanidm
    +
    3.2.3.36. kanidm

    The forgejo configuration is a little broken and will show a 500 error when signing in through kanidm. However, when pressing back and refreshing the page, I am logged in. Currently I cannot be bothered to fix this. @@ -13666,7 +13944,7 @@ in

    -
    3.2.3.35. oauth2-proxy
    +
    3.2.3.37. oauth2-proxy
    { lib, config, globals, dns, confLib, ... }:
    @@ -13902,7 +14180,7 @@ in
     
    -
    3.2.3.36. Firefly-III
    +
    3.2.3.38. Firefly-III
    { self, lib, config, globals, dns, confLib, ... }:
    @@ -14026,7 +14304,7 @@ in
     
    -
    3.2.3.37. Koillection
    +
    3.2.3.39. Koillection
    { self, lib, config, globals, dns, confLib, ... }:
    @@ -14172,7 +14450,7 @@ in
     
    -
    3.2.3.38. Atuin
    +
    3.2.3.40. Atuin
    { lib, config, globals, dns, confLib, ... }:
    @@ -14236,7 +14514,7 @@ in
     
    -
    3.2.3.39. Radicale
    +
    3.2.3.41. Radicale
    { lib, config, globals, dns, confLib, ... }:
    @@ -14367,7 +14645,7 @@ in
     
    -
    3.2.3.40. croc
    +
    3.2.3.42. croc
    { self, lib, config, pkgs, dns, globals, confLib, ... }:
    @@ -14449,7 +14727,7 @@ in
     
    -
    3.2.3.41. microbin
    +
    3.2.3.43. microbin
    { self, lib, config, dns, globals, confLib, ... }:
    @@ -14593,7 +14871,7 @@ in
     
    -
    3.2.3.42. shlink
    +
    3.2.3.44. shlink
    { self, lib, config, dns, globals, confLib, ... }:
    @@ -14713,7 +14991,7 @@ in
     
    -
    3.2.3.43. slink
    +
    3.2.3.45. slink

    Deployment notes: @@ -14827,7 +15105,7 @@ in

    -
    3.2.3.44. Snipe-IT (currently unused)
    +
    3.2.3.46. Snipe-IT (currently unused)
    { lib, config, globals, dns, confLib, ... }:
    @@ -14910,7 +15188,7 @@ in
     
    -
    3.2.3.45. Homebox
    +
    3.2.3.47. Homebox
    { lib, pkgs, config, globals, dns, confLib, ... }:
    @@ -14978,7 +15256,7 @@ in
     
    -
    3.2.3.46. OPKSSH
    +
    3.2.3.48. OPKSSH
    { lib, config, globals, confLib, ... }:
    @@ -15022,7 +15300,7 @@ in
     
    -
    3.2.3.47. Garage
    +
    3.2.3.49. Garage

    Garage acts as my s3 endpoint. I use it on two of my servers: @@ -15431,9 +15709,9 @@ in

    -
    -
    3.2.3.48. Set host domain for dns
    -
    +
    +
    3.2.3.50. Set host domain for dns
    +
    { lib, config, globals, dns, confLib, ... }:
     let
    @@ -15454,7 +15732,7 @@ in
     
    -
    3.2.3.49. nsd (dns)
    +
    3.2.3.51. nsd (dns)
    { lib, config, globals, dns, confLib, ... }:
    @@ -15552,7 +15830,7 @@ in
     
    -
    3.2.3.50. nsd (dns) - site1
    +
    3.2.3.52. nsd (dns) - site1
    { config, globals, dns, proxyAddress4, proxyAddress6, ... }:
    @@ -15560,7 +15838,7 @@ with dns.lib.combinators; {
       SOA = {
         nameServer = "soa";
         adminEmail = "admin@${globals.domains.main}"; # this option is not parsed as domain (we cannot just write "admin")
    -    serial = 2025120506; # update this on changes for secondary dns
    +    serial = 2025122204; # update this on changes for secondary dns
       };
     
       useOrigin = false;
    @@ -15570,7 +15848,23 @@ with dns.lib.combinators; {
         "srv"
       ] ++ globals.domains.externalDns;
     
    -  CAA = letsEncrypt config.repo.secrets.common.dnsMail;
    +  CAA = [
    +    {
    +      issuerCritical = false;
    +      tag = "issue";
    +      value = "letsencrypt.org";
    +    }
    +    {
    +      issuerCritical = false;
    +      tag = "issuewild";
    +      value = "letsencrypt.org";
    +    }
    +    {
    +      issuerCritical = false;
    +      tag = "iodef";
    +      value = "mailto:${config.repo.secrets.common.dnsMail}";
    +    }
    +  ];
     
       A = [ config.repo.secrets.local.dns.homepage-ip ];
     
    @@ -15661,7 +15955,7 @@ with dns.lib.combinators; {
     
    -
    3.2.3.51. Minecraft
    +
    3.2.3.53. Minecraft
    { lib, config, pkgs, globals, dns, confLib, ... }:
    @@ -15719,15 +16013,19 @@ in
     
    -
    3.2.3.52. Mailserver
    +
    3.2.3.54. Mailserver
    { lib, config, globals, dns, confLib, ... }:
     let
       inherit (config.swarselsystems) sopsFile;
    -  inherit (confLib.gen { name = "mailserver"; dir = "/var/lib/dovecot"; user = "virtualMail"; group = "virtualMail"; port = 443; }) serviceName serviceDir servicePort serviceUser serviceGroup serviceDomain serviceProxy proxyAddress4 proxyAddress6;
    +  inherit (confLib.gen { name = "mailserver"; dir = "/var/lib/dovecot"; user = "virtualMail"; group = "virtualMail"; port = 443; }) serviceName serviceDir servicePort serviceUser serviceGroup serviceAddress serviceDomain serviceProxy proxyAddress4 proxyAddress6;
       inherit (config.repo.secrets.local.mailserver) user1 alias1_1 alias1_2 alias1_3 alias1_4 user2 alias2_1 alias2_2 user3;
       baseDomain = globals.domains.main;
    +
    +  roundcubeDomain = config.repo.secrets.common.services.domains.roundcube;
    +  endpointAddress4 = globals.hosts.${config.node.name}.wanAddress4 or null;
    +  endpointAddress6 = globals.hosts.${config.node.name}.wanAddress6 or null;
     in
     {
       options = {
    @@ -15736,12 +16034,20 @@ in
       config = lib.mkIf config.swarselmodules.server.${serviceName} {
     
         nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = {
    -      "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6;
    +      "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host endpointAddress4 endpointAddress6;
    +      "${globals.services.roundcube.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6;
         };
     
    -    globals.services.${serviceName} = {
    -      domain = serviceDomain;
    -      inherit proxyAddress4 proxyAddress6;
    +    globals.services = {
    +      ${serviceName} = {
    +        domain = serviceDomain;
    +        proxyAddress4 = endpointAddress4;
    +        proxyAddress6 = endpointAddress6;
    +      };
    +      roundcube = {
    +        domain = roundcubeDomain;
    +        inherit proxyAddress4 proxyAddress6;
    +      };
         };
     
         sops.secrets = {
    @@ -15807,7 +16113,7 @@ in
           enable = true;
           # this is the url of the vhost, not necessarily the same as the fqdn of
           # the mailserver
    -      hostName = serviceDomain;
    +      hostName = roundcubeDomain;
           extraConfig = ''
             $config['imap_host'] = "ssl://${config.mailserver.fqdn}";
             $config['smtp_host'] = "ssl://${config.mailserver.fqdn}";
    @@ -15820,10 +16126,11 @@ in
         # the rest of the ports are managed by snm
         networking.firewall.allowedTCPPorts = [ 80 servicePort ];
     
    -    nodes.${serviceProxy}.services.nginx = {
    +    services.nginx = {
           virtualHosts = {
    -        "${serviceDomain}" = {
    -          enableACME = true;
    +        "${roundcubeDomain}" = {
    +          useACMEHost = globals.domains.main;
    +          enableACME = false;
               forceSSL = true;
               acmeRoot = null;
               locations = {
    @@ -15836,6 +16143,32 @@ in
           };
         };
     
    +    nodes.${serviceProxy}.services.nginx = {
    +      upstreams = {
    +        ${serviceName} = {
    +          servers = {
    +            "${serviceAddress}:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${roundcubeDomain}" = {
    +          useACMEHost = globals.domains.main;
    +
    +          forceSSL = true;
    +          acmeRoot = null;
    +          locations = {
    +            "/" = {
    +              proxyPass = "https://${serviceName}";
    +              extraConfig = ''
    +                client_max_body_size    0;
    +              '';
    +            };
    +          };
    +        };
    +      };
    +    };
    +
       };
     }
     
    @@ -15843,7 +16176,7 @@ in
    -
    3.2.3.53. Attic (nix binary cache)
    +
    3.2.3.55. Attic (nix binary cache)

    Generate the attic server token using openssl genrsa -traditional 4096 | base64 -w0 @@ -16021,9 +16354,9 @@ in

    -
    -
    3.2.3.54. Hydra
    -
    +
    +
    3.2.3.56. Hydra
    +

    Need to create user manually:

    @@ -16834,9 +17167,9 @@ in
    -
    -
    3.2.5.11. Uni
    -
    +
    +
    3.2.5.11. Uni
    +
    { self, config, ... }:
     {
    @@ -16901,9 +17234,9 @@ Some standard options that should be set vor every microvm guest. We set the def
     
    -
    -
    3.2.5.14. systemd-networkd (server)
    -
    +
    +
    3.2.5.14. systemd-networkd (server)
    +

    Some standard options that should be set vor every microvm guest. We set the default

    @@ -16960,6 +17293,32 @@ Some standard options that should be set vor every microvm guest. We set the def }; } +
    +
    +
    +
    +
    +
    3.2.5.15. nix-topology node config
    +
    +

    +Hold standard options for nix-topology per config +

    + +
    +
    { lib, config, globals, ... }:
    +{
    +  topology.self = {
    +    icon = lib.mkIf config.swarselsystems.isCloud "devices.cloud-server";
    +    interfaces.wan = lib.mkIf config.swarselsystems.isCloud { };
    +    interfaces.wg = lib.mkIf (config.swarselsystems.server.wireguard.isClient || config.swarselsystems.server.wireguard.isServer) {
    +      addresses = [ globals.networks.twothreetunnel-wg.hosts.${config.node.name}.ipv4 ];
    +      renderer.hidePhysicalConnections = true;
    +      virtual = true;
    +      type = "wireguard";
    +    };
    +  };
    +}
    +
     
    @@ -17508,6 +17867,8 @@ This is just a separate container for derivations defined in +
    +
    +
    +
    +
    3.3.2.32.13. TODO attic store push service
    +
    +

    +Normally, I want to push all nix build artifacts to my main cache automatically, which is realized here. Note that authentication to the cache must be done manually once on client nodes. TODO: fix that +

    + +
    +
    { lib, config, pkgs, ... }:
    +{
    +  options.swarselmodules.attic-store-push = lib.mkEnableOption "enable automatic attic store push";
    +  config = lib.mkIf config.swarselmodules.attic-store-push {
    +
    +    systemd.user.services.attic-store-push = {
    +      Unit = {
    +        Description = "Attic store pusher";
    +        Requires = [ "graphical-session.target" ];
    +        After = [ "graphical-session.target" ];
    +        PartOf = [ "graphical-session.target" ];
    +      };
    +
    +      Install = {
    +        WantedBy = [ "graphical-session.target" ];
    +      };
    +
    +      Service = {
    +        ExecStart = "${lib.getExe pkgs.attic-client} watch-store ${config.swarselsystems.mainUser}:${config.swarselsystems.mainUser}";
    +      };
    +    };
    +  };
    +
     }
     
    @@ -24246,9 +24643,9 @@ In short, the options defined here are passed to the modules systems using
    -
    -
    3.4.6.7. prstatus
    -
    +
    +
    3.4.6.7. prstatus
    +

    This script allows for quick checking of nixpkgs PR statuses.

    @@ -25280,10 +25677,10 @@ $ssh_root_cmd "nixos-generate-config --force --no-filesystems --root /mnt" mkdir -p "$FLAKE"/hosts/nixos/"$target_arch"/"$target_hostname" $scp_cmd root@"$target_destination":/mnt/etc/nixos/hardware-configuration.nix "${git_root}"/hosts/nixos/"$target_arch"/"$target_hostname"/hardware-configuration.nix # ------------------------ -# green "Generating hostkey for ssh initrd" -# $ssh_root_cmd "mkdir -p $temp/etc/secrets/initrd /etc/secrets/initrd" -# $ssh_root_cmd "ssh-keygen -t ed25519 -N '' -f $temp/etc/secrets/initrd/ssh_host_ed25519_key" -# $ssh_root_cmd "cp $temp/etc/secrets/initrd/ssh_host_ed25519_key /etc/secrets/initrd/ssh_host_ed25519_key" +green "Generating hostkey for ssh initrd" +$ssh_root_cmd "mkdir -p $temp/etc/secrets/initrd /etc/secrets/initrd" +$ssh_root_cmd "ssh-keygen -t ed25519 -N '' -f $temp/etc/secrets/initrd/ssh_host_ed25519_key" +$ssh_root_cmd "cp $temp/etc/secrets/initrd/ssh_host_ed25519_key /etc/secrets/initrd/ssh_host_ed25519_key" # ------------------------ green "Deploying minimal NixOS installation on $target_destination" @@ -26452,12 +26849,12 @@ writeShellApplication {

    3.4.7. Packages (config)

    -
    { self, homeConfig, lib, pkgs, ... }:
    +
    { self, homeConfig, lib, pkgs, config, ... }:
     let
       mkPackages = names: pkgs: builtins.listToAttrs (map
         (name: {
           inherit name;
    -      value = pkgs.callPackage "${self}/pkgs/config/${name}" { inherit self name homeConfig; };
    +      value = pkgs.callPackage "${self}/pkgs/config/${name}" { inherit self name homeConfig config; };
         })
         names);
       packageNames = lib.swarselsystems.readNix "pkgs/config";
    @@ -26485,6 +26882,52 @@ writeShellApplication {
     }
     
     
    +
    +
    +
    +
    +
    +
    3.4.7.2. swarsel-gens
    +
    +

    +This script quickly lists all nix generatinos on the system +

    + +
    +
    { name, writeShellApplication, config, ... }:
    +
    +writeShellApplication {
    +  inherit name;
    +  runtimeInputs = [ config.nix.package ];
    +  text = ''
    +    sudo nix-env --list-generations --profile /nix/var/nix/profiles/system
    +  '';
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    3.4.7.3. swarsel-switch
    +
    +

    +This script quickly switches to another nix generation. +

    + +
    +
    { name, writeShellApplication, config, ... }:
    +
    +writeShellApplication {
    +  inherit name;
    +  runtimeInputs = [ config.nix.package ];
    +  text = ''
    +    sudo nix-env --switch-generation "$1" -p /nix/var/nix/profiles/system && sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
    +  '';
    +}
    +
    +
     
    @@ -26715,7 +27158,7 @@ in diskEncryption = lib.mkDefault true; packages = lib.mkDefault true; ssh = lib.mkDefault true; - nginx = lib.mkDefault true; + attic-setup = lib.mkDefault true; }; }; }; @@ -26777,6 +27220,7 @@ in swarselmodules = { anki = lib.mkDefault true; anki-tray = lib.mkDefault true; + attic-store-push = lib.mkDefault true; atuin = lib.mkDefault true; autotiling = lib.mkDefault true; batsignal = lib.mkDefault true; @@ -33648,9 +34092,13 @@ Here lies defined the readme for GitHub and Forgejo: This sections explains commonly used functions in nix (both builtins as well as in the nixpkgs library).

    -
    -

    7.1. The '//' operator

    -
    +
    +

    7.1. Concepts

    +
    +
    +
    +

    7.1.1. The '//' operator

    +

    The // operator in nix is used to merge attribute sets; if an attribute exists in both sets, then the latter set takes precedence:

    @@ -33684,26 +34132,17 @@ It only took the latter b set, even though the b2 valu

    -
    -

    7.2. builtins.listToAttrs

    -
    +
    +
    +

    7.2. Builtin functions

    +

    -builtins.listToAttrs converts a list of name-value pairs into an attribute set. +The following functions are only base functionality of nix, and not exported to nixpkgs. When using them we need to make sure that the nix version in use is matching the expected function.

    - -
    -
    swarsel-instantiate 'builtins.listToAttrs [{ name = "foo"; value = 1; } { name = "bar"; value = 2; }]'
    -
    - -
    -{ bar = 2; foo = 1; }
    -
    -
    -
    -
    -

    7.3. builtins.readDir

    -
    +
    +

    7.2.1. builtins.readDir

    +

    builtins.readDir reads the name of items of a directory as attributes and their type as values.

    @@ -33718,43 +34157,34 @@ builtins.listToAttrs converts a list of name-value pairs into an attribute set.
    -
    -

    7.4. nixpkgs.lib.recursiveUpdate

    -
    +
    +
    +

    7.3. Builtin functions exported to nixpkgs

    +

    -If you want to merge nested attribute sets, use nixpkgs.lib.recursiveUpdate instead of The '//' operator: +These functions exist in nix, but are exported to nixpkgs to make sure they use a nix version that works in the used nixpgks. Most of the times I should prefer to use the nixpkgs version of these. +

    +
    +
    +

    7.3.1. nixpkgs.lib.listToAttrs

    +
    +

    +builtins.listToAttrs converts a list of name-value pairs into an attribute set.

    -
    swarsel-instantiate 'lib.recursiveUpdate { a = { a1 = 1; a2 = 2; }; b = { b1 = 1; b2 = 2; }; } { b = { b1 = 3; b3 = 4;}; c = 4; }'
    +
    swarsel-instantiate 'builtins.listToAttrs [{ name = "foo"; value = 1; } { name = "bar"; value = 2; }]'
     
    -{ a = { a1 = 1; a2 = 2; }; b = { b1 = 3; b2 = 2; b3 = 4; }; c = 4; }
    +{ bar = 2; foo = 1; }
     
    -
    -

    7.5. nixpkgs.lib.genAttrs

    -
    -

    -nixpkgs.lib.genAttrs is used to generate an attribute set (a dictionary-like structure) from a list of keys and a function that computes the values for those keys. -

    - -
    -
    swarsel-instantiate 'lib.genAttrs ["a" "b" "c"] (x: "${x}-value")'
    -
    -
    - -
    -{ a = "a-value"; b = "b-value"; c = "c-value"; }
    -
    -
    -
    -
    -

    7.6. nixpkgs.lib.attrNames

    -
    +
    +

    7.3.2. nixpkgs.lib.attrNames

    +

    nixpkgs.lib.attrNames returns the list of attribute names from an attribute set.

    @@ -33769,9 +34199,9 @@ If you want to merge nested attribute sets, use nixpkgs.lib.recursiveUpdat
    -
    -

    7.7. nixpkgs.lib.map

    -
    +
    +

    7.3.3. nixpkgs.lib.map

    +

    nixpkgs.lib.map takes a function and applies the elements of a list upon them.

    @@ -33786,6 +34216,7 @@ If you want to merge nested attribute sets, use nixpkgs.lib.recursiveUpdat +
    swarsel-instantiate 'lib.map (x: { result = x + 1; }) [1 2 3]'
     
    @@ -33796,42 +34227,9 @@ If you want to merge nested attribute sets, use nixpkgs.lib.recursiveUpdat
    -
    -

    7.8. nixpkgs.lib.mkOverride

    -
    -

    -nixpkgs.lib.mkOverride sets the priority of an expression. -

    - -

    -If two expression are defined twice accross the configuration, the evaluator does not know which one should take precedence; this will lead to an error. By default, all option definitions are given priority 100. Lower values take precedence over lower values. For reference, here are some commonly used values: -

    - -
      -
    • nixpkgs.lib.mkForce sets the priority to 50, which is a very low value, meaning it will be prioritised in nearly all cases
    • -
    • nixpkgs.lib.mkDefault sets the priority to 1000, which is a quite high value that will almost never be used if the same attribute is defined elswheer.
    • -
    -
    -
    -
    -

    7.9. nixpkgs.lib.mkForce

    -
    -

    -An alias for (nixpkgs.lib.mkOverride 50). -

    -
    -
    -
    -

    7.10. nixpkgs.lib.mkDefault

    -
    -

    -An alias for (nixpkgs.lib.mkOverride 1000). -

    -
    -
    -
    -

    7.11. nixpkgs.lib.filter

    -
    +
    +

    7.3.4. nixpkgs.lib.filter

    +

    nixpkgs.lib.filter takes a list as input and only keeps elements that fulfill a given function:

    @@ -33846,9 +34244,306 @@ An alias for (nixpkgs.lib.mkOv
    -
    -

    7.12. nixpkgs.lib.mapAttrsToList

    -
    +
    +

    7.3.5. nixpkgs.lib.concatLists

    +
    +

    +nixpkgs.lib.concatLists, as the name suggests, concatenates lists, while keeping the original order of items: +

    + +
    +
    swarsel-instantiate 'lib.concatLists  [ [ 1 2 ] [ 3 4 ] [ 5 ] ]'
    +
    +
    + +
    +[ 1 2 3 4 5 ]
    +
    +
    +
    +
    +

    7.3.6. nixpkgs.lib.genList

    +
    +

    +nixpkgs.lib.genList generates a list from a function: +

    + +
    +
    swarsel-instantiate 'lib.genList (i: if i < 16 then 1 else 0) 32'
    +
    +
    + +
    +[ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
    +
    +
    +
    +
    +

    7.3.7. nixpkgs.lib.concatStringsSep

    +
    +

    +nixpkgs.lib.concatStringsSep concatenates a list of strings with a defined separator: +

    + +
    +
    swarsel-instantiate 'lib.concatStringsSep "." ["services" "nginx"]'
    +
    +
    + +
    +services.nginx
    +
    +
    +
    +
    +

    7.3.8. nixpkgs.lib.split

    +
    +

    +nixpkgs.lib.split creates a list containing both the list of regex matches as well as the non-matched strings of a regex pattern: +

    + +
    +
    swarsel-instantiate 'lib.split "\\." "sub.about.com"'
    +
    +
    + +
    +[ "sub" [ ] "about" [ ] "com" ]
    +
    + + +

    +Another example with regex matches: +

    + +
    +
    swarsel-instantiate 'lib.split "(s)." "swarsel"'
    +
    +
    + +
    +[ "" [ "s" ] "ar" [ "s" ] "l" ]
    +
    + + +

    +The result list always begins with non-matching patterns, so if the first character is in a match, the first element will be "". +

    +
    +
    +
    +

    7.3.9. nixpkgs.lib.tail

    +
    +

    +nixpkgs.lib.tail returns a list without its first item: +

    + +
    +
    swarsel-instantiate 'lib.tail [ "sub" "about" "com" ]'
    +
    +
    + +
    +[ "about" "com" ]
    +
    +
    +
    +
    +

    7.3.10. nixpkgs.lib.head

    +
    +

    +nixpkgs.lib.tail returns the first item of a list: +

    + +
    +
    swarsel-instantiate 'lib.head [ "sub" "about" "com" ]'
    +
    +
    + +
    +sub
    +
    +
    +
    +
    +

    7.3.11. nixpkgs.lib.isString

    +
    +

    +nixpkgs.lib.isString returns a bool based on if the input evaluates to a string: +

    + +
    +
    swarsel-instantiate 'lib.isString [ "1" ]'
    +swarsel-instantiate 'lib.isString "1"'
    +
    +
    + + + + +++ + + + + + + + + + +
    false
    true
    +
    +
    +
    +

    7.3.12. nixpkgs.lib.length

    +
    +

    +nixpkgs.lib.length returns the length of a list: +

    + +
    +
    swarsel-instantiate 'lib.length [ "1" [] ]'
    +
    +
    + +
    +2
    +
    +
    +
    +
    +

    7.3.13. nixpkgs.lib.stringLength

    +
    +

    +nixpkgs.lib.stringLength returns the byte length of a string: +

    + +
    +
    swarsel-instantiate 'lib.stringLength "<3"'
    +swarsel-instantiate 'lib.stringLength "❤"'
    +swarsel-instantiate 'lib.stringLength "❤️"'
    +
    +
    + + + + +++ + + + + + + + + + + + + + +
    2
    3
    6
    +
    +
    +
    +

    7.3.14. nixpkgs.lib.subString

    +
    +

    +nixpkgs.lib.substring returns the substring within the specified range (excluding the end index): +

    + +
    +
    swarsel-instantiate 'lib.substring 0 3 "about"'
    +
    +
    + +
    +abo
    +
    +
    +
    +
    +
    +

    7.4. Functions in nixpgks

    +
    +

    +These functions only exist in nixpkgs and cannot be used in generic nix code. +

    +
    +
    +

    7.4.1. nixpkgs.lib.recursiveUpdate

    +
    +

    +If you want to merge nested attribute sets, use nixpkgs.lib.recursiveUpdate instead of The '//' operator: +

    + +
    +
    swarsel-instantiate 'lib.recursiveUpdate { a = { a1 = 1; a2 = 2; }; b = { b1 = 1; b2 = 2; }; } { b = { b1 = 3; b3 = 4;}; c = 4; }'
    +
    +
    + +
    +{ a = { a1 = 1; a2 = 2; }; b = { b1 = 3; b2 = 2; b3 = 4; }; c = 4; }
    +
    +
    +
    +
    +

    7.4.2. nixpkgs.lib.genAttrs

    +
    +

    +nixpkgs.lib.genAttrs is used to generate an attribute set (a dictionary-like structure) from a list of keys and a function that computes the values for those keys. +

    + +
    +
    swarsel-instantiate 'lib.genAttrs ["a" "b" "c"] (x: "${x}-value")'
    +
    +
    + +
    +{ a = "a-value"; b = "b-value"; c = "c-value"; }
    +
    +
    +
    +
    +

    7.4.3. nixpkgs.lib.mkOverride

    +
    +

    +nixpkgs.lib.mkOverride sets the priority of an expression. +

    + +

    +If two expression are defined twice accross the configuration, the evaluator does not know which one should take precedence; this will lead to an error. By default, all option definitions are given priority 100. Lower values take precedence over lower values. For reference, here are some commonly used values: +

    + +
      +
    • nixpkgs.lib.mkForce sets the priority to 50, which is a very low value, meaning it will be prioritised in nearly all cases
    • +
    • nixpkgs.lib.mkDefault sets the priority to 1000, which is a quite high value that will almost never be used if the same attribute is defined elswheer.
    • +
    +
    +
    +
    +

    7.4.4. nixpkgs.lib.mkForce

    +
    +

    +An alias for (nixpkgs.lib.mkOverride 50). +

    +
    +
    +
    +

    7.4.5. nixpkgs.lib.mkDefault

    +
    +

    +An alias for (nixpkgs.lib.mkOverride 1000). +

    +
    +
    +
    +

    7.4.6. nixpkgs.lib.mapAttrsToList

    +

    nixpkgs.lib.mapAttrsToList converts an attribute set into a list by applying a given function to each name-value pair:

    @@ -33863,9 +34558,9 @@ An alias for (nixpkgs.lib.mkOv
    -
    -

    7.13. nixpkgs.lib.flip

    -
    +
    +

    7.4.7. nixpkgs.lib.flip

    +
    • nixpkgs.lib.flip reverses the argument order passed to an expression (in semantics, that is lib.flip f a b == f b a). This is useful when an expression has one big argument and one small one; in that case it is convenient to have the shorter expression at the start of the function. Take this example of a function that simply prints its attributes as a list:
    @@ -33894,26 +34589,9 @@ It prints the attributs unchanged, as is to be expected. If, however, we call th
    -
    -

    7.14. nixpkgs.lib.concatLists

    -
    -

    -nixpkgs.lib.concatLists, as the name suggests, concatenates lists, while keeping the original order of items: -

    - -
    -
    swarsel-instantiate 'lib.concatLists  [ [ 1 2 ] [ 3 4 ] [ 5 ] ]'
    -
    -
    - -
    -[ 1 2 3 4 5 ]
    -
    -
    -
    -