1 { config, lib, pkgs, ... }:
6 cfg = config.networking.networkmanager;
8 delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [];
10 enableIwd = cfg.wifi.backend == "iwd";
13 if v == true then "yes"
14 else if v == false then "no"
15 else if lib.isInt v then toString v
18 mkSection = name: attrs: ''
21 lib.concatStringsSep "\n"
23 (k: v: "${k}=${mkValue v}")
30 configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [
35 # If resolvconf is disabled that means that resolv.conf is managed by some other module.
37 if config.networking.resolvconf.enable then "resolvconf"
39 firewall-backend = cfg.firewallBackend;
41 (mkSection "keyfile" {
43 if cfg.unmanaged == [] then null
44 else lib.concatStringsSep ";" cfg.unmanaged;
46 (mkSection "logging" {
47 audit = config.security.audit.enable;
50 (mkSection "connection" cfg.connectionConfig)
52 "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
53 "wifi.backend" = cfg.wifi.backend;
60 Identity=unix-group:networkmanager
61 Action=org.freedesktop.NetworkManager.*
67 Identity=unix-group:networkmanager
68 Action=org.freedesktop.ModemManager*
74 polkit.addRule(function(action, subject) {
76 subject.isInGroup("networkmanager")
77 && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
78 || action.id.indexOf("org.freedesktop.ModemManager") == 0
80 { return polkit.Result.YES; }
84 ns = xs: pkgs.writeText "nameservers" (
85 concatStrings (map (s: "nameserver ${s}\n") xs)
88 overrideNameserversScript = pkgs.writeScript "02overridedns" ''
90 PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
92 sed '/nameserver /d' /etc/resolv.conf > $tmp
93 grep 'nameserver ' /etc/resolv.conf | \
94 grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
95 cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf
99 dispatcherTypesSubdirMap = {
101 pre-up = "pre-up.d/";
102 pre-down = "pre-down.d/";
105 macAddressOpt = mkOption {
106 type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]);
107 default = "preserve";
108 example = "00:11:22:33:44:55";
109 description = lib.mdDoc ''
110 Set the MAC address of the interface.
112 - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface
113 - `"permanent"`: Use the permanent MAC address of the device
114 - `"preserve"`: Don’t change the MAC address of the device upon activation
115 - `"random"`: Generate a randomized value upon each connect
116 - `"stable"`: Generate a stable, hashed MAC address
125 ++ lib.optionals (!delegateWireless && !enableIwd) [
132 maintainers = teams.freedesktop.members;
139 networking.networkmanager = {
144 description = lib.mdDoc ''
145 Whether to use NetworkManager to obtain an IP address and other
146 configuration for all network interfaces that are not manually
147 configured. If enabled, a group `networkmanager`
148 will be created. Add all users that should have permission
149 to change network settings to this group.
153 connectionConfig = mkOption {
154 type = with types; attrsOf (nullOr (oneOf [
160 description = lib.mdDoc ''
161 Configuration for the [connection] section of NetworkManager.conf.
164 https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#id-1.2.3.11
165 ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
167 {manpage}`NetworkManager.conf(5)`
168 for more information.
172 extraConfig = mkOption {
175 description = lib.mdDoc ''
176 Configuration appended to the generated NetworkManager.conf.
179 https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
180 ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
182 {manpage}`NetworkManager.conf(5)`
183 for more information.
187 unmanaged = mkOption {
188 type = types.listOf types.str;
190 description = lib.mdDoc ''
191 List of interfaces that will not be managed by NetworkManager.
192 Interface name can be specified here, but if you need more fidelity,
195 https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec
196 ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec)
197 or the "Device List Format" Appendix of
198 {manpage}`NetworkManager.conf(5)`.
205 networkManagerPluginPackage = types.package // {
206 description = "NetworkManager plug-in";
210 (types.package.check p
211 && p ? networkManagerPlugin
212 && lib.isString p.networkManagerPlugin)
214 Package ‘${p.name}’, is not a NetworkManager plug-in.
215 Those need to have a ‘networkManagerPlugin’ attribute.
219 types.listOf networkManagerPluginPackage;
221 description = lib.mdDoc ''
222 List of NetworkManager plug-ins to enable.
223 Some plug-ins are enabled by the NetworkManager module by default.
228 type = types.enum [ "dhcpcd" "internal" ];
229 default = "internal";
230 description = lib.mdDoc ''
231 Which program (or internal library) should be used for DHCP.
235 firewallBackend = mkOption {
236 type = types.enum [ "iptables" "nftables" "none" ];
237 default = "iptables";
238 description = lib.mdDoc ''
239 Which firewall backend should be used for configuring masquerading with shared mode.
240 If set to none, NetworkManager doesn't manage the configuration at all.
244 logLevel = mkOption {
245 type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
247 description = lib.mdDoc ''
248 Set the default logging verbosity level.
252 appendNameservers = mkOption {
253 type = types.listOf types.str;
255 description = lib.mdDoc ''
256 A list of name servers that should be appended
257 to the ones configured in NetworkManager or received by DHCP.
261 insertNameservers = mkOption {
262 type = types.listOf types.str;
264 description = lib.mdDoc ''
265 A list of name servers that should be inserted before
266 the ones configured in NetworkManager or received by DHCP.
270 ethernet.macAddress = macAddressOpt;
273 macAddress = macAddressOpt;
276 type = types.enum [ "wpa_supplicant" "iwd" ];
277 default = "wpa_supplicant";
278 description = lib.mdDoc ''
279 Specify the Wi-Fi backend used for the device.
280 Currently supported are {option}`wpa_supplicant` or {option}`iwd` (experimental).
284 powersave = mkOption {
285 type = types.nullOr types.bool;
287 description = lib.mdDoc ''
288 Whether to enable Wi-Fi power saving.
292 scanRandMacAddress = mkOption {
295 description = lib.mdDoc ''
296 Whether to enable MAC address randomization of a Wi-Fi device
303 type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
305 description = lib.mdDoc ''
306 Set the DNS (`resolv.conf`) processing mode.
308 A description of these modes can be found in the main section of
310 https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
311 ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
313 {manpage}`NetworkManager.conf(5)`.
317 dispatcherScripts = mkOption {
318 type = types.listOf (types.submodule {
322 description = lib.mdDoc ''
323 Path to the hook script.
328 type = types.enum (attrNames dispatcherTypesSubdirMap);
330 description = lib.mdDoc ''
331 Dispatcher hook type. Look up the hooks described at
332 [https://developer.gnome.org/NetworkManager/stable/NetworkManager.html](https://developer.gnome.org/NetworkManager/stable/NetworkManager.html)
333 and choose the type depending on the output folder.
334 You should then filter the event type (e.g., "up"/"down") from within your script.
340 example = literalExpression ''
342 source = pkgs.writeText "upHook" '''
344 if [ "$2" != "up" ]; then
345 logger "exit: event $2 != up"
349 # coreutils and iproute are in PATH too
350 logger "Device $DEVICE_IFACE coming up"
354 description = lib.mdDoc ''
355 A list of scripts which will be executed in response to network events.
359 enableStrongSwan = mkOption {
362 description = lib.mdDoc ''
363 Enable the StrongSwan plugin.
365 If you enable this option the
366 `networkmanager_strongswan` plugin will be added to
367 the {option}`networking.networkmanager.plugins` option
368 so you don't need to do that yourself.
372 enableFccUnlock = mkOption {
375 description = lib.mdDoc ''
376 Enable FCC unlock procedures. Since release 1.18.4, the ModemManager daemon no longer
377 automatically performs the FCC unlock procedure by default. See
378 [the docs](https://modemmanager.org/docs/modemmanager/fcc-unlock/)
386 (mkRenamedOptionModule
387 [ "networking" "networkmanager" "packages" ]
388 [ "networking" "networkmanager" "plugins" ])
389 (mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ])
390 (mkRemovedOptionModule ["networking" "networkmanager" "dynamicHosts"] ''
391 This option was removed because allowing (multiple) regular users to
392 override host entries affecting the whole system opens up a huge attack
393 vector. There seem to be very rare cases where this might be useful.
394 Consider setting system-wide host entries using networking.hosts, provide
395 them via the DNS server in your network, or use environment.etc
396 to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir.
401 ###### implementation
403 config = mkIf cfg.enable {
406 { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [];
408 You can not use networking.networkmanager with networking.wireless.
409 Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
414 hardware.wirelessRegulatoryDatabase = true;
417 "NetworkManager/NetworkManager.conf".source = configFile;
419 // builtins.listToAttrs (map (pkg: nameValuePair "NetworkManager/${pkg.networkManagerPlugin}" {
420 source = "${pkg}/lib/NetworkManager/${pkg.networkManagerPlugin}";
422 // optionalAttrs cfg.enableFccUnlock
424 "ModemManager/fcc-unlock.d".source =
425 "${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/*";
427 // optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != [])
429 "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
431 // listToAttrs (lib.imap1 (i: s:
433 name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
434 value = { mode = "0544"; inherit (s) source; };
435 }) cfg.dispatcherScripts);
437 environment.systemPackages = packages;
440 networkmanager.gid = config.ids.gids.networkmanager;
441 nm-openvpn.gid = config.ids.gids.nm-openvpn;
446 uid = config.ids.uids.nm-openvpn;
447 group = "nm-openvpn";
448 extraGroups = [ "networkmanager" ];
452 group = "networkmanager";
456 systemd.packages = packages;
458 systemd.tmpfiles.rules = [
459 "d /etc/NetworkManager/system-connections 0700 root root -"
460 "d /etc/ipsec.d 0700 root root -"
461 "d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
463 "d /var/lib/misc 0755 root root -" # for dnsmasq.leases
466 systemd.services.NetworkManager = {
467 wantedBy = [ "network.target" ];
468 restartTriggers = [ configFile ];
470 aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
473 StateDirectory = "NetworkManager";
474 StateDirectoryMode = 755; # not sure if this really needs to be 755
478 systemd.services.NetworkManager-wait-online = {
479 wantedBy = [ "network-online.target" ];
482 systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
484 systemd.services.NetworkManager-dispatcher = {
485 wantedBy = [ "network.target" ];
486 restartTriggers = [ configFile overrideNameserversScript ];
488 # useful binaries for user-specified hooks
489 path = [ pkgs.iproute2 pkgs.util-linux pkgs.coreutils ];
490 aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ];
493 # Turn off NixOS' network management when networking is managed entirely by NetworkManager
494 networking = mkMerge [
495 (mkIf (!delegateWireless) {
500 networkmanager.plugins = with pkgs; [
501 networkmanager-fortisslvpn
502 networkmanager-iodine
504 networkmanager-openconnect
505 networkmanager-openvpn
511 (mkIf cfg.enableStrongSwan {
512 networkmanager.plugins = [ pkgs.networkmanager_strongswan ];
516 wireless.iwd.enable = true;
520 networkmanager.connectionConfig = {
521 "ethernet.cloned-mac-address" = cfg.ethernet.macAddress;
522 "wifi.cloned-mac-address" = cfg.wifi.macAddress;
524 if cfg.wifi.powersave == null then null
525 else if cfg.wifi.powersave then 3
531 boot.kernelModules = [ "ctr" ];
533 security.polkit.enable = true;
534 security.polkit.extraConfig = polkitConf;
536 services.dbus.packages = packages
537 ++ optional cfg.enableStrongSwan pkgs.strongswanNM
538 ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
540 services.udev.packages = packages;