summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorMichele Guerini Rocco <rnhmjoj@users.noreply.github.com>2022-01-31 10:06:57 +0100
committerGitHub <noreply@github.com>2022-01-31 10:06:57 +0100
commit09e29560121f00cc238a4ea4960e3a9d74e68afe (patch)
tree0cc57a81fb7b8dd373a143de57a285e4541b4d7b /nixos
parentb48539da9a48b66f6bbb0c668b81e0b55f602416 (diff)
parent79b4b7eaa1a00424cbf63305516e92c8dd055c94 (diff)
downloadnixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar.gz
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar.bz2
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar.lz
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar.xz
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.tar.zst
nixpkgs-09e29560121f00cc238a4ea4960e3a9d74e68afe.zip
Merge pull request #155895 from rnhmjoj/pr-dhcpd-hard
nixos/dhcpd: switch to DynamicUser [v2]
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml17
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md5
-rw-r--r--nixos/modules/services/networking/dhcpd.nix95
-rw-r--r--nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix19
4 files changed, 72 insertions, 64 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 41a84ed0221..1811c5ec561 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -237,6 +237,23 @@
       </listitem>
       <listitem>
         <para>
+          The DHCP server (<literal>services.dhcpd4</literal>,
+          <literal>services.dhcpd6</literal>) has been hardened. The
+          service is now using the systemd’s
+          <literal>DynamicUser</literal> mechanism to run as an
+          unprivileged dynamically-allocated user with limited
+          capabilities. The dhcpd state files are now always stored in
+          <literal>/var/lib/dhcpd{4,6}</literal> and the
+          <literal>services.dhcpd4.stateDir</literal> and
+          <literal>service.dhcpd6.stateDir</literal> options have been
+          removed. If you were depending on root privileges or
+          set{uid,gid,cap} binaries in dhcpd shell hooks, you may give
+          dhcpd more capabilities with e.g.
+          <literal>systemd.services.dhcpd6.serviceConfig.AmbientCapabilities</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>mailpile</literal> email webclient
           (<literal>services.mailpile</literal>) has been removed due to
           its reliance on python2.
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 50099671f62..f22204801b8 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -81,6 +81,11 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `services.kubernetes.addons.dashboard` was removed due to it being an outdated version.
 
+- The DHCP server (`services.dhcpd4`, `services.dhcpd6`) has been hardened.
+  The service is now using the systemd's `DynamicUser` mechanism to run as an unprivileged dynamically-allocated user with limited capabilities.
+  The dhcpd state files are now always stored in `/var/lib/dhcpd{4,6}` and the `services.dhcpd4.stateDir` and `service.dhcpd6.stateDir` options have been removed.
+  If you were depending on root privileges or set{uid,gid,cap} binaries in dhcpd shell hooks, you may give dhcpd more capabilities with e.g. `systemd.services.dhcpd6.serviceConfig.AmbientCapabilities`.
+
 - The `mailpile` email webclient (`services.mailpile`) has been removed due to its reliance on python2.
 
 - The MoinMoin wiki engine (`services.moinmoin`) has been removed, because Python 2 is being retired from nixpkgs.
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index 54e4f900285..3c4c0069dfd 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -28,38 +28,45 @@ let
       }
     '';
 
