diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index 308ae1278a1b..6aecbec7bd4a 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -69,7 +69,7 @@ let hostname # A list of options to request from the DHCP server. - option domain_name_servers, domain_name, domain_search, host_name + option domain_name_servers, domain_name, domain_search option classless_static_routes, ntp_servers, interface_mtu # A ServerID is required by RFC2131. @@ -112,6 +112,7 @@ let ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) '' noipv6rs ''} + ${lib.optionalString cfg.setHostname "option host_name"} ${cfg.extraConfig} ''; @@ -137,7 +138,7 @@ in type = lib.types.bool; default = false; description = '' - Whenever to leave interfaces configured on dhcpcd daemon + Whether to leave interfaces configured on dhcpcd daemon shutdown. Set to true if you have your root or store mounted over the network or this machine accepts SSH connections through DHCP interfaces and clients should be notified when @@ -145,6 +146,22 @@ in ''; }; + networking.dhcpcd.setHostname = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to set the machine hostname based on the information + received from the DHCP server. + + ::: {.note} + The hostname will be changed only if the current one is + the empty string, `localhost` or `nixos`. + + Polkit ([](#opt-security.polkit.enable)) is also required. + ::: + ''; + }; + networking.dhcpcd.denyInterfaces = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; @@ -185,6 +202,15 @@ in ''; }; + networking.dhcpcd.allowSetuid = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to relax the security sandbox to allow running setuid + binaries (e.g. `sudo`) in the dhcpcd hooks. + ''; + }; + networking.dhcpcd.runHook = lib.mkOption { type = lib.types.lines; default = ""; @@ -196,7 +222,7 @@ in ::: {.note} To use sudo or similar tools in your script you may have to set: - systemd.services.dhcpcd.serviceConfig.NoNewPrivileges = false; + networking.dhcpcd.allowSetuid = true; In addition, as most of the filesystem is inaccessible to dhcpcd by default, you may want to define some exceptions, e.g. @@ -263,11 +289,16 @@ in # dhcpcd. So do a "systemctl restart" instead. stopIfChanged = false; - path = [ - dhcpcd - pkgs.nettools - config.networking.resolvconf.package - ]; + path = + [ + dhcpcd + config.networking.resolvconf.package + ] + ++ lib.optional cfg.setHostname ( + pkgs.writeShellScriptBin "hostname" '' + ${lib.getExe' pkgs.systemd "hostnamectl"} set-hostname --transient $1 + '' + ); unitConfig.ConditionCapability = "CAP_NET_ADMIN"; @@ -299,7 +330,7 @@ in "CAP_NET_RAW" "CAP_NET_BIND_SERVICE" ]; - CapabilityBoundingSet = [ + CapabilityBoundingSet = lib.optionals (!cfg.allowSetuid) [ "CAP_NET_ADMIN" "CAP_NET_RAW" "CAP_NET_BIND_SERVICE" @@ -313,7 +344,7 @@ in DeviceAllow = ""; LockPersonality = true; MemoryDenyWriteExecute = true; - NoNewPrivileges = lib.mkDefault true; # may be disabled for sudo in runHook + NoNewPrivileges = lib.mkDefault (!cfg.allowSetuid); # may be disabled for sudo in runHook PrivateDevices = true; PrivateMounts = true; PrivateTmp = true; @@ -338,15 +369,18 @@ in RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; - SystemCallFilter = [ - "@system-service" - "~@aio" - "~@keyring" - "~@memlock" - "~@mount" - "~@privileged" - "~@resources" - ]; + SystemCallFilter = + [ + "@system-service" + "~@aio" + "~@keyring" + "~@memlock" + "~@mount" + ] + ++ lib.optionals (!cfg.allowSetuid) [ + "~@privileged" + "~@resources" + ]; SystemCallArchitectures = "native"; UMask = "0027"; }; @@ -371,17 +405,27 @@ in /run/current-system/systemd/bin/systemctl reload dhcpcd.service ''; - security.polkit.extraConfig = lib.mkIf config.services.resolved.enable '' - polkit.addRule(function(action, subject) { - if (action.id == 'org.freedesktop.resolve1.revert' || - action.id == 'org.freedesktop.resolve1.set-dns-servers' || - action.id == 'org.freedesktop.resolve1.set-domains') { - if (subject.user == '${config.systemd.services.dhcpcd.serviceConfig.User}') { - return polkit.Result.YES; - } - } - }); - ''; + security.polkit.extraConfig = lib.mkMerge [ + (lib.mkIf config.services.resolved.enable '' + polkit.addRule(function(action, subject) { + if (action.id == 'org.freedesktop.resolve1.revert' || + action.id == 'org.freedesktop.resolve1.set-dns-servers' || + action.id == 'org.freedesktop.resolve1.set-domains') { + if (subject.user == 'dhcpcd') { + return polkit.Result.YES; + } + } + }); + '') + (lib.mkIf cfg.setHostname '' + polkit.addRule(function(action, subject) { + if (action.id == 'org.freedesktop.hostname1.set-hostname' && + subject.user == 'dhcpcd') { + return polkit.Result.YES; + } + }); + '') + ]; }; diff --git a/nixos/tests/networking/networkd-and-scripted.nix b/nixos/tests/networking/networkd-and-scripted.nix index f2211738db2b..312981317735 100644 --- a/nixos/tests/networking/networkd-and-scripted.nix +++ b/nixos/tests/networking/networkd-and-scripted.nix @@ -178,6 +178,29 @@ let router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2") ''; }; + dhcpHostname = { + name = "hostnameDHCP"; + nodes.router = router; + nodes.client = clientConfig { + # use the name given by the DHCP server + system.name = "client"; + networking.hostName = lib.mkForce ""; + security.polkit.enable = true; + virtualisation.interfaces.enp1s0.vlan = 1; + networking.interfaces.enp1s0.useDHCP = true; + }; + testScript = '' + router.start() + router.systemctl("start network-online.target") + router.wait_for_unit("network-online.target") + + client.start() + client.wait_for_unit("network.target") + + with subtest("Wait until we have received the hostname"): + client.wait_until_succeeds("hostname | grep -q 'client1'") + ''; + }; dhcpOneIf = { name = "OneInterfaceDHCP"; nodes.router = router; diff --git a/pkgs/by-name/dh/dhcpcd/package.nix b/pkgs/by-name/dh/dhcpcd/package.nix index 7394fea8f83f..b6b814248050 100644 --- a/pkgs/by-name/dh/dhcpcd/package.nix +++ b/pkgs/by-name/dh/dhcpcd/package.nix @@ -44,6 +44,7 @@ stdenv.mkDerivation rec { "--localstatedir=/var" "--disable-privsep" "--dbdir=/var/lib/dhcpcd" + "--with-default-hostname=nixos" (lib.enableFeature enablePrivSep "privsep") ] ++ lib.optional enablePrivSep "--privsepuser=dhcpcd"; @@ -62,7 +63,12 @@ stdenv.mkDerivation rec { ) "[ -e ${placeholder "out"}/lib/dhcpcd/dev/udev.so ]"; passthru.tests = { - inherit (nixosTests.networking.scripted) macvlan dhcpSimple dhcpOneIf; + inherit (nixosTests.networking.scripted) + macvlan + dhcpSimple + dhcpHostname + dhcpOneIf + ; }; meta = with lib; {