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

This commit is contained in:
Michele Guerini Rocco
2025-05-06 03:19:20 +02:00
committed by GitHub
3 changed files with 104 additions and 31 deletions

View File

@@ -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;
}
});
'')
];
};

View File

@@ -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;

View File

@@ -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; {