nixos/dhcpcd: fix hostname via DHCP (#385348)

authored by Michele Guerini Rocco and committed by GitHub 377ae8a7 4a6660ea

+104 -31
+74 -30
nixos/modules/services/networking/dhcpcd.nix
··· 69 69 hostname 70 70 71 71 # A list of options to request from the DHCP server. 72 - option domain_name_servers, domain_name, domain_search, host_name 72 + option domain_name_servers, domain_name, domain_search 73 73 option classless_static_routes, ntp_servers, interface_mtu 74 74 75 75 # A ServerID is required by RFC2131. ··· 112 112 ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) '' 113 113 noipv6rs 114 114 ''} 115 + ${lib.optionalString cfg.setHostname "option host_name"} 115 116 116 117 ${cfg.extraConfig} 117 118 ''; ··· 137 138 type = lib.types.bool; 138 139 default = false; 139 140 description = '' 140 - Whenever to leave interfaces configured on dhcpcd daemon 141 + Whether to leave interfaces configured on dhcpcd daemon 141 142 shutdown. Set to true if you have your root or store mounted 142 143 over the network or this machine accepts SSH connections 143 144 through DHCP interfaces and clients should be notified when ··· 145 146 ''; 146 147 }; 147 148 149 + networking.dhcpcd.setHostname = lib.mkOption { 150 + type = lib.types.bool; 151 + default = true; 152 + description = '' 153 + Whether to set the machine hostname based on the information 154 + received from the DHCP server. 155 + 156 + ::: {.note} 157 + The hostname will be changed only if the current one is 158 + the empty string, `localhost` or `nixos`. 159 + 160 + Polkit ([](#opt-security.polkit.enable)) is also required. 161 + ::: 162 + ''; 163 + }; 164 + 148 165 networking.dhcpcd.denyInterfaces = lib.mkOption { 149 166 type = lib.types.listOf lib.types.str; 150 167 default = [ ]; ··· 185 202 ''; 186 203 }; 187 204 205 + networking.dhcpcd.allowSetuid = lib.mkOption { 206 + type = lib.types.bool; 207 + default = false; 208 + description = '' 209 + Whether to relax the security sandbox to allow running setuid 210 + binaries (e.g. `sudo`) in the dhcpcd hooks. 211 + ''; 212 + }; 213 + 188 214 networking.dhcpcd.runHook = lib.mkOption { 189 215 type = lib.types.lines; 190 216 default = ""; ··· 196 222 ::: {.note} 197 223 To use sudo or similar tools in your script you may have to set: 198 224 199 - systemd.services.dhcpcd.serviceConfig.NoNewPrivileges = false; 225 + networking.dhcpcd.allowSetuid = true; 200 226 201 227 In addition, as most of the filesystem is inaccessible to dhcpcd 202 228 by default, you may want to define some exceptions, e.g. ··· 263 289 # dhcpcd. So do a "systemctl restart" instead. 264 290 stopIfChanged = false; 265 291 266 - path = [ 267 - dhcpcd 268 - pkgs.nettools 269 - config.networking.resolvconf.package 270 - ]; 292 + path = 293 + [ 294 + dhcpcd 295 + config.networking.resolvconf.package 296 + ] 297 + ++ lib.optional cfg.setHostname ( 298 + pkgs.writeShellScriptBin "hostname" '' 299 + ${lib.getExe' pkgs.systemd "hostnamectl"} set-hostname --transient $1 300 + '' 301 + ); 271 302 272 303 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 273 304 ··· 299 330 "CAP_NET_RAW" 300 331 "CAP_NET_BIND_SERVICE" 301 332 ]; 302 - CapabilityBoundingSet = [ 333 + CapabilityBoundingSet = lib.optionals (!cfg.allowSetuid) [ 303 334 "CAP_NET_ADMIN" 304 335 "CAP_NET_RAW" 305 336 "CAP_NET_BIND_SERVICE" ··· 313 344 DeviceAllow = ""; 314 345 LockPersonality = true; 315 346 MemoryDenyWriteExecute = true; 316 - NoNewPrivileges = lib.mkDefault true; # may be disabled for sudo in runHook 347 + NoNewPrivileges = lib.mkDefault (!cfg.allowSetuid); # may be disabled for sudo in runHook 317 348 PrivateDevices = true; 318 349 PrivateMounts = true; 319 350 PrivateTmp = true; ··· 338 369 RestrictNamespaces = true; 339 370 RestrictRealtime = true; 340 371 RestrictSUIDSGID = true; 341 - SystemCallFilter = [ 342 - "@system-service" 343 - "~@aio" 344 - "~@keyring" 345 - "~@memlock" 346 - "~@mount" 347 - "~@privileged" 348 - "~@resources" 349 - ]; 372 + SystemCallFilter = 373 + [ 374 + "@system-service" 375 + "~@aio" 376 + "~@keyring" 377 + "~@memlock" 378 + "~@mount" 379 + ] 380 + ++ lib.optionals (!cfg.allowSetuid) [ 381 + "~@privileged" 382 + "~@resources" 383 + ]; 350 384 SystemCallArchitectures = "native"; 351 385 UMask = "0027"; 352 386 }; ··· 371 405 /run/current-system/systemd/bin/systemctl reload dhcpcd.service 372 406 ''; 373 407 374 - security.polkit.extraConfig = lib.mkIf config.services.resolved.enable '' 375 - polkit.addRule(function(action, subject) { 376 - if (action.id == 'org.freedesktop.resolve1.revert' || 377 - action.id == 'org.freedesktop.resolve1.set-dns-servers' || 378 - action.id == 'org.freedesktop.resolve1.set-domains') { 379 - if (subject.user == '${config.systemd.services.dhcpcd.serviceConfig.User}') { 380 - return polkit.Result.YES; 381 - } 382 - } 383 - }); 384 - ''; 408 + security.polkit.extraConfig = lib.mkMerge [ 409 + (lib.mkIf config.services.resolved.enable '' 410 + polkit.addRule(function(action, subject) { 411 + if (action.id == 'org.freedesktop.resolve1.revert' || 412 + action.id == 'org.freedesktop.resolve1.set-dns-servers' || 413 + action.id == 'org.freedesktop.resolve1.set-domains') { 414 + if (subject.user == 'dhcpcd') { 415 + return polkit.Result.YES; 416 + } 417 + } 418 + }); 419 + '') 420 + (lib.mkIf cfg.setHostname '' 421 + polkit.addRule(function(action, subject) { 422 + if (action.id == 'org.freedesktop.hostname1.set-hostname' && 423 + subject.user == 'dhcpcd') { 424 + return polkit.Result.YES; 425 + } 426 + }); 427 + '') 428 + ]; 385 429 386 430 }; 387 431
+23
nixos/tests/networking/networkd-and-scripted.nix
··· 178 178 router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2") 179 179 ''; 180 180 }; 181 + dhcpHostname = { 182 + name = "hostnameDHCP"; 183 + nodes.router = router; 184 + nodes.client = clientConfig { 185 + # use the name given by the DHCP server 186 + system.name = "client"; 187 + networking.hostName = lib.mkForce ""; 188 + security.polkit.enable = true; 189 + virtualisation.interfaces.enp1s0.vlan = 1; 190 + networking.interfaces.enp1s0.useDHCP = true; 191 + }; 192 + testScript = '' 193 + router.start() 194 + router.systemctl("start network-online.target") 195 + router.wait_for_unit("network-online.target") 196 + 197 + client.start() 198 + client.wait_for_unit("network.target") 199 + 200 + with subtest("Wait until we have received the hostname"): 201 + client.wait_until_succeeds("hostname | grep -q 'client1'") 202 + ''; 203 + }; 181 204 dhcpOneIf = { 182 205 name = "OneInterfaceDHCP"; 183 206 nodes.router = router;
+7 -1
pkgs/by-name/dh/dhcpcd/package.nix
··· 44 44 "--localstatedir=/var" 45 45 "--disable-privsep" 46 46 "--dbdir=/var/lib/dhcpcd" 47 + "--with-default-hostname=nixos" 47 48 (lib.enableFeature enablePrivSep "privsep") 48 49 ] ++ lib.optional enablePrivSep "--privsepuser=dhcpcd"; 49 50 ··· 62 63 ) "[ -e ${placeholder "out"}/lib/dhcpcd/dev/udev.so ]"; 63 64 64 65 passthru.tests = { 65 - inherit (nixosTests.networking.scripted) macvlan dhcpSimple dhcpOneIf; 66 + inherit (nixosTests.networking.scripted) 67 + macvlan 68 + dhcpSimple 69 + dhcpHostname 70 + dhcpOneIf 71 + ; 66 72 }; 67 73 68 74 meta = with lib; {