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