Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 8 additions & 5 deletions nixos/modules/config/resolvconf.nix
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ in
}

(lib.mkIf cfg.enable {
users.groups.resolvconf = {};

networking.resolvconf.package = pkgs.openresolv;

environment.systemPackages = [ cfg.package ];
Expand All @@ -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
'';
};

})
Expand Down
103 changes: 65 additions & 38 deletions nixos/modules/services/networking/dhcpcd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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

{
Expand Down Expand Up @@ -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
];
:::
'';
};

Expand All @@ -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 != "")
Expand All @@ -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
Expand All @@ -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";
Expand All @@ -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
''
Expand Down
2 changes: 0 additions & 2 deletions nixos/tests/chrony.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};
};
Expand Down
5 changes: 0 additions & 5 deletions nixos/tests/hardened.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions nixos/tests/networking/networkd-and-scripted.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions nixos/tests/networking/networkmanager.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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'")
'';
Expand Down
1 change: 1 addition & 0 deletions nixos/tests/networking/router.nix
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
AdvSendAdvert on;
AdvManagedFlag on;
AdvOtherConfigFlag on;
RDNSS 2001:db8::1 {};

prefix fd00:1234:5678:${toString n}::/64 {
AdvAutonomous off;
Expand Down
22 changes: 6 additions & 16 deletions pkgs/tools/networking/dhcpcd/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
, runtimeShellPackage
, runtimeShell
, nixosTests
, enablePrivSep ? true
}:

stdenv.mkDerivation rec {
Expand Down Expand Up @@ -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; {
Expand Down