python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / networking / networkmanager.nix
blob3b28cec83cb7ae0bc82d78be0264dfbc55d3946d
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.networking.networkmanager;
8   delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [];
10   enableIwd = cfg.wifi.backend == "iwd";
12   mkValue = v:
13     if v == true then "yes"
14     else if v == false then "no"
15     else if lib.isInt v then toString v
16     else v;
18   mkSection = name: attrs: ''
19     [${name}]
20     ${
21       lib.concatStringsSep "\n"
22         (lib.mapAttrsToList
23           (k: v: "${k}=${mkValue v}")
24           (lib.filterAttrs
25             (k: v: v != null)
26             attrs))
27     }
28   '';
30   configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [
31     (mkSection "main" {
32       plugins = "keyfile";
33       dhcp = cfg.dhcp;
34       dns = cfg.dns;
35       # If resolvconf is disabled that means that resolv.conf is managed by some other module.
36       rc-manager =
37         if config.networking.resolvconf.enable then "resolvconf"
38         else "unmanaged";
39       firewall-backend = cfg.firewallBackend;
40     })
41     (mkSection "keyfile" {
42       unmanaged-devices =
43         if cfg.unmanaged == [] then null
44         else lib.concatStringsSep ";" cfg.unmanaged;
45     })
46     (mkSection "logging" {
47       audit = config.security.audit.enable;
48       level = cfg.logLevel;
49     })
50     (mkSection "connection" cfg.connectionConfig)
51     (mkSection "device" {
52       "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
53       "wifi.backend" = cfg.wifi.backend;
54     })
55     cfg.extraConfig
56   ]);
58   /*
59     [network-manager]
60     Identity=unix-group:networkmanager
61     Action=org.freedesktop.NetworkManager.*
62     ResultAny=yes
63     ResultInactive=no
64     ResultActive=yes
66     [modem-manager]
67     Identity=unix-group:networkmanager
68     Action=org.freedesktop.ModemManager*
69     ResultAny=yes
70     ResultInactive=no
71     ResultActive=yes
72   */
73   polkitConf = ''
74     polkit.addRule(function(action, subject) {
75       if (
76         subject.isInGroup("networkmanager")
77         && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
78             || action.id.indexOf("org.freedesktop.ModemManager")  == 0
79         ))
80           { return polkit.Result.YES; }
81     });
82   '';
84   ns = xs: pkgs.writeText "nameservers" (
85     concatStrings (map (s: "nameserver ${s}\n") xs)
86   );
88   overrideNameserversScript = pkgs.writeScript "02overridedns" ''
89     #!/bin/sh
90     PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
91     tmp=$(mktemp)
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
96     rm -f $tmp $tmp.ns
97   '';
99   dispatcherTypesSubdirMap = {
100     basic = "";
101     pre-up = "pre-up.d/";
102     pre-down = "pre-down.d/";
103   };
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
117     '';
118   };
120   packages = [
121     pkgs.modemmanager
122     pkgs.networkmanager
123   ]
124   ++ cfg.plugins
125   ++ lib.optionals (!delegateWireless && !enableIwd) [
126     pkgs.wpa_supplicant
127   ];
129 in {
131   meta = {
132     maintainers = teams.freedesktop.members;
133   };
135   ###### interface
137   options = {
139     networking.networkmanager = {
141       enable = mkOption {
142         type = types.bool;
143         default = false;
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.
150         '';
151       };
153       connectionConfig = mkOption {
154         type = with types; attrsOf (nullOr (oneOf [
155           bool
156           int
157           str
158         ]));
159         default = {};
160         description = lib.mdDoc ''
161           Configuration for the [connection] section of NetworkManager.conf.
162           Refer to
163           [
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)
166           or
167           {manpage}`NetworkManager.conf(5)`
168           for more information.
169         '';
170       };
172       extraConfig = mkOption {
173         type = types.lines;
174         default = "";
175         description = lib.mdDoc ''
176           Configuration appended to the generated NetworkManager.conf.
177           Refer to
178           [
179             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
180           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
181           or
182           {manpage}`NetworkManager.conf(5)`
183           for more information.
184         '';
185       };
187       unmanaged = mkOption {
188         type = types.listOf types.str;
189         default = [];
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,
193           refer to
194           [
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)`.
199         '';
200       };
202       plugins = mkOption {
203         type =
204           let
205             networkManagerPluginPackage = types.package // {
206               description = "NetworkManager plug-in";
207               check =
208                 p:
209                 lib.assertMsg
210                   (types.package.check p
211                     && p ? networkManagerPlugin
212                     && lib.isString p.networkManagerPlugin)
213                   ''
214                     Package ‘${p.name}’, is not a NetworkManager plug-in.
215                     Those need to have a ‘networkManagerPlugin’ attribute.
216                   '';
217             };
218           in
219           types.listOf networkManagerPluginPackage;
220         default = [ ];
221         description = lib.mdDoc ''
222           List of NetworkManager plug-ins to enable.
223           Some plug-ins are enabled by the NetworkManager module by default.
224         '';
225       };
227       dhcp = mkOption {
228         type = types.enum [ "dhcpcd" "internal" ];
229         default = "internal";
230         description = lib.mdDoc ''
231           Which program (or internal library) should be used for DHCP.
232         '';
233       };
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.
241         '';
242       };
244       logLevel = mkOption {
245         type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
246         default = "WARN";
247         description = lib.mdDoc ''
248           Set the default logging verbosity level.
249         '';
250       };
252       appendNameservers = mkOption {
253         type = types.listOf types.str;
254         default = [];
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.
258         '';
259       };
261       insertNameservers = mkOption {
262         type = types.listOf types.str;
263         default = [];
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.
267         '';
268       };
270       ethernet.macAddress = macAddressOpt;
272       wifi = {
273         macAddress = macAddressOpt;
275         backend = mkOption {
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).
281           '';
282         };
284         powersave = mkOption {
285           type = types.nullOr types.bool;
286           default = null;
287           description = lib.mdDoc ''
288             Whether to enable Wi-Fi power saving.
289           '';
290         };
292         scanRandMacAddress = mkOption {
293           type = types.bool;
294           default = true;
295           description = lib.mdDoc ''
296             Whether to enable MAC address randomization of a Wi-Fi device
297             during scanning.
298           '';
299         };
300       };
302       dns = mkOption {
303         type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
304         default = "default";
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
309           [
310             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
311           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
312           or in
313           {manpage}`NetworkManager.conf(5)`.
314         '';
315       };
317       dispatcherScripts = mkOption {
318         type = types.listOf (types.submodule {
319           options = {
320             source = mkOption {
321               type = types.path;
322               description = lib.mdDoc ''
323                 Path to the hook script.
324               '';
325             };
327             type = mkOption {
328               type = types.enum (attrNames dispatcherTypesSubdirMap);
329               default = "basic";
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.
335               '';
336             };
337           };
338         });
339         default = [];
340         example = literalExpression ''
341         [ {
342               source = pkgs.writeText "upHook" '''
344                 if [ "$2" != "up" ]; then
345                     logger "exit: event $2 != up"
346                     exit
347                 fi
349                 # coreutils and iproute are in PATH too
350                 logger "Device $DEVICE_IFACE coming up"
351             ''';
352             type = "basic";
353         } ]'';
354         description = lib.mdDoc ''
355           A list of scripts which will be executed in response to  network  events.
356         '';
357       };
359       enableStrongSwan = mkOption {
360         type = types.bool;
361         default = false;
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.
369         '';
370       };
372       enableFccUnlock = mkOption {
373         type = types.bool;
374         default = false;
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/)
379           for more details.
380         '';
381       };
382     };
383   };
385   imports = [
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.
397     '')
398   ];
401   ###### implementation
403   config = mkIf cfg.enable {
405     assertions = [
406       { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [];
407         message = ''
408           You can not use networking.networkmanager with networking.wireless.
409           Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
410         '';
411       }
412     ];
414     hardware.wirelessRegulatoryDatabase = true;
416     environment.etc = {
417         "NetworkManager/NetworkManager.conf".source = configFile;
418       }
419       // builtins.listToAttrs (map (pkg: nameValuePair "NetworkManager/${pkg.networkManagerPlugin}" {
420         source = "${pkg}/lib/NetworkManager/${pkg.networkManagerPlugin}";
421       }) cfg.plugins)
422       // optionalAttrs cfg.enableFccUnlock
423          {
424            "ModemManager/fcc-unlock.d".source =
425              "${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/*";
426          }
427       // optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != [])
428          {
429            "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
430          }
431       // listToAttrs (lib.imap1 (i: s:
432          {
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;
439     users.groups = {
440       networkmanager.gid = config.ids.gids.networkmanager;
441       nm-openvpn.gid = config.ids.gids.nm-openvpn;
442     };
444     users.users = {
445       nm-openvpn = {
446         uid = config.ids.uids.nm-openvpn;
447         group = "nm-openvpn";
448         extraGroups = [ "networkmanager" ];
449       };
450       nm-iodine = {
451         isSystemUser = true;
452         group = "networkmanager";
453       };
454     };
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
464     ];
466     systemd.services.NetworkManager = {
467       wantedBy = [ "network.target" ];
468       restartTriggers = [ configFile ];
470       aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
472       serviceConfig = {
473         StateDirectory = "NetworkManager";
474         StateDirectoryMode = 755; # not sure if this really needs to be 755
475       };
476     };
478     systemd.services.NetworkManager-wait-online = {
479       wantedBy = [ "network-online.target" ];
480     };
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" ];
491     };
493     # Turn off NixOS' network management when networking is managed entirely by NetworkManager
494     networking = mkMerge [
495       (mkIf (!delegateWireless) {
496         useDHCP = false;
497       })
499       {
500         networkmanager.plugins = with pkgs; [
501           networkmanager-fortisslvpn
502           networkmanager-iodine
503           networkmanager-l2tp
504           networkmanager-openconnect
505           networkmanager-openvpn
506           networkmanager-vpnc
507           networkmanager-sstp
508         ];
509       }
511       (mkIf cfg.enableStrongSwan {
512         networkmanager.plugins = [ pkgs.networkmanager_strongswan ];
513       })
515       (mkIf enableIwd {
516         wireless.iwd.enable = true;
517       })
519       {
520         networkmanager.connectionConfig = {
521           "ethernet.cloned-mac-address" = cfg.ethernet.macAddress;
522           "wifi.cloned-mac-address" = cfg.wifi.macAddress;
523           "wifi.powersave" =
524             if cfg.wifi.powersave == null then null
525             else if cfg.wifi.powersave then 3
526             else 2;
527         };
528       }
529     ];
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;
541   };