about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md4
-rw-r--r--nixos/lib/systemd-lib.nix4
-rw-r--r--nixos/lib/systemd-network-units.nix3
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/programs/bash/bash.nix2
-rw-r--r--nixos/modules/programs/nh.nix96
-rw-r--r--nixos/modules/programs/slock.nix5
-rw-r--r--nixos/modules/services/monitoring/grafana.nix2
-rw-r--r--nixos/modules/services/networking/nsd.nix18
-rw-r--r--nixos/modules/services/web-apps/coder.nix20
-rw-r--r--nixos/modules/system/boot/networkd.nix42
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/systemd-networkd-bridge.nix103
13 files changed, 283 insertions, 18 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 9fcfd6defc572..988632fc44349 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -153,6 +153,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - binfmt option for AppImage-run to support running [AppImage](https://appimage.org/)'s seamlessly on NixOS.. Available as [programs.appimage.binfmt](#opt-programs.appimage.binfmt).
 
+- [nh](https://github.com/viperML/nh), yet another Nix CLI helper. Available as [programs.nh](#opt-programs.nh.enable).
+
 - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable)
 
 - [RustDesk](https://rustdesk.com), a full-featured open source remote control alternative for self-hosting and security with minimal configuration. Alternative to TeamViewer.
@@ -596,3 +598,5 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `documentation.man.mandoc` now by default uses `MANPATH` to set the directories where mandoc will search for manual pages.
   This enables mandoc to find manual pages in Nix profiles. To set the manual search paths via the `mandoc.conf` configuration file like before, use `documentation.man.mandoc.settings.manpath` instead.
+
+- The `grafana-loki` package was updated to 3.0.0 which includes [breaking changes](https://github.com/grafana/loki/releases/tag/v3.0.0)
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index b67495609ff5a..198a710f052dd 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -148,6 +148,10 @@ in rec {
     optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
       "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
 
+  assertRangeOrOneOf = name: min: max: values: group: attr:
+    optional (attr ? ${name} && !((min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
+      "Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}";
+
   assertMinimum = name: min: group: attr:
     optional (attr ? ${name} && attr.${name} < min)
       "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
diff --git a/nixos/lib/systemd-network-units.nix b/nixos/lib/systemd-network-units.nix
index 986586a61a705..ae581495772a8 100644
--- a/nixos/lib/systemd-network-units.nix
+++ b/nixos/lib/systemd-network-units.nix
@@ -25,6 +25,9 @@ in {
     commonMatchText def + ''
       [NetDev]
       ${attrsToSection def.netdevConfig}
+    '' + optionalString (def.bridgeConfig != { }) ''
+      [Bridge]
+      ${attrsToSection def.bridgeConfig}
     '' + optionalString (def.vlanConfig != { }) ''
       [VLAN]
       ${attrsToSection def.vlanConfig}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6c43cd06180ab..a45507d5ee3cd 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -233,6 +233,7 @@
   ./programs/neovim.nix
   ./programs/nethoscope.nix
   ./programs/nexttrace.nix
+  ./programs/nh.nix
   ./programs/nix-index.nix
   ./programs/nix-ld.nix
   ./programs/nm-applet.nix
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 87fb29ed5bbe6..21ef8338d8dd8 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -30,7 +30,7 @@ in
       /*
       enable = mkOption {
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whenever to configure Bash as an interactive shell.
           Note that this tries to make Bash the default
           {option}`users.defaultUserShell`,
diff --git a/nixos/modules/programs/nh.nix b/nixos/modules/programs/nh.nix
new file mode 100644
index 0000000000000..c42fb2fc724a7
--- /dev/null
+++ b/nixos/modules/programs/nh.nix
@@ -0,0 +1,96 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.programs.nh;
+in
+{
+  meta.maintainers = [ lib.maintainers.viperML ];
+
+  options.programs.nh = {
+    enable = lib.mkEnableOption "nh, yet another Nix CLI helper";
+
+    package = lib.mkPackageOption pkgs "nh" { };
+
+    flake = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      description = ''
+        The path that will be used for the `FLAKE` environment variable.
+
+        `FLAKE` is used by nh as the default flake for performing actions, like `nh os switch`.
+      '';
+    };
+
+    clean = {
+      enable = lib.mkEnableOption "periodic garbage collection with nh clean all";
+
+      dates = lib.mkOption {
+        type = lib.types.singleLineStr;
+        default = "weekly";
+        description = ''
+          How often cleanup is performed. Passed to systemd.time
+
+          The format is described in
+          {manpage}`systemd.time(7)`.
+        '';
+      };
+
+      extraArgs = lib.mkOption {
+        type = lib.types.singleLineStr;
+        default = "";
+        example = "--keep 5 --keep-since 3d";
+        description = ''
+          Options given to nh clean when the service is run automatically.
+
+          See `nh clean all --help` for more information.
+        '';
+      };
+    };
+  };
+
+  config = {
+    warnings =
+      if (!(cfg.clean.enable -> !config.nix.gc.automatic)) then [
+        "programs.nh.clean.enable and nix.gc.automatic are both enabled. Please use one or the other to avoid conflict."
+      ] else [ ];
+
+    assertions = [
+      # Not strictly required but probably a good assertion to have
+      {
+        assertion = cfg.clean.enable -> cfg.enable;
+        message = "programs.nh.clean.enable requires programs.nh.enable";
+      }
+
+      {
+        assertion = (cfg.flake != null) -> !(lib.hasSuffix ".nix" cfg.flake);
+        message = "nh.flake must be a directory, not a nix file";
+      }
+    ];
+
+    environment = lib.mkIf cfg.enable {
+      systemPackages = [ cfg.package ];
+      variables = lib.mkIf (cfg.flake != null) {
+        FLAKE = cfg.flake;
+      };
+    };
+
+    systemd = lib.mkIf cfg.clean.enable {
+      services.nh-clean = {
+        description = "Nh clean";
+        script = "exec ${lib.getExe cfg.package} clean all ${cfg.clean.extraArgs}";
+        startAt = cfg.clean.dates;
+        path = [ config.nix.package ];
+        serviceConfig.Type = "oneshot";
+      };
+
+      timers.nh-clean = {
+        timerConfig = {
+          Persistent = true;
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/programs/slock.nix b/nixos/modules/programs/slock.nix
index ce80fcc5d4a8a..f39b4d5e9280e 100644
--- a/nixos/modules/programs/slock.nix
+++ b/nixos/modules/programs/slock.nix
@@ -16,16 +16,17 @@ in
           Whether to install slock screen locker with setuid wrapper.
         '';
       };
+      package = mkPackageOption pkgs "slock" {};
     };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.slock ];
+    environment.systemPackages = [ cfg.package ];
     security.wrappers.slock =
       { setuid = true;
         owner = "root";
         group = "root";
-        source = "${pkgs.slock.out}/bin/slock";
+        source = lib.getExe cfg.package;
       };
   };
 }
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index e691cd64497f0..9d453c5394824 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -682,7 +682,7 @@ in
 
             # TODO Add "instrument_queries" option when upgrading to grafana 10.0
             # instrument_queries = mkOption {
-            #   description = lib.mdDoc "Set to `true` to add metrics and tracing for database queries.";
+            #   description = "Set to `true` to add metrics and tracing for database queries.";
             #   default = false;
             #   type = types.bool;
             # };
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index 23bb92f09ab9a..b17416c1e3d34 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -152,9 +152,7 @@ let
   copyKeys = concatStrings (mapAttrsToList (keyName: keyOptions: ''
     secret=$(cat "${keyOptions.keyFile}")
     dest="${stateDir}/private/${keyName}"
-    echo "  secret: \"$secret\"" > "$dest"
-    chown ${username}:${username} "$dest"
-    chmod 0400 "$dest"
+    install -m 0400 -o "${username}" -g "${username}" <(echo "  secret: \"$secret\"") "$dest"
   '') cfg.keys);
 
 
@@ -457,9 +455,7 @@ let
   dnssecTools = pkgs.bind.override { enablePython = true; };
 
   signZones = optionalString dnssec ''
-    mkdir -p ${stateDir}/dnssec
-    chown ${username}:${username} ${stateDir}/dnssec
-    chmod 0600 ${stateDir}/dnssec
+    install -m 0600 -o "${username}" -g "${username}" -d "${stateDir}/dnssec"
 
     ${concatStrings (mapAttrsToList signZone dnssecZones)}
   '';
@@ -961,9 +957,9 @@ in
         rm -Rf "${stateDir}/private/"
         rm -Rf "${stateDir}/tmp/"
 
-        mkdir -m 0700 -p "${stateDir}/private"
-        mkdir -m 0700 -p "${stateDir}/tmp"
-        mkdir -m 0700 -p "${stateDir}/var"
+        install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/private"
+        install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/tmp"
+        install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/var"
 
         cat > "${stateDir}/don't touch anything in here" << EOF
         Everything in this directory except NSD's state in var and dnssec
@@ -971,10 +967,6 @@ in
         the nsd.service pre-start script.
         EOF
 
-        chown ${username}:${username} -R "${stateDir}/private"
-        chown ${username}:${username} -R "${stateDir}/tmp"
-        chown ${username}:${username} -R "${stateDir}/var"
-
         rm -rf "${stateDir}/zones"
         cp -rL "${nsdEnv}/zones" "${stateDir}/zones"
 
diff --git a/nixos/modules/services/web-apps/coder.nix b/nixos/modules/services/web-apps/coder.nix
index 64805fd75248b..318a7c8fc1357 100644
--- a/nixos/modules/services/web-apps/coder.nix
+++ b/nixos/modules/services/web-apps/coder.nix
@@ -72,6 +72,23 @@ in {
         example = "*.coder.example.com";
       };
 
+      environment = {
+        extra = mkOption {
+          type = types.attrs;
+          description = "Extra environment variables to pass run Coder's server with. See Coder documentation.";
+          default = {};
+          example = {
+            CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS = true;
+            CODER_OAUTH2_GITHUB_ALLOWED_ORGS = "your-org";
+          };
+        };
+        file = mkOption {
+          type = types.nullOr types.path;
+          description = "Systemd environment file to add to Coder.";
+          default = null;
+        };
+      };
+
       database = {
         createLocally = mkOption {
           type = types.bool;
@@ -152,7 +169,7 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      environment = {
+      environment = config.environment.extra // {
         CODER_ACCESS_URL = cfg.accessUrl;
         CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl;
         CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${optionalString (cfg.database.password != null) "password=${cfg.database.password}"} database=${cfg.database.database} host=${cfg.database.host} ${optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}"}";
@@ -177,6 +194,7 @@ in {
         ExecStart = "${cfg.package}/bin/coder server";
         User = cfg.user;
         Group = cfg.group;
+        EnvironmentFile = lib.mkIf (cfg.environment.file != null) cfg.environment.file;
       };
     };
 
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 0ddb7dcb651de..bb899c8d89994 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -186,6 +186,37 @@ let
         (assertNetdevMacAddress "MACAddress")
       ];
 
+      sectionBridge = checkUnitConfig "Bridge" [
+        (assertOnlyFields [
+          "HelloTimeSec"
+          "MaxAgeSec"
+          "ForwardDelaySec"
+          "AgeingTimeSec"
+          "Priority"
+          "GroupForwardMask"
+          "DefaultPVID"
+          "MulticastQuerier"
+          "MulticastSnooping"
+          "VLANFiltering"
+          "VLANProtocol"
+          "STP"
+          "MulticastIGMPVersion"
+        ])
+        (assertInt "HelloTimeSec")
+        (assertInt "MaxAgeSec")
+        (assertInt "ForwardDelaySec")
+        (assertInt "AgeingTimeSec")
+        (assertRange "Priority" 0 65535)
+        (assertRange "GroupForwardMask" 0 65535)
+        (assertRangeOrOneOf "DefaultPVID" 0 4094 ["none"])
+        (assertValueOneOf "MulticastQuerier" boolValues)
+        (assertValueOneOf "MulticastSnooping" boolValues)
+        (assertValueOneOf "VLANFiltering" boolValues)
+        (assertValueOneOf "VLANProtocol" ["802.1q" "802.ad"])
+        (assertValueOneOf "STP" boolValues)
+        (assertValueOneOf "MulticastIGMPVersion" [2 3])
+      ];
+
       sectionVLAN = checkUnitConfig "VLAN" [
         (assertOnlyFields [
           "Id"
@@ -1635,6 +1666,17 @@ let
       '';
     };
 
+    bridgeConfig = mkOption {
+      default = {};
+      example = { STP = true; };
+      type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionBridge;
+      description = ''
+        Each attribute in this set specifies an option in the
+        `[Bridge]` section of the unit.  See
+        {manpage}`systemd.netdev(5)` for details.
+      '';
+    };
+
     vlanConfig = mkOption {
       default = {};
       example = { Id = 4; };
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index fd5a0186bdecf..fe02a97d6ff1e 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -900,6 +900,7 @@ in {
   systemd-lock-handler = runTestOn ["aarch64-linux" "x86_64-linux"] ./systemd-lock-handler.nix;
   systemd-machinectl = handleTest ./systemd-machinectl.nix {};
   systemd-networkd = handleTest ./systemd-networkd.nix {};
+  systemd-networkd-bridge = handleTest ./systemd-networkd-bridge.nix {};
   systemd-networkd-dhcpserver = handleTest ./systemd-networkd-dhcpserver.nix {};
   systemd-networkd-dhcpserver-static-leases = handleTest ./systemd-networkd-dhcpserver-static-leases.nix {};
   systemd-networkd-ipv6-prefix-delegation = handleTest ./systemd-networkd-ipv6-prefix-delegation.nix {};
diff --git a/nixos/tests/systemd-networkd-bridge.nix b/nixos/tests/systemd-networkd-bridge.nix
new file mode 100644
index 0000000000000..f1f8823e84205
--- /dev/null
+++ b/nixos/tests/systemd-networkd-bridge.nix
@@ -0,0 +1,103 @@
+/* This test ensures that we can configure spanning-tree protocol
+   across bridges using systemd-networkd.
+
+   Test topology:
+
+              1       2       3
+       node1 --- sw1 --- sw2 --- node2
+                   \     /
+                  4 \   / 5
+                     sw3
+                      |
+                    6 |
+                      |
+                    node3
+
+   where switches 1, 2, and 3 bridge their links and use STP,
+   and each link is labeled with the VLAN we are assigning it in
+   virtualisation.vlans.
+*/
+with builtins;
+let
+  commonConf = {
+    systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
+    networking.useNetworkd = true;
+    networking.useDHCP = false;
+    networking.firewall.enable = false;
+  };
+
+  generateNodeConf = { octet, vlan }:
+    { lib, pkgs, config, ... }: {
+      imports = [ common/user-account.nix commonConf ];
+      virtualisation.vlans = [ vlan ];
+      systemd.network = {
+        enable = true;
+        networks = {
+          "30-eth" = {
+            matchConfig.Name = "eth1";
+            address = [ "10.0.0.${toString octet}/24" ];
+          };
+        };
+      };
+    };
+
+  generateSwitchConf = vlans:
+    { lib, pkgs, config, ... }: {
+      imports = [ common/user-account.nix commonConf ];
+      virtualisation.vlans = vlans;
+      systemd.network = {
+        enable = true;
+        netdevs = {
+          "40-br0" = {
+            netdevConfig = {
+              Kind = "bridge";
+              Name = "br0";
+            };
+            bridgeConfig.STP = "yes";
+          };
+        };
+        networks = {
+          "30-eth" = {
+            matchConfig.Name = "eth*";
+            networkConfig.Bridge = "br0";
+          };
+          "40-br0" = { matchConfig.Name = "br0"; };
+        };
+      };
+    };
+in import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "networkd";
+  meta = with pkgs.lib.maintainers; { maintainers = [ picnoir ]; };
+  nodes = {
+    node1 = generateNodeConf {
+      octet = 1;
+      vlan = 1;
+    };
+    node2 = generateNodeConf {
+      octet = 2;
+      vlan = 3;
+    };
+    node3 = generateNodeConf {
+      octet = 3;
+      vlan = 6;
+    };
+    sw1 = generateSwitchConf [ 1 2 4 ];
+    sw2 = generateSwitchConf [ 2 3 5 ];
+    sw3 = generateSwitchConf [ 4 5 6 ];
+  };
+  testScript = ''
+    network_nodes = [node1, node2, node3]
+    network_switches = [sw1, sw2, sw3]
+    start_all()
+
+    for n in network_nodes + network_switches:
+        n.wait_for_unit("systemd-networkd-wait-online.service")
+
+    node1.succeed("ping 10.0.0.2 -w 10 -c 1")
+    node1.succeed("ping 10.0.0.3 -w 10 -c 1")
+    node2.succeed("ping 10.0.0.1 -w 10 -c 1")
+    node2.succeed("ping 10.0.0.3 -w 10 -c 1")
+    node3.succeed("ping 10.0.0.1 -w 10 -c 1")
+    node3.succeed("ping 10.0.0.2 -w 10 -c 1")
+  '';
+})