vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / dhcpcd.nix
blob50780a2473b6a136e7174bc8a2ed6ec82ac8fef5
1 { config, lib, pkgs, ... }:
2 let
4   dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; };
6   cfg = config.networking.dhcpcd;
8   interfaces = lib.attrValues config.networking.interfaces;
10   enableDHCP = config.networking.dhcpcd.enable &&
11         (config.networking.useDHCP || lib.any (i: i.useDHCP == true) interfaces);
13   enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable);
15   # Don't start dhcpcd on explicitly configured interfaces or on
16   # interfaces that are part of a bridge, bond or sit device.
17   ignoredInterfaces =
18     map (i: i.name) (lib.filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces)
19     ++ lib.mapAttrsToList (i: _: i) config.networking.sits
20     ++ lib.concatLists (lib.attrValues (lib.mapAttrs (n: v: v.interfaces) config.networking.bridges))
21     ++ lib.flatten (lib.concatMap (i: lib.attrNames (lib.filterAttrs (_: config: config.type != "internal") i.interfaces)) (lib.attrValues config.networking.vswitches))
22     ++ lib.concatLists (lib.attrValues (lib.mapAttrs (n: v: v.interfaces) config.networking.bonds))
23     ++ config.networking.dhcpcd.denyInterfaces;
25   arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null
26     else if a1 == null then a2 else if a2 == null then a1
27       else a1 ++ a2;
29   # If dhcp is disabled but explicit interfaces are enabled,
30   # we need to provide dhcp just for those interfaces.
31   allowInterfaces = arrayAppendOrNull cfg.allowInterfaces
32     (if !config.networking.useDHCP && enableDHCP then
33       map (i: i.name) (lib.filter (i: i.useDHCP == true) interfaces) else null);
35   staticIPv6Addresses = map (i: i.name) (lib.filter (i: i.ipv6.addresses != [ ]) interfaces);
37   noIPv6rs = lib.concatStringsSep "\n" (map (name: ''
38     interface ${name}
39     noipv6rs
40   '') staticIPv6Addresses);
42   # Config file adapted from the one that ships with dhcpcd.
43   dhcpcdConf = pkgs.writeText "dhcpcd.conf"
44     ''
45       # Inform the DHCP server of our hostname for DDNS.
46       hostname
48       # A list of options to request from the DHCP server.
49       option domain_name_servers, domain_name, domain_search, host_name
50       option classless_static_routes, ntp_servers, interface_mtu
52       # A ServerID is required by RFC2131.
53       # Commented out because of many non-compliant DHCP servers in the wild :(
54       #require dhcp_server_identifier
56       # A hook script is provided to lookup the hostname if not set by
57       # the DHCP server, but it should not be run by default.
58       nohook lookup-hostname
60       # Ignore peth* devices; on Xen, they're renamed physical
61       # Ethernet cards used for bridging.  Likewise for vif* and tap*
62       # (Xen) and virbr* and vnet* (libvirt).
63       denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit*
65       # Use the list of allowed interfaces if specified
66       ${lib.optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"}
68       # Immediately fork to background if specified, otherwise wait for IP address to be assigned
69       ${{
70         background = "background";
71         any = "waitip";
72         ipv4 = "waitip 4";
73         ipv6 = "waitip 6";
74         both = "waitip 4\nwaitip 6";
75         if-carrier-up = "";
76       }.${cfg.wait}}
78       ${lib.optionalString (config.networking.enableIPv6 == false) ''
79         # Don't solicit or accept IPv6 Router Advertisements and DHCPv6 if disabled IPv6
80         noipv6
81       ''}
83       ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == null && staticIPv6Addresses != [ ]) noIPv6rs}
84       ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) ''
85         noipv6rs
86       ''}
88       ${cfg.extraConfig}
89     '';
91   exitHook = pkgs.writeText "dhcpcd.exit-hook" ''
92     ${lib.optionalString enableNTPService ''
93       if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
94         # Restart ntpd. We need to restart it to make sure that it will actually do something:
95         # if ntpd cannot resolve the server hostnames in its config file, then it will never do
96         # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose
97         # time synchronisation. This also applies to openntpd.
98         ${lib.optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"}
99         ${lib.optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"}
100         ${lib.optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"}
101         ${lib.optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"}
102       fi
103     ''}
105     ${cfg.runHook}
106   '';
112   ###### interface
114   options = {
116     networking.dhcpcd.enable = lib.mkOption {
117       type = lib.types.bool;
118       default = true;
119       description = ''
120         Whether to enable dhcpcd for device configuration. This is mainly to
121         explicitly disable dhcpcd (for example when using networkd).
122       '';
123     };
125     networking.dhcpcd.persistent = lib.mkOption {
126       type = lib.types.bool;
127       default = false;
128       description = ''
129           Whenever to leave interfaces configured on dhcpcd daemon
130           shutdown. Set to true if you have your root or store mounted
131           over the network or this machine accepts SSH connections
132           through DHCP interfaces and clients should be notified when
133           it shuts down.
134       '';
135     };
137     networking.dhcpcd.denyInterfaces = lib.mkOption {
138       type = lib.types.listOf lib.types.str;
139       default = [];
140       description = ''
141          Disable the DHCP client for any interface whose name matches
142          any of the shell glob patterns in this list. The purpose of
143          this option is to blacklist virtual interfaces such as those
144          created by Xen, libvirt, LXC, etc.
145       '';
146     };
148     networking.dhcpcd.allowInterfaces = lib.mkOption {
149       type = lib.types.nullOr (lib.types.listOf lib.types.str);
150       default = null;
151       description = ''
152          Enable the DHCP client for any interface whose name matches
153          any of the shell glob patterns in this list. Any interface not
154          explicitly matched by this pattern will be denied. This pattern only
155          applies when non-null.
156       '';
157     };
159     networking.dhcpcd.extraConfig = lib.mkOption {
160       type = lib.types.lines;
161       default = "";
162       description = ''
163          Literal string to append to the config file generated for dhcpcd.
164       '';
165     };
167     networking.dhcpcd.IPv6rs = lib.mkOption {
168       type = lib.types.nullOr lib.types.bool;
169       default = null;
170       description = ''
171         Force enable or disable solicitation and receipt of IPv6 Router Advertisements.
172         This is required, for example, when using a static unique local IPv6 address (ULA)
173         and global IPv6 address auto-configuration with SLAAC.
174       '';
175     };
177     networking.dhcpcd.runHook = lib.mkOption {
178       type = lib.types.lines;
179       default = "";
180       example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi";
181       description = ''
182          Shell code that will be run after all other hooks. See
183          `man dhcpcd-run-hooks` for details on what is possible.
184       '';
185     };
187     networking.dhcpcd.wait = lib.mkOption {
188       type = lib.types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ];
189       default = "any";
190       description = ''
191         This option specifies when the dhcpcd service will fork to background.
192         If set to "background", dhcpcd will fork to background immediately.
193         If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP
194         address to be assigned. If set to "any", dhcpcd will wait for any type
195         (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for
196         both an IPv4 and an IPv6 address before forking.
197         The option "if-carrier-up" is equivalent to "any" if either ethernet
198         is plugged nor WiFi is powered, and to "background" otherwise.
199       '';
200     };
202   };
205   ###### implementation
207   config = lib.mkIf enableDHCP {
209     assertions = [ {
210       # dhcpcd doesn't start properly with malloc ∉ [ jemalloc libc mimalloc scudo ]
211       # see https://github.com/NixOS/nixpkgs/issues/151696
212       assertion =
213         dhcpcd.enablePrivSep
214           -> lib.elem config.environment.memoryAllocator.provider [ "jemalloc" "libc" "mimalloc" "scudo" ];
215       message = ''
216         dhcpcd with privilege separation is incompatible with chosen system malloc.
217           Currently `graphene-hardened` allocator is known to be broken.
218           To disable dhcpcd's privilege separation, overlay Nixpkgs and override dhcpcd
219           to set `enablePrivSep = false`.
220       '';
221     } ];
223     environment.etc."dhcpcd.conf".source = dhcpcdConf;
225     systemd.services.dhcpcd = let
226       cfgN = config.networking;
227       hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")
228                           && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != ""));
229     in
230       { description = "DHCP Client";
232         wantedBy = [ "multi-user.target" ] ++ lib.optional (!hasDefaultGatewaySet) "network-online.target";
233         wants = [ "network.target" ];
234         before = [ "network-online.target" ];
236         restartTriggers = lib.optional (enableNTPService || cfg.runHook != "") [ exitHook ];
238         # Stopping dhcpcd during a reconfiguration is undesirable
239         # because it brings down the network interfaces configured by
240         # dhcpcd.  So do a "systemctl restart" instead.
241         stopIfChanged = false;
243         path = [ dhcpcd pkgs.nettools config.networking.resolvconf.package ];
245         unitConfig.ConditionCapability = "CAP_NET_ADMIN";
247         serviceConfig =
248           { Type = "forking";
249             PIDFile = "/run/dhcpcd/pid";
250             RuntimeDirectory = "dhcpcd";
251             ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${lib.optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
252             ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
253             Restart = "always";
254           } // lib.optionalAttrs (cfg.runHook == "") {
255             # Proc filesystem
256             ProcSubset = "all";
257             ProtectProc = "invisible";
258             # Access write directories
259             UMask = "0027";
260             # Capabilities
261             CapabilityBoundingSet = [ "CAP_NET_ADMIN" "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" "CAP_SETGID" "CAP_SETUID" "CAP_SYS_CHROOT" ];
262             # Security
263             NoNewPrivileges = true;
264             # Sandboxing
265             ProtectSystem = true;
266             ProtectHome = true;
267             PrivateTmp = true;
268             PrivateDevices = true;
269             PrivateUsers = false;
270             ProtectHostname = true;
271             ProtectClock = true;
272             ProtectKernelTunables = false;
273             ProtectKernelModules = true;
274             ProtectKernelLogs = true;
275             ProtectControlGroups = true;
276             RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" "AF_PACKET" ];
277             RestrictNamespaces = true;
278             LockPersonality = true;
279             MemoryDenyWriteExecute = true;
280             RestrictRealtime = true;
281             RestrictSUIDSGID = true;
282             RemoveIPC = true;
283             PrivateMounts = true;
284             # System Call Filtering
285             SystemCallArchitectures = "native";
286             SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @resources" "chroot" "gettid" "setgroups" "setuid" ];
287           };
288       };
290     users.users.dhcpcd = {
291       isSystemUser = true;
292       group = "dhcpcd";
293     };
294     users.groups.dhcpcd = {};
296     environment.systemPackages = [ dhcpcd ];
298     environment.etc."dhcpcd.exit-hook" = lib.mkIf (enableNTPService || cfg.runHook != "") {
299       source = exitHook;
300     };
302     powerManagement.resumeCommands = lib.mkIf config.systemd.services.dhcpcd.enable
303       ''
304         # Tell dhcpcd to rebind its interfaces if it's running.
305         /run/current-system/systemd/bin/systemctl reload dhcpcd.service
306       '';
308   };