at 18.09-beta 18 kB view raw
1{ config, lib, pkgs, ... }: 2 3with pkgs; 4with lib; 5 6let 7 cfg = config.networking.networkmanager; 8 9 dynamicHostsEnabled = 10 cfg.dynamicHosts.enable && cfg.dynamicHosts.hostsDirs != {}; 11 12 # /var/lib/misc is for dnsmasq.leases. 13 stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc"; 14 15 configFile = writeText "NetworkManager.conf" '' 16 [main] 17 plugins=keyfile 18 dhcp=${cfg.dhcp} 19 dns=${cfg.dns} 20 21 [keyfile] 22 ${optionalString (cfg.unmanaged != []) 23 ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''} 24 25 [logging] 26 level=${cfg.logLevel} 27 28 [connection] 29 ipv6.ip6-privacy=2 30 ethernet.cloned-mac-address=${cfg.ethernet.macAddress} 31 wifi.cloned-mac-address=${cfg.wifi.macAddress} 32 ${optionalString (cfg.wifi.powersave != null) 33 ''wifi.powersave=${if cfg.wifi.powersave then "3" else "2"}''} 34 35 [device] 36 wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"} 37 38 ${cfg.extraConfig} 39 ''; 40 41 /* 42 [network-manager] 43 Identity=unix-group:networkmanager 44 Action=org.freedesktop.NetworkManager.* 45 ResultAny=yes 46 ResultInactive=no 47 ResultActive=yes 48 49 [modem-manager] 50 Identity=unix-group:networkmanager 51 Action=org.freedesktop.ModemManager* 52 ResultAny=yes 53 ResultInactive=no 54 ResultActive=yes 55 */ 56 polkitConf = '' 57 polkit.addRule(function(action, subject) { 58 if ( 59 subject.isInGroup("networkmanager") 60 && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 61 || action.id.indexOf("org.freedesktop.ModemManager") == 0 62 )) 63 { return polkit.Result.YES; } 64 }); 65 ''; 66 67 ns = xs: writeText "nameservers" ( 68 concatStrings (map (s: "nameserver ${s}\n") xs) 69 ); 70 71 overrideNameserversScript = writeScript "02overridedns" '' 72 #!/bin/sh 73 tmp=`${coreutils}/bin/mktemp` 74 ${gnused}/bin/sed '/nameserver /d' /etc/resolv.conf > $tmp 75 ${gnugrep}/bin/grep 'nameserver ' /etc/resolv.conf | \ 76 ${gnugrep}/bin/grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns 77 ${optionalString (cfg.appendNameservers != []) "${coreutils}/bin/cat $tmp $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf"} 78 ${optionalString (cfg.insertNameservers != []) "${coreutils}/bin/cat $tmp ${ns cfg.insertNameservers} $tmp.ns > /etc/resolv.conf"} 79 ${coreutils}/bin/rm -f $tmp $tmp.ns 80 ''; 81 82 dispatcherTypesSubdirMap = { 83 "basic" = ""; 84 "pre-up" = "pre-up.d/"; 85 "pre-down" = "pre-down.d/"; 86 }; 87 88 macAddressOpt = mkOption { 89 type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]); 90 default = "preserve"; 91 example = "00:11:22:33:44:55"; 92 description = '' 93 "XX:XX:XX:XX:XX:XX": MAC address of the interface. 94 <literal>permanent</literal>: use the permanent MAC address of the device. 95 <literal>preserve</literal>: dont change the MAC address of the device upon activation. 96 <literal>random</literal>: generate a randomized value upon each connect. 97 <literal>stable</literal>: generate a stable, hashed MAC address. 98 ''; 99 }; 100 101in { 102 103 ###### interface 104 105 options = { 106 107 networking.networkmanager = { 108 109 enable = mkOption { 110 type = types.bool; 111 default = false; 112 description = '' 113 Whether to use NetworkManager to obtain an IP address and other 114 configuration for all network interfaces that are not manually 115 configured. If enabled, a group <literal>networkmanager</literal> 116 will be created. Add all users that should have permission 117 to change network settings to this group. 118 ''; 119 }; 120 121 extraConfig = mkOption { 122 type = types.lines; 123 default = ""; 124 description = '' 125 Configuration appended to the generated NetworkManager.conf. 126 ''; 127 }; 128 129 unmanaged = mkOption { 130 type = types.listOf types.string; 131 default = []; 132 description = '' 133 List of interfaces that will not be managed by NetworkManager. 134 Interface name can be specified here, but if you need more fidelity 135 see "Device List Format" in NetworkManager.conf man page. 136 ''; 137 }; 138 139 # Ugly hack for using the correct gnome3 packageSet 140 basePackages = mkOption { 141 type = types.attrsOf types.package; 142 default = { inherit networkmanager modemmanager wpa_supplicant 143 networkmanager-openvpn networkmanager-vpnc 144 networkmanager-openconnect networkmanager-fortisslvpn 145 networkmanager-l2tp networkmanager-iodine; }; 146 internal = true; 147 }; 148 149 packages = mkOption { 150 type = types.listOf types.path; 151 default = [ ]; 152 description = '' 153 Extra packages that provide NetworkManager plugins. 154 ''; 155 apply = list: (attrValues cfg.basePackages) ++ list; 156 }; 157 158 dhcp = mkOption { 159 type = types.enum [ "dhclient" "dhcpcd" "internal" ]; 160 default = "dhclient"; 161 description = '' 162 Which program (or internal library) should be used for DHCP. 163 ''; 164 }; 165 166 logLevel = mkOption { 167 type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ]; 168 default = "WARN"; 169 description = '' 170 Set the default logging verbosity level. 171 ''; 172 }; 173 174 appendNameservers = mkOption { 175 type = types.listOf types.str; 176 default = []; 177 description = '' 178 A list of name servers that should be appended 179 to the ones configured in NetworkManager or received by DHCP. 180 ''; 181 }; 182 183 insertNameservers = mkOption { 184 type = types.listOf types.str; 185 default = []; 186 description = '' 187 A list of name servers that should be inserted before 188 the ones configured in NetworkManager or received by DHCP. 189 ''; 190 }; 191 192 ethernet.macAddress = macAddressOpt; 193 194 wifi = { 195 macAddress = macAddressOpt; 196 197 powersave = mkOption { 198 type = types.nullOr types.bool; 199 default = null; 200 description = '' 201 Whether to enable Wi-Fi power saving. 202 ''; 203 }; 204 205 scanRandMacAddress = mkOption { 206 type = types.bool; 207 default = true; 208 description = '' 209 Whether to enable MAC address randomization of a Wi-Fi device 210 during scanning. 211 ''; 212 }; 213 }; 214 215 dns = mkOption { 216 type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ]; 217 default = "default"; 218 description = '' 219 Set the DNS (<literal>resolv.conf</literal>) processing mode. 220 </para> 221 <para> 222 Options: 223 <variablelist> 224 <varlistentry> 225 <term><literal>"default"</literal></term> 226 <listitem><para> 227 NetworkManager will update <literal>/etc/resolv.conf</literal> to 228 reflect the nameservers provided by currently active connections. 229 </para></listitem> 230 </varlistentry> 231 <varlistentry> 232 <term><literal>"dnsmasq"</literal></term> 233 <listitem> 234 <para> 235 Enable NetworkManager's dnsmasq integration. NetworkManager will 236 run dnsmasq as a local caching nameserver, using a "split DNS" 237 configuration if you are connected to a VPN, and then update 238 <literal>resolv.conf</literal> to point to the local nameserver. 239 </para> 240 <para> 241 It is possible to pass custom options to the dnsmasq instance by 242 adding them to files in the 243 <literal>/etc/NetworkManager/dnsmasq.d/</literal> directory. 244 </para> 245 <para> 246 When multiple upstream servers are available, dnsmasq will 247 initially contact them in parallel and then use the fastest to 248 respond, probing again other servers after some time. This 249 behavior can be modified passing the 250 <literal>all-servers</literal> or <literal>strict-order</literal> 251 options to dnsmasq (see the manual page for more details). 252 </para> 253 <para> 254 Note that this option causes NetworkManager to launch and manage 255 its own instance of the dnsmasq daemon, which is 256 <emphasis>not</emphasis> the same as setting 257 <literal>services.dnsmasq.enable = true;</literal>. 258 </para> 259 </listitem> 260 </varlistentry> 261 <varlistentry> 262 <term><literal>"unbound"</literal></term> 263 <listitem><para> 264 NetworkManager will talk to unbound and dnssec-triggerd, 265 providing a "split DNS" configuration with DNSSEC support. 266 <literal>/etc/resolv.conf</literal> will be managed by 267 dnssec-trigger daemon. 268 </para></listitem> 269 </varlistentry> 270 <varlistentry> 271 <term><literal>"systemd-resolved"</literal></term> 272 <listitem><para> 273 NetworkManager will push the DNS configuration to systemd-resolved. 274 </para></listitem> 275 </varlistentry> 276 <varlistentry> 277 <term><literal>"none"</literal></term> 278 <listitem><para> 279 NetworkManager will not modify resolv.conf. 280 </para></listitem> 281 </varlistentry> 282 </variablelist> 283 ''; 284 }; 285 286 dispatcherScripts = mkOption { 287 type = types.listOf (types.submodule { 288 options = { 289 source = mkOption { 290 type = types.path; 291 description = '' 292 Path to the hook script. 293 ''; 294 }; 295 296 type = mkOption { 297 type = types.enum (attrNames dispatcherTypesSubdirMap); 298 default = "basic"; 299 description = '' 300 Dispatcher hook type. Look up the hooks described at 301 <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.html">https://developer.gnome.org/NetworkManager/stable/NetworkManager.html</link> 302 and choose the type depending on the output folder. 303 You should then filter the event type (e.g., "up"/"down") from within your script. 304 ''; 305 }; 306 }; 307 }); 308 default = []; 309 example = literalExample '' 310 [ { 311 source = pkgs.writeText "upHook" ''' 312 313 if [ "$2" != "up" ]; then 314 logger "exit: event $2 != up" 315 fi 316 317 # coreutils and iproute are in PATH too 318 logger "Device $DEVICE_IFACE coming up" 319 '''; 320 type = "basic"; 321 } ]''; 322 description = '' 323 A list of scripts which will be executed in response to network events. 324 ''; 325 }; 326 327 enableStrongSwan = mkOption { 328 type = types.bool; 329 default = false; 330 description = '' 331 Enable the StrongSwan plugin. 332 </para><para> 333 If you enable this option the 334 <literal>networkmanager_strongswan</literal> plugin will be added to 335 the <option>networking.networkmanager.packages</option> option 336 so you don't need to to that yourself. 337 ''; 338 }; 339 340 dynamicHosts = { 341 enable = mkOption { 342 type = types.bool; 343 default = false; 344 description = '' 345 Enabling this option requires the 346 <option>networking.networkmanager.dns</option> option to be 347 set to <literal>dnsmasq</literal>. If enabled, the directories 348 defined by the 349 <option>networking.networkmanager.dynamicHosts.hostsDirs</option> 350 option will be set up when the service starts. The dnsmasq instance 351 managed by NetworkManager will then watch those directories for 352 hosts files (see the <literal>--hostsdir</literal> option of 353 dnsmasq). This way a non-privileged user can add or override DNS 354 entries on the local system (depending on what hosts directories 355 that are configured).. 356 ''; 357 }; 358 hostsDirs = mkOption { 359 type = with types; attrsOf (submodule { 360 options = { 361 user = mkOption { 362 type = types.str; 363 default = "root"; 364 description = '' 365 The user that will own the hosts directory. 366 ''; 367 }; 368 group = mkOption { 369 type = types.str; 370 default = "root"; 371 description = '' 372 The group that will own the hosts directory. 373 ''; 374 }; 375 }; 376 }); 377 default = {}; 378 description = '' 379 Defines a set of directories (relative to 380 <literal>/run/NetworkManager/hostdirs</literal>) that dnsmasq will 381 watch for hosts files. 382 ''; 383 }; 384 }; 385 }; 386 }; 387 388 389 ###### implementation 390 391 config = mkIf cfg.enable { 392 393 assertions = [ 394 { assertion = config.networking.wireless.enable == false; 395 message = "You can not use networking.networkmanager with networking.wireless"; 396 } 397 { assertion = !dynamicHostsEnabled || (dynamicHostsEnabled && cfg.dns == "dnsmasq"); 398 message = '' 399 To use networking.networkmanager.dynamicHosts you also need to set 400 networking.networkmanager.dns = "dnsmasq" 401 ''; 402 } 403 ]; 404 405 environment.etc = with cfg.basePackages; [ 406 { source = configFile; 407 target = "NetworkManager/NetworkManager.conf"; 408 } 409 { source = "${networkmanager-openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name"; 410 target = "NetworkManager/VPN/nm-openvpn-service.name"; 411 } 412 { source = "${networkmanager-vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name"; 413 target = "NetworkManager/VPN/nm-vpnc-service.name"; 414 } 415 { source = "${networkmanager-openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name"; 416 target = "NetworkManager/VPN/nm-openconnect-service.name"; 417 } 418 { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name"; 419 target = "NetworkManager/VPN/nm-fortisslvpn-service.name"; 420 } 421 { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name"; 422 target = "NetworkManager/VPN/nm-l2tp-service.name"; 423 } 424 { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name"; 425 target = "NetworkManager/VPN/nm-strongswan-service.name"; 426 } 427 { source = "${networkmanager-iodine}/etc/NetworkManager/VPN/nm-iodine-service.name"; 428 target = "NetworkManager/VPN/nm-iodine-service.name"; 429 } 430 ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == []) 431 { source = overrideNameserversScript; 432 target = "NetworkManager/dispatcher.d/02overridedns"; 433 } 434 ++ lib.imap1 (i: s: { 435 inherit (s) source; 436 target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}"; 437 mode = "0544"; 438 }) cfg.dispatcherScripts 439 ++ optional (dynamicHostsEnabled) 440 { target = "NetworkManager/dnsmasq.d/dyndns.conf"; 441 text = concatMapStrings (n: '' 442 hostsdir=/run/NetworkManager/hostsdirs/${n} 443 '') (attrNames cfg.dynamicHosts.hostsDirs); 444 }; 445 446 environment.systemPackages = cfg.packages; 447 448 users.groups = [{ 449 name = "networkmanager"; 450 gid = config.ids.gids.networkmanager; 451 } 452 { 453 name = "nm-openvpn"; 454 gid = config.ids.gids.nm-openvpn; 455 }]; 456 users.users = [{ 457 name = "nm-openvpn"; 458 uid = config.ids.uids.nm-openvpn; 459 extraGroups = [ "networkmanager" ]; 460 } 461 { 462 name = "nm-iodine"; 463 isSystemUser = true; 464 group = "networkmanager"; 465 }]; 466 467 systemd.packages = cfg.packages; 468 469 systemd.services."network-manager" = { 470 wantedBy = [ "network.target" ]; 471 restartTriggers = [ configFile ]; 472 473 preStart = '' 474 mkdir -m 700 -p /etc/NetworkManager/system-connections 475 mkdir -m 700 -p /etc/ipsec.d 476 mkdir -m 755 -p ${stateDirs} 477 ''; 478 }; 479 480 systemd.services.nm-setup-hostsdirs = mkIf dynamicHostsEnabled { 481 wantedBy = [ "network-manager.service" ]; 482 before = [ "network-manager.service" ]; 483 partOf = [ "network-manager.service" ]; 484 script = concatStrings (mapAttrsToList (n: d: '' 485 mkdir -p "/run/NetworkManager/hostsdirs/${n}" 486 chown "${d.user}:${d.group}" "/run/NetworkManager/hostsdirs/${n}" 487 chmod 0775 "/run/NetworkManager/hostsdirs/${n}" 488 '') cfg.dynamicHosts.hostsDirs); 489 serviceConfig = { 490 Type = "oneshot"; 491 RemainAfterExist = true; 492 }; 493 }; 494 495 systemd.services."NetworkManager-dispatcher" = { 496 wantedBy = [ "network.target" ]; 497 restartTriggers = [ configFile ]; 498 499 # useful binaries for user-specified hooks 500 path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ]; 501 }; 502 503 # Turn off NixOS' network management 504 networking = { 505 useDHCP = false; 506 # use mkDefault to trigger the assertion about the conflict above 507 wireless.enable = lib.mkDefault false; 508 }; 509 510 security.polkit.extraConfig = polkitConf; 511 512 networking.networkmanager.packages = 513 mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ]; 514 515 services.dbus.packages = 516 optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages; 517 518 services.udev.packages = cfg.packages; 519 }; 520}