at 24.11-pre 24 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.networking.networkmanager; 7 ini = pkgs.formats.ini { }; 8 9 delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [ ]; 10 11 enableIwd = cfg.wifi.backend == "iwd"; 12 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; 38 39 /* 40 [network-manager] 41 Identity=unix-group:networkmanager 42 Action=org.freedesktop.NetworkManager.* 43 ResultAny=yes 44 ResultInactive=no 45 ResultActive=yes 46 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 ''; 64 65 ns = xs: pkgs.writeText "nameservers" ( 66 concatStrings (map (s: "nameserver ${s}\n") xs) 67 ); 68 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 ''; 79 80 dispatcherTypesSubdirMap = { 81 basic = ""; 82 pre-up = "pre-up.d/"; 83 pre-down = "pre-down.d/"; 84 }; 85 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. 92 93 - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface 94 - `"permanent"`: Use the permanent MAC address of the device 95 - `"preserve"`: Dont 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 }; 101 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. 108 109 - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface 110 - `"permanent"`: Use the permanent MAC address of the device 111 - `"preserve"`: Dont 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 }; 116 117 packages = [ 118 pkgs.modemmanager 119 pkgs.networkmanager 120 ] 121 ++ cfg.plugins 122 ++ lib.optionals (!delegateWireless && !enableIwd) [ 123 pkgs.wpa_supplicant 124 ]; 125 126in 127{ 128 129 meta = { 130 maintainers = teams.freedesktop.members ++ [ lib.maintainers.janik ]; 131 }; 132 133 ###### interface 134 135 options = { 136 137 networking.networkmanager = { 138 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 }; 150 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 }; 169 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 }; 184 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 }; 199 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 }; 224 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 }; 232 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 }; 240 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 }; 249 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 }; 258 259 ethernet.macAddress = macAddressOptEth; 260 261 wifi = { 262 macAddress = macAddressOptWifi; 263 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 }; 272 273 powersave = mkOption { 274 type = types.nullOr types.bool; 275 default = null; 276 description = '' 277 Whether to enable Wi-Fi power saving. 278 ''; 279 }; 280 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 }; 290 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. 296 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 }; 305 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 }; 315 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 336 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 }; 347 348 enableStrongSwan = mkOption { 349 type = types.bool; 350 default = false; 351 description = '' 352 Enable the StrongSwan plugin. 353 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 }; 360 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; 385 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 }; 452 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 ]; 497 498 499 ###### implementation 500 501 config = mkIf cfg.enable { 502 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 ]; 512 513 hardware.wirelessRegulatoryDatabase = true; 514 515 environment.etc = { 516 "NetworkManager/NetworkManager.conf".source = configFile; 517 } 518 // builtins.listToAttrs (map 519 (pkg: nameValuePair "NetworkManager/${pkg.networkManagerPlugin}" { 520 source = "${pkg}/lib/NetworkManager/${pkg.networkManagerPlugin}"; 521 }) 522 cfg.plugins) 523 // builtins.listToAttrs (map 524 (e: nameValuePair "ModemManager/fcc-unlock.d/${e.id}" { 525 source = e.path; 526 }) 527 cfg.fccUnlockScripts) 528 // optionalAttrs (cfg.appendNameservers != [ ] || cfg.insertNameservers != [ ]) 529 { 530 "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript; 531 } 532 // listToAttrs (lib.imap1 533 (i: s: 534 { 535 name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}"; 536 value = { mode = "0544"; inherit (s) source; }; 537 }) 538 cfg.dispatcherScripts); 539 540 environment.systemPackages = packages; 541 542 users.groups = { 543 networkmanager.gid = config.ids.gids.networkmanager; 544 nm-openvpn.gid = config.ids.gids.nm-openvpn; 545 }; 546 547 users.users = { 548 nm-openvpn = { 549 uid = config.ids.uids.nm-openvpn; 550 group = "nm-openvpn"; 551 extraGroups = [ "networkmanager" ]; 552 }; 553 nm-iodine = { 554 isSystemUser = true; 555 group = "networkmanager"; 556 }; 557 }; 558 559 systemd.packages = packages; 560 561 systemd.tmpfiles.rules = [ 562 "d /etc/NetworkManager/system-connections 0700 root root -" 563 "d /etc/ipsec.d 0700 root root -" 564 "d /var/lib/NetworkManager-fortisslvpn 0700 root root -" 565 566 "d /var/lib/misc 0755 root root -" # for dnsmasq.leases 567 # ppp isn't able to mkdir that directory at runtime 568 "d /run/pppd/lock 0700 root root -" 569 ]; 570 571 systemd.services.NetworkManager = { 572 wantedBy = [ "network.target" ]; 573 restartTriggers = [ configFile ]; 574 575 aliases = [ "dbus-org.freedesktop.NetworkManager.service" ]; 576 577 serviceConfig = { 578 StateDirectory = "NetworkManager"; 579 StateDirectoryMode = 755; # not sure if this really needs to be 755 580 }; 581 }; 582 583 systemd.services.NetworkManager-wait-online = { 584 wantedBy = [ "network-online.target" ]; 585 }; 586 587 systemd.services.ModemManager = { 588 aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; 589 path = lib.optionals (cfg.fccUnlockScripts != []) [ pkgs.libqmi pkgs.libmbim ]; 590 }; 591 592 systemd.services.NetworkManager-dispatcher = { 593 wantedBy = [ "network.target" ]; 594 restartTriggers = [ configFile overrideNameserversScript ]; 595 596 # useful binaries for user-specified hooks 597 path = [ pkgs.iproute2 pkgs.util-linux pkgs.coreutils ]; 598 aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ]; 599 }; 600 601 systemd.services.NetworkManager-ensure-profiles = mkIf (cfg.ensureProfiles.profiles != { }) { 602 description = "Ensure that NetworkManager declarative profiles are created"; 603 wantedBy = [ "multi-user.target" ]; 604 before = [ "network-online.target" ]; 605 after = [ "NetworkManager.service" ]; 606 script = let 607 path = id: "/run/NetworkManager/system-connections/${id}.nmconnection"; 608 in '' 609 mkdir -p /run/NetworkManager/system-connections 610 '' + lib.concatMapStringsSep "\n" 611 (profile: '' 612 ${pkgs.envsubst}/bin/envsubst -i ${ini.generate (lib.escapeShellArg profile.n) profile.v} > ${path (lib.escapeShellArg profile.n)} 613 '') (lib.mapAttrsToList (n: v: { inherit n v; }) cfg.ensureProfiles.profiles) 614 + '' 615 ${pkgs.networkmanager}/bin/nmcli connection reload 616 ''; 617 serviceConfig = { 618 EnvironmentFile = cfg.ensureProfiles.environmentFiles; 619 UMask = "0177"; 620 Type = "oneshot"; 621 }; 622 }; 623 624 # Turn off NixOS' network management when networking is managed entirely by NetworkManager 625 networking = mkMerge [ 626 (mkIf (!delegateWireless) { 627 useDHCP = false; 628 }) 629 630 { 631 networkmanager.plugins = with pkgs; [ 632 networkmanager-fortisslvpn 633 networkmanager-iodine 634 networkmanager-l2tp 635 networkmanager-openconnect 636 networkmanager-openvpn 637 networkmanager-vpnc 638 networkmanager-sstp 639 ]; 640 } 641 642 (mkIf cfg.enableStrongSwan { 643 networkmanager.plugins = [ pkgs.networkmanager_strongswan ]; 644 }) 645 646 (mkIf enableIwd { 647 wireless.iwd.enable = true; 648 }) 649 650 { 651 networkmanager.connectionConfig = { 652 "ethernet.cloned-mac-address" = cfg.ethernet.macAddress; 653 "wifi.cloned-mac-address" = cfg.wifi.macAddress; 654 "wifi.powersave" = 655 if cfg.wifi.powersave == null then null 656 else if cfg.wifi.powersave then 3 657 else 2; 658 }; 659 } 660 ]; 661 662 boot.kernelModules = [ "ctr" ]; 663 664 security.polkit.enable = true; 665 security.polkit.extraConfig = polkitConf; 666 667 services.dbus.packages = packages 668 ++ optional cfg.enableStrongSwan pkgs.strongswanNM 669 ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq; 670 671 services.udev.packages = packages; 672 }; 673}