-  dhcpdService = postfix: cfg: optionalAttrs cfg.enable {
-    "dhcpd${postfix}" = {
-      description = "DHCPv${postfix} server";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-
-      preStart = ''
-        mkdir -m 755 -p ${cfg.stateDir}
-        chown dhcpd:nogroup ${cfg.stateDir}
-        touch ${cfg.stateDir}/dhcpd.leases
-      '';
-
-      serviceConfig =
-        let
-          configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg;
-          args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}"
-                   "-pf" "/run/dhcpd${postfix}/dhcpd.pid"
-                   "-cf" "${configFile}"
-                   "-lf" "${cfg.stateDir}/dhcpd.leases"
-                   "-user" "dhcpd" "-group" "nogroup"
-                 ] ++ cfg.extraFlags
-                   ++ cfg.interfaces;
-
-        in {
-          ExecStart = concatMapStringsSep " " escapeShellArg args;
-          Type = "forking";
-          Restart = "always";
-          RuntimeDirectory = [ "dhcpd${postfix}" ];
-          PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
+  dhcpdService = postfix: cfg:
+    let
+      configFile =
+        if cfg.configFile != null
+          then cfg.configFile
+          else writeConfig cfg;
+      leaseFile = "/var/lib/dhcpd${postfix}/dhcpd.leases";
+      args = [
+        "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}"
+        "-pf" "/run/dhcpd${postfix}/dhcpd.pid"
+        "-cf" configFile
+        "-lf" leaseFile
+      ] ++ cfg.extraFlags
+        ++ cfg.interfaces;
+    in
+      optionalAttrs cfg.enable {
+        "dhcpd${postfix}" = {
+          description = "DHCPv${postfix} server";
+          wantedBy = [ "multi-user.target" ];
+          after = [ "network.target" ];
+
+          preStart = "touch ${leaseFile}";
+          serviceConfig = {
+            ExecStart = concatMapStringsSep " " escapeShellArg args;
+            Type = "forking";
+            Restart = "always";
+            DynamicUser = true;
+            User = "dhcpd";
+            Group = "dhcpd";
+            AmbientCapabilities = [
+              "CAP_NET_RAW"          # to send ICMP messages
+              "CAP_NET_BIND_SERVICE" # to bind on DHCP port (67)
+            ];
+            StateDirectory   = "dhcpd${postfix}";
+            RuntimeDirectory = "dhcpd${postfix}";
+            PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
+          };
         };
-    };
-  };
+      };
 
   machineOpts = { ... }: {
 
@@ -102,15 +109,6 @@ let
       '';
     };
 
-    stateDir = mkOption {
-      type = types.path;
-      # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility.
-      default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}";
-      description = ''
-        State directory for the DHCP server.
-      '';
-    };
-
     extraConfig = mkOption {
       type = types.lines;
       default = "";
@@ -194,7 +192,13 @@ in
 
   imports = [
     (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ])
-  ];
+  ] ++ flip map [ "4" "6" ] (postfix:
+    mkRemovedOptionModule [ "services" "dhcpd${postfix}" "stateDir" ] ''
+      The DHCP server state directory is now managed with the systemd's DynamicUser mechanism.
+      This means the directory is named after the service (dhcpd${postfix}), created under
+      /var/lib/private/ and symlinked to /var/lib/.
+    ''
+  );
 
   ###### interface
 
@@ -210,15 +214,6 @@ in
 
   config = mkIf (cfg4.enable || cfg6.enable) {
 
-    users = {
-      users.dhcpd = {
-        isSystemUser = true;
-        group = "dhcpd";
-        description = "DHCP daemon user";
-      };
-      groups.dhcpd = {};
-    };
-
     systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
 
   };
diff --git a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
index 68836c73072..37a89fc21e4 100644
--- a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
+++ b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
@@ -36,19 +36,10 @@ import ./make-test-python.nix ({pkgs, ...}: {
       };
 
       # Since we want to program the routes that we delegate to the "customer"
-      # into our routing table we must have a way to gain the required privs.
-      # This security wrapper will do in our test setup.
-      #
-      # DO NOT COPY THIS TO PRODUCTION AS IS. Think about it at least twice.
-      # Everyone on the "isp" machine will be able to add routes to the kernel.
-      security.wrappers.add-dhcpd-lease = {
-        owner = "root";
-        group = "root";
-        source = pkgs.writeShellScript "add-dhcpd-lease" ''
-          exec ${pkgs.iproute2}/bin/ip -6 route replace "$1" via "$2"
-        '';
-        capabilities = "cap_net_admin+ep";
-      };
+      # into our routing table we must give dhcpd the required privs.
+      systemd.services.dhcpd6.serviceConfig.AmbientCapabilities =
+        [ "CAP_NET_ADMIN" ];
+
       services = {
         # Configure the DHCPv6 server
         #
@@ -80,7 +71,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
               set Prefix = pick-first-value(binary-to-ascii(16, 16, ":", suffix(option dhcp6.ia-pd, 16)), "n/a");
               set PrefixLength = pick-first-value(binary-to-ascii(10, 8, ":", substring(suffix(option dhcp6.ia-pd, 17), 0, 1)), "n/a");
               log(concat(IP, " ", Prefix, " ", PrefixLength));
-              execute("/run/wrappers/bin/add-dhcpd-lease", concat(Prefix,"/",PrefixLength), IP);
+              execute("${pkgs.iproute2}/bin/ip", "-6", "route", "replace", concat(Prefix,"/",PrefixLength), "via", IP);
             }
           '';
         };