diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 82cadca537f04..5d82fd196a60d 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -189,6 +189,11 @@ - The nvidia driver no longer defaults to the proprietary driver starting with version 560. You will need to manually set `hardware.nvidia.open` to select the proprietary or open driver. +- The dhcpcd service (`networking.useDHCP`) has been hardened and now runs exclusively as the "dhcpcd" user. + Users that were relying on the root privileges in `networking.dhcpcd.runHook` will have to write specific [sudo](security.sudo.extraRules) or [polkit](security.polkit.extraConfig) rules to allow dhcpcd to perform privileged actions. + + As part of these changes, the DHCP lease files directory has also been moved from `/var/db/dhcpcd` to `/var/lib/dhcpcd`. This migration is performed automatically, but users may have to update their backup configuration. + - `singularity-tools` have the `storeDir` argument removed from its override interface and use `builtins.storeDir` instead. - Two build helpers in `singularity-tools`, i.e., `mkLayer` and `shellScript`, are deprecated, as they are no longer involved in image-building. Maintainers will remove them in future releases. diff --git a/nixos/modules/config/resolvconf.nix b/nixos/modules/config/resolvconf.nix index 3609b1ba475b8..6ffcbcabe5192 100644 --- a/nixos/modules/config/resolvconf.nix +++ b/nixos/modules/config/resolvconf.nix @@ -132,6 +132,8 @@ in } (lib.mkIf cfg.enable { + users.groups.resolvconf = {}; + networking.resolvconf.package = pkgs.openresolv; environment.systemPackages = [ cfg.package ]; @@ -143,12 +145,13 @@ in wants = [ "network-pre.target" ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."resolvconf.conf".source ]; + serviceConfig.RemainAfterExit = true; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${cfg.package}/bin/resolvconf -u"; - RemainAfterExit = true; - }; + script = '' + ${lib.getExe cfg.package} -u + chgrp -R resolvconf /etc/resolv.conf /run/resolvconf + chmod -R g=u /etc/resolv.conf /run/resolvconf + ''; }; }) diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index 9b3269e965f5b..39c8930eea133 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -10,7 +10,7 @@ let enableDHCP = config.networking.dhcpcd.enable && (config.networking.useDHCP || lib.any (i: i.useDHCP == true) interfaces); - enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable); + useResolvConf = config.networking.resolvconf.enable; # Don't start dhcpcd on explicitly configured interfaces or on # interfaces that are part of a bridge, bond or sit device. @@ -88,23 +88,6 @@ let ${cfg.extraConfig} ''; - exitHook = pkgs.writeText "dhcpcd.exit-hook" '' - ${lib.optionalString enableNTPService '' - if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then - # Restart ntpd. We need to restart it to make sure that it will actually do something: - # if ntpd cannot resolve the server hostnames in its config file, then it will never do - # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose - # time synchronisation. This also applies to openntpd. - ${lib.optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"} - ${lib.optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"} - ${lib.optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"} - ${lib.optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"} - fi - ''} - - ${cfg.runHook} - ''; - in { @@ -181,6 +164,19 @@ in description = '' Shell code that will be run after all other hooks. See `man dhcpcd-run-hooks` for details on what is possible. + + ::: {.note} + To use sudo or similar tools in your script you may have to set: + + systemd.services.dhcpcd.serviceConfig.NoNewPrivileges = false; + + In addition, as most of the filesystem is inaccessible to dhcpcd + by default, you may want to define some exceptions, e.g. + + systemd.services.dhcpcd.serviceConfig.ReadOnlyPaths = [ + "/run/user/1000/bus" # to send desktop notifications + ]; + ::: ''; }; @@ -206,22 +202,6 @@ in config = lib.mkIf enableDHCP { - assertions = [ { - # dhcpcd doesn't start properly with malloc ∉ [ libc scudo ] - # see https://github.com/NixOS/nixpkgs/issues/151696 - assertion = - dhcpcd.enablePrivSep - -> lib.elem config.environment.memoryAllocator.provider [ "libc" "scudo" ]; - message = '' - dhcpcd with privilege separation is incompatible with chosen system malloc. - Currently only the `libc` and `scudo` allocators are known to work. - To disable dhcpcd's privilege separation, overlay Nixpkgs and override dhcpcd - to set `enablePrivSep = false`. - ''; - } ]; - - environment.etc."dhcpcd.conf".source = dhcpcdConf; - systemd.services.dhcpcd = let cfgN = config.networking; hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") @@ -233,7 +213,7 @@ in wants = [ "network.target" ]; before = [ "network-online.target" ]; - restartTriggers = lib.optional (enableNTPService || cfg.runHook != "") [ exitHook ]; + restartTriggers = [ cfg.runHook ]; # Stopping dhcpcd during a reconfiguration is undesirable # because it brings down the network interfaces configured by @@ -247,13 +227,62 @@ in serviceConfig = { Type = "forking"; PIDFile = "/run/dhcpcd/pid"; + SupplementaryGroups = lib.optional useResolvConf "resolvconf"; + User = "dhcpcd"; + Group = "dhcpcd"; + StateDirectory = "dhcpcd"; RuntimeDirectory = "dhcpcd"; + + ExecStartPre = "+${pkgs.writeShellScript "migrate-dhcpcd" '' + # migrate from old database directory + if test -f /var/db/dhcpcd/duid; then + echo 'migrating DHCP leases from /var/db/dhcpcd to /var/lib/dhcpcd ...' + mv /var/db/dhcpcd/* -t /var/lib/dhcpcd + chown dhcpcd:dhcpcd /var/lib/dhcpcd/* + rmdir /var/db/dhcpcd || true + echo done + fi + ''}"; + ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${lib.optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; Restart = "always"; + AmbientCapabilities = [ "CAP_NET_ADMIN" "CAP_NET_RAW" "CAP_NET_BIND_SERVICE" ]; + ReadWritePaths = [ "/proc/sys/net/ipv6" ] + ++ lib.optionals useResolvConf [ "/etc/resolv.conf" "/run/resolvconf" ]; + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = lib.mkDefault true; # may be disabled for sudo in runHook + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = false; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = "tmpfs"; # allow exceptions to be added to ReadOnlyPaths, etc. + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" "AF_PACKET" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = [ + "@system-service" + "~@aio" "~@chown" "~@keyring" "~@memlock" + ]; + SystemCallArchitectures = "native"; }; }; + # Note: the service could run with `DynamicUser`, however that makes + # impossible (for no good reason, see systemd issue #20495) to disable + # `NoNewPrivileges` or `ProtectHome`, which users may want to in order + # to run certain scripts in `networking.dhcpcd.runHook`. users.users.dhcpcd = { isSystemUser = true; group = "dhcpcd"; @@ -262,9 +291,7 @@ in environment.systemPackages = [ dhcpcd ]; - environment.etc."dhcpcd.exit-hook" = lib.mkIf (enableNTPService || cfg.runHook != "") { - source = exitHook; - }; + environment.etc."dhcpcd.exit-hook".text = cfg.runHook; powerManagement.resumeCommands = lib.mkIf config.systemd.services.dhcpcd.enable '' diff --git a/nixos/tests/chrony.nix b/nixos/tests/chrony.nix index 2dcc363728beb..9582ab14bb8f1 100644 --- a/nixos/tests/chrony.nix +++ b/nixos/tests/chrony.nix @@ -13,8 +13,6 @@ import ./make-test-python.nix ({ lib, ... }: specialisation.hardened.configuration = { services.chrony.enableMemoryLocking = true; environment.memoryAllocator.provider = "graphene-hardened"; - # dhcpcd privsep is incompatible with graphene-hardened - networking.useNetworkd = true; }; }; }; diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix index e38834961e13a..0c43e3523dacf 100644 --- a/nixos/tests/hardened.nix +++ b/nixos/tests/hardened.nix @@ -11,11 +11,6 @@ import ./make-test-python.nix ({ pkgs, ... } : { imports = [ ../modules/profiles/hardened.nix ]; environment.memoryAllocator.provider = "graphene-hardened"; nix.settings.sandbox = false; - nixpkgs.overlays = [ - (final: super: { - dhcpcd = super.dhcpcd.override { enablePrivSep = false; }; - }) - ]; virtualisation.emptyDiskImages = [ 4096 ]; boot.initrd.postDeviceCommands = '' ${pkgs.dosfstools}/bin/mkfs.vfat -n EFISYS /dev/vdb diff --git a/nixos/tests/networking/networkd-and-scripted.nix b/nixos/tests/networking/networkd-and-scripted.nix index 777c00f74e228..6b8ed50a2f19e 100644 --- a/nixos/tests/networking/networkd-and-scripted.nix +++ b/nixos/tests/networking/networkd-and-scripted.nix @@ -132,6 +132,10 @@ let client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q '192.168.2'") client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q 'fd00:1234:5678:2:'") + with subtest("Wait until we have received the nameservers"): + client.wait_until_succeeds("grep -q 2001:db8::1 /etc/resolv.conf") + client.wait_until_succeeds("grep -q 192.168.2.1 /etc/resolv.conf") + with subtest("Test vlan 1"): client.wait_until_succeeds("ping -c 1 192.168.1.1") client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") diff --git a/nixos/tests/networking/networkmanager.nix b/nixos/tests/networking/networkmanager.nix index bd989408df8a1..70d28fe96380b 100644 --- a/nixos/tests/networking/networkmanager.nix +++ b/nixos/tests/networking/networkmanager.nix @@ -121,6 +121,7 @@ let static.wait_for_unit("NetworkManager.service") dynamic.wait_until_succeeds("cat /etc/resolv.conf | grep -q '192.168.1.1'") + dynamic.wait_until_succeeds("cat /etc/resolv.conf | grep -q '2001:db8::1'") static.wait_until_succeeds("cat /etc/resolv.conf | grep -q '10.10.10.10'") static.wait_until_fails("cat /etc/resolv.conf | grep -q '192.168.1.1'") ''; diff --git a/nixos/tests/networking/router.nix b/nixos/tests/networking/router.nix index e0ad7fa01591a..fab21c9e78624 100644 --- a/nixos/tests/networking/router.nix +++ b/nixos/tests/networking/router.nix @@ -72,6 +72,7 @@ AdvSendAdvert on; AdvManagedFlag on; AdvOtherConfigFlag on; + RDNSS 2001:db8::1 {}; prefix fd00:1234:5678:${toString n}::/64 { AdvAutonomous off; diff --git a/pkgs/tools/networking/dhcpcd/default.nix b/pkgs/tools/networking/dhcpcd/default.nix index 6221034dc2857..324ac174513b0 100644 --- a/pkgs/tools/networking/dhcpcd/default.nix +++ b/pkgs/tools/networking/dhcpcd/default.nix @@ -7,7 +7,6 @@ , runtimeShellPackage , runtimeShell , nixosTests -, enablePrivSep ? true }: stdenv.mkDerivation rec { @@ -38,30 +37,21 @@ stdenv.mkDerivation rec { configureFlags = [ "--sysconfdir=/etc" "--localstatedir=/var" - ] - ++ ( - if ! enablePrivSep - then [ "--disable-privsep" ] - else [ - "--enable-privsep" - # dhcpcd disables privsep if it can't find the default user, - # so we explicitly specify a user. - "--privsepuser=dhcpcd" - ] - ); + "--disable-privsep" + "--dbdir=/var/lib/dhcpcd" + ]; makeFlags = [ "PREFIX=${placeholder "out"}" ]; - # Hack to make installation succeed. dhcpcd will still use /var/db + # Hack to make installation succeed. dhcpcd will still use /var/lib # at runtime. installFlags = [ "DBDIR=$(TMPDIR)/db" "SYSCONFDIR=${placeholder "out"}/etc" ]; # Check that the udev plugin got built. postInstall = lib.optionalString (udev != null && stdenv.isLinux) "[ -e ${placeholder "out"}/lib/dhcpcd/dev/udev.so ]"; - passthru = { - inherit enablePrivSep; - tests = { inherit (nixosTests.networking.scripted) macvlan dhcpSimple dhcpOneIf; }; + passthru.tests = { + inherit (nixosTests.networking.scripted) macvlan dhcpSimple dhcpOneIf; }; meta = with lib; {