at 23.11-pre 17 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.networking.networkmanager; 7 8 delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != []; 9 10 enableIwd = cfg.wifi.backend == "iwd"; 11 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; 17 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 ''; 29 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 ]); 57 58 /* 59 [network-manager] 60 Identity=unix-group:networkmanager 61 Action=org.freedesktop.NetworkManager.* 62 ResultAny=yes 63 ResultInactive=no 64 ResultActive=yes 65 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 ''; 83 84 ns = xs: pkgs.writeText "nameservers" ( 85 concatStrings (map (s: "nameserver ${s}\n") xs) 86 ); 87 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 ''; 98 99 dispatcherTypesSubdirMap = { 100 basic = ""; 101 pre-up = "pre-up.d/"; 102 pre-down = "pre-down.d/"; 103 }; 104 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. 111 112 - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface 113 - `"permanent"`: Use the permanent MAC address of the device 114 - `"preserve"`: Dont 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 }; 119 120 packages = [ 121 pkgs.modemmanager 122 pkgs.networkmanager 123 ] 124 ++ cfg.plugins 125 ++ lib.optionals (!delegateWireless && !enableIwd) [ 126 pkgs.wpa_supplicant 127 ]; 128 129in { 130 131 meta = { 132 maintainers = teams.freedesktop.members; 133 }; 134 135 ###### interface 136 137 options = { 138 139 networking.networkmanager = { 140 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 }; 152 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 }; 171 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 }; 186 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 }; 201 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 }; 226 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 }; 234 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 }; 243 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 }; 251 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 }; 260 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 }; 269 270 ethernet.macAddress = macAddressOpt; 271 272 wifi = { 273 macAddress = macAddressOpt; 274 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 }; 283 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 }; 291 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 }; 301 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. 307 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 }; 316 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 }; 326 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" ''' 343 344 if [ "$2" != "up" ]; then 345 logger "exit: event $2 != up" 346 exit 347 fi 348 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 }; 358 359 enableStrongSwan = mkOption { 360 type = types.bool; 361 default = false; 362 description = lib.mdDoc '' 363 Enable the StrongSwan plugin. 364 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 }; 371 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 }; 384 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 ]; 399 400 401 ###### implementation 402 403 config = mkIf cfg.enable { 404 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 ]; 413 414 hardware.wirelessRegulatoryDatabase = true; 415 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); 436 437 environment.systemPackages = packages; 438 439 users.groups = { 440 networkmanager.gid = config.ids.gids.networkmanager; 441 nm-openvpn.gid = config.ids.gids.nm-openvpn; 442 }; 443 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 }; 455 456 systemd.packages = packages; 457 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 -" 462 463 "d /var/lib/misc 0755 root root -" # for dnsmasq.leases 464 ]; 465 466 systemd.services.NetworkManager = { 467 wantedBy = [ "network.target" ]; 468 restartTriggers = [ configFile ]; 469 470 aliases = [ "dbus-org.freedesktop.NetworkManager.service" ]; 471 472 serviceConfig = { 473 StateDirectory = "NetworkManager"; 474 StateDirectoryMode = 755; # not sure if this really needs to be 755 475 }; 476 }; 477 478 systemd.services.NetworkManager-wait-online = { 479 wantedBy = [ "network-online.target" ]; 480 }; 481 482 systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; 483 484 systemd.services.NetworkManager-dispatcher = { 485 wantedBy = [ "network.target" ]; 486 restartTriggers = [ configFile overrideNameserversScript ]; 487 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 }; 492 493 # Turn off NixOS' network management when networking is managed entirely by NetworkManager 494 networking = mkMerge [ 495 (mkIf (!delegateWireless) { 496 useDHCP = false; 497 }) 498 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 } 510 511 (mkIf cfg.enableStrongSwan { 512 networkmanager.plugins = [ pkgs.networkmanager_strongswan ]; 513 }) 514 515 (mkIf enableIwd { 516 wireless.iwd.enable = true; 517 }) 518 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 ]; 530 531 boot.kernelModules = [ "ctr" ]; 532 533 security.polkit.enable = true; 534 security.polkit.extraConfig = polkitConf; 535 536 services.dbus.packages = packages 537 ++ optional cfg.enableStrongSwan pkgs.strongswanNM 538 ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq; 539 540 services.udev.packages = packages; 541 }; 542}