vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / networkmanager.nix
blobdedd53e345fe57cdd0cf00e2d3dce5f3ec26fdae
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.networking.networkmanager;
7   ini = pkgs.formats.ini { };
9   delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [ ];
11   enableIwd = cfg.wifi.backend == "iwd";
13   configAttrs = lib.recursiveUpdate {
14     main = {
15       plugins = "keyfile";
16       inherit (cfg) dhcp dns;
17       # If resolvconf is disabled that means that resolv.conf is managed by some other module.
18       rc-manager =
19         if config.networking.resolvconf.enable then "resolvconf"
20         else "unmanaged";
21     };
22     keyfile = {
23       unmanaged-devices =
24       if cfg.unmanaged == [ ] then null
25       else lib.concatStringsSep ";" cfg.unmanaged;
26     };
27     logging = {
28       audit = config.security.audit.enable;
29       level = cfg.logLevel;
30     };
31     connection = cfg.connectionConfig;
32     device = {
33         "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
34         "wifi.backend" = cfg.wifi.backend;
35     };
36   } cfg.settings;
37   configFile = ini.generate "NetworkManager.conf" configAttrs;
39   /*
40     [network-manager]
41     Identity=unix-group:networkmanager
42     Action=org.freedesktop.NetworkManager.*
43     ResultAny=yes
44     ResultInactive=no
45     ResultActive=yes
47     [modem-manager]
48     Identity=unix-group:networkmanager
49     Action=org.freedesktop.ModemManager*
50     ResultAny=yes
51     ResultInactive=no
52     ResultActive=yes
53   */
54   polkitConf = ''
55     polkit.addRule(function(action, subject) {
56       if (
57         subject.isInGroup("networkmanager")
58         && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
59             || action.id.indexOf("org.freedesktop.ModemManager")  == 0
60         ))
61           { return polkit.Result.YES; }
62     });
63   '';
65   ns = xs: pkgs.writeText "nameservers" (
66     concatStrings (map (s: "nameserver ${s}\n") xs)
67   );
69   overrideNameserversScript = pkgs.writeScript "02overridedns" ''
70     #!/bin/sh
71     PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
72     tmp=$(mktemp)
73     sed '/nameserver /d' /etc/resolv.conf > $tmp
74     grep 'nameserver ' /etc/resolv.conf | \
75       grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
76     cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf
77     rm -f $tmp $tmp.ns
78   '';
80   dispatcherTypesSubdirMap = {
81     basic = "";
82     pre-up = "pre-up.d/";
83     pre-down = "pre-down.d/";
84   };
86   macAddressOptWifi = mkOption {
87     type = types.either types.str (types.enum [ "permanent" "preserve" "random" "stable" "stable-ssid" ]);
88     default = "preserve";
89     example = "00:11:22:33:44:55";
90     description = ''
91       Set the MAC address of the interface.
93       - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface
94       - `"permanent"`: Use the permanent MAC address of the device
95       - `"preserve"`: Don’t change the MAC address of the device upon activation
96       - `"random"`: Generate a randomized value upon each connect
97       - `"stable"`: Generate a stable, hashed MAC address
98       - `"stable-ssid"`: Generate a stable MAC addressed based on Wi-Fi network
99     '';
100   };
102   macAddressOptEth = mkOption {
103     type = types.either types.str (types.enum [ "permanent" "preserve" "random" "stable" ]);
104     default = "preserve";
105     example = "00:11:22:33:44:55";
106     description = ''
107       Set the MAC address of the interface.
109       - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface
110       - `"permanent"`: Use the permanent MAC address of the device
111       - `"preserve"`: Don’t change the MAC address of the device upon activation
112       - `"random"`: Generate a randomized value upon each connect
113       - `"stable"`: Generate a stable, hashed MAC address
114     '';
115   };
117   packages = [
118     pkgs.modemmanager
119     pkgs.networkmanager
120   ]
121   ++ cfg.plugins
122   ++ lib.optionals (!delegateWireless && !enableIwd) [
123     pkgs.wpa_supplicant
124   ];
129   meta = {
130     maintainers = teams.freedesktop.members;
131   };
133   ###### interface
135   options = {
137     networking.networkmanager = {
139       enable = mkOption {
140         type = types.bool;
141         default = false;
142         description = ''
143           Whether to use NetworkManager to obtain an IP address and other
144           configuration for all network interfaces that are not manually
145           configured. If enabled, a group `networkmanager`
146           will be created. Add all users that should have permission
147           to change network settings to this group.
148         '';
149       };
151       connectionConfig = mkOption {
152         type = with types; attrsOf (nullOr (oneOf [
153           bool
154           int
155           str
156         ]));
157         default = { };
158         description = ''
159           Configuration for the [connection] section of NetworkManager.conf.
160           Refer to
161           [
162             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#id-1.2.3.11
163           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
164           or
165           {manpage}`NetworkManager.conf(5)`
166           for more information.
167         '';
168       };
170       settings = mkOption {
171         type = ini.type;
172         default = {};
173         description = ''
174           Configuration added to the generated NetworkManager.conf, note that you can overwrite settings with this.
175           Refer to
176           [
177             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
178           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
179           or
180           {manpage}`NetworkManager.conf(5)`
181           for more information.
182         '';
183       };
185       unmanaged = mkOption {
186         type = types.listOf types.str;
187         default = [ ];
188         description = ''
189           List of interfaces that will not be managed by NetworkManager.
190           Interface name can be specified here, but if you need more fidelity,
191           refer to
192           [
193             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec
194           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec)
195           or the "Device List Format" Appendix of
196           {manpage}`NetworkManager.conf(5)`.
197         '';
198       };
200       plugins = mkOption {
201         type =
202           let
203             networkManagerPluginPackage = types.package // {
204               description = "NetworkManager plug-in";
205               check =
206                 p:
207                 lib.assertMsg
208                   (types.package.check p
209                     && p ? networkManagerPlugin
210                     && lib.isString p.networkManagerPlugin)
211                   ''
212                     Package ‘${p.name}’, is not a NetworkManager plug-in.
213                     Those need to have a ‘networkManagerPlugin’ attribute.
214                   '';
215             };
216           in
217           types.listOf networkManagerPluginPackage;
218         default = [ ];
219         description = ''
220           List of NetworkManager plug-ins to enable.
221           Some plug-ins are enabled by the NetworkManager module by default.
222         '';
223       };
225       dhcp = mkOption {
226         type = types.enum [ "dhcpcd" "internal" ];
227         default = "internal";
228         description = ''
229           Which program (or internal library) should be used for DHCP.
230         '';
231       };
233       logLevel = mkOption {
234         type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
235         default = "WARN";
236         description = ''
237           Set the default logging verbosity level.
238         '';
239       };
241       appendNameservers = mkOption {
242         type = types.listOf types.str;
243         default = [ ];
244         description = ''
245           A list of name servers that should be appended
246           to the ones configured in NetworkManager or received by DHCP.
247         '';
248       };
250       insertNameservers = mkOption {
251         type = types.listOf types.str;
252         default = [ ];
253         description = ''
254           A list of name servers that should be inserted before
255           the ones configured in NetworkManager or received by DHCP.
256         '';
257       };
259       ethernet.macAddress = macAddressOptEth;
261       wifi = {
262         macAddress = macAddressOptWifi;
264         backend = mkOption {
265           type = types.enum [ "wpa_supplicant" "iwd" ];
266           default = "wpa_supplicant";
267           description = ''
268             Specify the Wi-Fi backend used for the device.
269             Currently supported are {option}`wpa_supplicant` or {option}`iwd` (experimental).
270           '';
271         };
273         powersave = mkOption {
274           type = types.nullOr types.bool;
275           default = null;
276           description = ''
277             Whether to enable Wi-Fi power saving.
278           '';
279         };
281         scanRandMacAddress = mkOption {
282           type = types.bool;
283           default = true;
284           description = ''
285             Whether to enable MAC address randomization of a Wi-Fi device
286             during scanning.
287           '';
288         };
289       };
291       dns = mkOption {
292         type = types.enum [ "default" "dnsmasq" "systemd-resolved" "none" ];
293         default = "default";
294         description = ''
295           Set the DNS (`resolv.conf`) processing mode.
297           A description of these modes can be found in the main section of
298           [
299             https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
300           ](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
301           or in
302           {manpage}`NetworkManager.conf(5)`.
303         '';
304       };
306       dispatcherScripts = mkOption {
307         type = types.listOf (types.submodule {
308           options = {
309             source = mkOption {
310               type = types.path;
311               description = ''
312                 Path to the hook script.
313               '';
314             };
316             type = mkOption {
317               type = types.enum (attrNames dispatcherTypesSubdirMap);
318               default = "basic";
319               description = ''
320                 Dispatcher hook type. Look up the hooks described at
321                 [https://developer.gnome.org/NetworkManager/stable/NetworkManager.html](https://developer.gnome.org/NetworkManager/stable/NetworkManager.html)
322                 and choose the type depending on the output folder.
323                 You should then filter the event type (e.g., "up"/"down") from within your script.
324               '';
325             };
326           };
327         });
328         default = [ ];
329         example = literalExpression ''
330           [ {
331             source = pkgs.writeText "upHook" '''
332               if [ "$2" != "up" ]; then
333                 logger "exit: event $2 != up"
334                 exit
335               fi
337               # coreutils and iproute are in PATH too
338               logger "Device $DEVICE_IFACE coming up"
339             ''';
340             type = "basic";
341           } ]
342         '';
343         description = ''
344           A list of scripts which will be executed in response to network events.
345         '';
346       };
348       enableStrongSwan = mkOption {
349         type = types.bool;
350         default = false;
351         description = ''
352           Enable the StrongSwan plugin.
354           If you enable this option the
355           `networkmanager_strongswan` plugin will be added to
356           the {option}`networking.networkmanager.plugins` option
357           so you don't need to do that yourself.
358         '';
359       };
361       fccUnlockScripts = mkOption {
362         type = types.listOf (types.submodule {
363           options = {
364             id = mkOption {
365               type = types.str;
366               description = "vid:pid of either the PCI or USB vendor and product ID";
367             };
368             path = mkOption {
369               type = types.path;
370               description = "Path to the unlock script";
371             };
372           };
373         });
374         default = [ ];
375         example = literalExpression ''[{ id = "03f0:4e1d"; path = "''${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/03f0:4e1d"; }]'';
376         description = ''
377           List of FCC unlock scripts to enable on the system, behaving as described in
378           https://modemmanager.org/docs/modemmanager/fcc-unlock/#integration-with-third-party-fcc-unlock-tools.
379         '';
380       };
381       ensureProfiles = {
382         profiles = with lib.types; mkOption {
383           type = attrsOf (submodule {
384             freeformType = ini.type;
386             options = {
387               connection = {
388                 id = lib.mkOption {
389                   type = str;
390                   description = "This is the name that will be displayed by NetworkManager and GUIs.";
391                 };
392                 type = lib.mkOption {
393                   type = str;
394                   description = "The connection type defines the connection kind, like vpn, wireguard, gsm, wifi and more.";
395                   example = "vpn";
396                 };
397               };
398             };
399           });
400           apply = (lib.filterAttrsRecursive (n: v: v != { }));
401           default = { };
402           example = {
403             home-wifi = {
404               connection = {
405                 id = "home-wifi";
406                 type = "wifi";
407                 permissions = "";
408               };
409               wifi = {
410                 mac-address-blacklist = "";
411                 mode = "infrastructure";
412                 ssid = "Home Wi-Fi";
413               };
414               wifi-security = {
415                 auth-alg = "open";
416                 key-mgmt = "wpa-psk";
417                 psk = "$HOME_WIFI_PASSWORD";
418               };
419               ipv4 = {
420                 dns-search = "";
421                 method = "auto";
422               };
423               ipv6 = {
424                 addr-gen-mode = "stable-privacy";
425                 dns-search = "";
426                 method = "auto";
427               };
428             };
429           };
430           description = ''
431             Declaratively define NetworkManager profiles. You can find information about the generated file format [here](https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html) and [here](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/assembly_networkmanager-connection-profiles-in-keyfile-format_configuring-and-managing-networking).
432             You current profiles which are most likely stored in `/etc/NetworkManager/system-connections` and there is [a tool](https://github.com/janik-haag/nm2nix) to convert them to the needed nix code.
433             If you add a new ad-hoc connection via a GUI or nmtui or anything similar it should just work together with the declarative ones.
434             And if you edit a declarative profile NetworkManager will move it to the persistent storage and treat it like a ad-hoc one,
435             but there will be two profiles as soon as the systemd unit from this option runs again which can be confusing since NetworkManager tools will start displaying two profiles with the same name and probably a bit different settings depending on what you edited.
436             A profile won't be deleted even if it's removed from the config until the system reboots because that's when NetworkManager clears it's temp directory.
437             If `networking.resolvconf.enable` is true, attributes affecting the name resolution (such as `ignore-auto-dns`) may not end up changing `/etc/resolv.conf` as expected when other name services (for example `networking.dhcpcd`) are enabled. Run `resolvconf -l` in the terminal to see what each service produces.
438           '';
439         };
440         environmentFiles = mkOption {
441           default = [];
442           type = types.listOf types.path;
443           example = [ "/run/secrets/network-manager.env" ];
444           description = ''
445             Files to load as environment file. Environment variables from this file
446             will be substituted into the static configuration file using [envsubst](https://github.com/a8m/envsubst).
447           '';
448         };
449       };
450     };
451   };
453   imports = [
454     (mkRenamedOptionModule
455       [ "networking" "networkmanager" "packages" ]
456       [ "networking" "networkmanager" "plugins" ]
457     )
458     (mkRenamedOptionModule
459       [ "networking" "networkmanager" "useDnsmasq" ]
460       [ "networking" "networkmanager" "dns" ]
461     )
462     (mkRemovedOptionModule [ "networking" "networkmanager" "extraConfig" ] ''
463       This option was removed in favour of `networking.networkmanager.settings`,
464       which accepts structured nix-code equivalent to the ini
465       and allows for overriding settings.
466       Example patch:
467       ```patch
468          networking.networkmanager = {
469       -    extraConfig = '''
470       -      [main]
471       -      no-auto-default=*
472       -    '''
473       +    settings.main.no-auto-default = "*";
474          };
475       ```
476     ''
477     )
478     (mkRemovedOptionModule [ "networking" "networkmanager" "enableFccUnlock" ] ''
479       This option was removed, because using bundled FCC unlock scripts is risky,
480       might conflict with vendor-provided unlock scripts, and should
481       be a conscious decision on a per-device basis.
482       Instead it's recommended to use the
483       `networking.networkmanager.fccUnlockScripts` option.
484     '')
485     (mkRemovedOptionModule [ "networking" "networkmanager" "dynamicHosts" ] ''
486       This option was removed because allowing (multiple) regular users to
487       override host entries affecting the whole system opens up a huge attack
488       vector. There seem to be very rare cases where this might be useful.
489       Consider setting system-wide host entries using networking.hosts, provide
490       them via the DNS server in your network, or use environment.etc
491       to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir.
492     '')
493     (mkRemovedOptionModule [ "networking" "networkmanager" "firewallBackend" ] ''
494       This option was removed as NixOS is now using iptables-nftables-compat even when using iptables, therefore Networkmanager now uses the nftables backend unconditionally.
495     '')
496   ];
499   ###### implementation
501   config = mkIf cfg.enable {
503     assertions = [
504       {
505         assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [ ];
506         message = ''
507           You can not use networking.networkmanager with networking.wireless.
508           Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
509         '';
510       }
511     ];
513     hardware.wirelessRegulatoryDatabase = true;
515     environment.etc = {
516       "NetworkManager/NetworkManager.conf".source = configFile;
518       # The networkmanager-l2tp plugin expects /etc/ipsec.secrets to include /etc/ipsec.d/ipsec.nm-l2tp.secrets;
519       # see https://github.com/NixOS/nixpkgs/issues/64965
520       "ipsec.secrets".text = ''
521         include ipsec.d/ipsec.nm-l2tp.secrets
522       '';
523     }
524     // builtins.listToAttrs (map
525       (pkg: nameValuePair "NetworkManager/${pkg.networkManagerPlugin}" {
526         source = "${pkg}/lib/NetworkManager/${pkg.networkManagerPlugin}";
527       })
528       cfg.plugins)
529     // builtins.listToAttrs (map
530       (e: nameValuePair "ModemManager/fcc-unlock.d/${e.id}" {
531         source = e.path;
532       })
533       cfg.fccUnlockScripts)
534     // optionalAttrs (cfg.appendNameservers != [ ] || cfg.insertNameservers != [ ])
535       {
536         "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
537       }
538     // listToAttrs (lib.imap1
539       (i: s:
540         {
541           name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
542           value = { mode = "0544"; inherit (s) source; };
543         })
544       cfg.dispatcherScripts);
546     environment.systemPackages = packages;
548     users.groups = {
549       networkmanager.gid = config.ids.gids.networkmanager;
550       nm-openvpn.gid = config.ids.gids.nm-openvpn;
551     };
553     users.users = {
554       nm-openvpn = {
555         uid = config.ids.uids.nm-openvpn;
556         group = "nm-openvpn";
557         extraGroups = [ "networkmanager" ];
558       };
559       nm-iodine = {
560         isSystemUser = true;
561         group = "networkmanager";
562       };
563     };
565     systemd.packages = packages;
567     systemd.tmpfiles.rules = [
568       "d /etc/NetworkManager/system-connections 0700 root root -"
569       "d /etc/ipsec.d 0700 root root -"
570       "d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
572       "d /var/lib/misc 0755 root root -" # for dnsmasq.leases
573       # ppp isn't able to mkdir that directory at runtime
574       "d /run/pppd/lock 0700 root root -"
575     ];
577     systemd.services.NetworkManager = {
578       wantedBy = [ "network.target" ];
579       restartTriggers = [ configFile ];
581       aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
583       serviceConfig = {
584         StateDirectory = "NetworkManager";
585         StateDirectoryMode = 755; # not sure if this really needs to be 755
586       };
587     };
589     systemd.services.NetworkManager-wait-online = {
590       wantedBy = [ "network-online.target" ];
591     };
593     systemd.services.ModemManager = {
594       aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
595       path = lib.optionals (cfg.fccUnlockScripts != []) [ pkgs.libqmi pkgs.libmbim ];
596     };
598     systemd.services.NetworkManager-dispatcher = {
599       wantedBy = [ "network.target" ];
600       restartTriggers = [ configFile overrideNameserversScript ];
602       # useful binaries for user-specified hooks
603       path = [ pkgs.iproute2 pkgs.util-linux pkgs.coreutils ];
604       aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ];
605     };
607     systemd.services.NetworkManager-ensure-profiles = mkIf (cfg.ensureProfiles.profiles != { }) {
608       description = "Ensure that NetworkManager declarative profiles are created";
609       wantedBy = [ "multi-user.target" ];
610       before = [ "network-online.target" ];
611       after = [ "NetworkManager.service" ];
612       script = let
613         path = id: "/run/NetworkManager/system-connections/${id}.nmconnection";
614       in ''
615         mkdir -p /run/NetworkManager/system-connections
616       '' + lib.concatMapStringsSep "\n"
617         (profile: ''
618           ${pkgs.envsubst}/bin/envsubst -i ${ini.generate (lib.escapeShellArg profile.n) profile.v} > ${path (lib.escapeShellArg profile.n)}
619         '') (lib.mapAttrsToList (n: v: { inherit n v; }) cfg.ensureProfiles.profiles)
620       + ''
621         ${pkgs.networkmanager}/bin/nmcli connection reload
622       '';
623       serviceConfig = {
624         EnvironmentFile = cfg.ensureProfiles.environmentFiles;
625         UMask = "0177";
626         Type = "oneshot";
627       };
628     };
630     # Turn off NixOS' network management when networking is managed entirely by NetworkManager
631     networking = mkMerge [
632       (mkIf (!delegateWireless) {
633         useDHCP = false;
634       })
636       {
637         networkmanager.plugins = with pkgs; [
638           networkmanager-fortisslvpn
639           networkmanager-iodine
640           networkmanager-l2tp
641           networkmanager-openconnect
642           networkmanager-openvpn
643           networkmanager-vpnc
644           networkmanager-sstp
645         ];
646       }
648       (mkIf cfg.enableStrongSwan {
649         networkmanager.plugins = [ pkgs.networkmanager_strongswan ];
650       })
652       (mkIf enableIwd {
653         wireless.iwd.enable = true;
654       })
656       {
657         networkmanager.connectionConfig = {
658           "ethernet.cloned-mac-address" = cfg.ethernet.macAddress;
659           "wifi.cloned-mac-address" = cfg.wifi.macAddress;
660           "wifi.powersave" =
661             if cfg.wifi.powersave == null then null
662             else if cfg.wifi.powersave then 3
663             else 2;
664         };
665       }
666     ];
668     boot.kernelModules = [ "ctr" ];
670     security.polkit.enable = true;
671     security.polkit.extraConfig = polkitConf;
673     services.dbus.packages = packages
674       ++ optional cfg.enableStrongSwan pkgs.strongswanNM
675       ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
677     services.udev.packages = packages;
678   };