at 25.11-pre 26 kB view raw
1{ 2 config, 3 lib, 4 options, 5 pkgs, 6 ... 7}: 8 9with lib; 10 11let 12 13 cfg = config.networking.wireguard; 14 opt = options.networking.wireguard; 15 16 kernel = config.boot.kernelPackages; 17 18 # interface options 19 20 interfaceOpts = 21 { ... }: 22 { 23 24 options = { 25 26 type = mkOption { 27 example = "amneziawg"; 28 default = "wireguard"; 29 type = types.enum [ 30 "wireguard" 31 "amneziawg" 32 ]; 33 description = '' 34 The type of the interface. Currently only "wireguard" and "amneziawg" are supported. 35 ''; 36 }; 37 38 ips = mkOption { 39 example = [ "192.168.2.1/24" ]; 40 default = [ ]; 41 type = with types; listOf str; 42 description = "The IP addresses of the interface."; 43 }; 44 45 privateKey = mkOption { 46 example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; 47 type = with types; nullOr str; 48 default = null; 49 description = '' 50 Base64 private key generated by {command}`wg genkey`. 51 52 Warning: Consider using privateKeyFile instead if you do not 53 want to store the key in the world-readable Nix store. 54 ''; 55 }; 56 57 generatePrivateKeyFile = mkOption { 58 default = false; 59 type = types.bool; 60 description = '' 61 Automatically generate a private key with 62 {command}`wg genkey`, at the privateKeyFile location. 63 ''; 64 }; 65 66 privateKeyFile = mkOption { 67 example = "/private/wireguard_key"; 68 type = with types; nullOr str; 69 default = null; 70 description = '' 71 Private key file as generated by {command}`wg genkey`. 72 ''; 73 }; 74 75 listenPort = mkOption { 76 default = null; 77 type = with types; nullOr int; 78 example = 51820; 79 description = '' 80 16-bit port for listening. Optional; if not specified, 81 automatically generated based on interface name. 82 ''; 83 }; 84 85 preSetup = mkOption { 86 example = literalExpression ''"''${pkgs.iproute2}/bin/ip netns add foo"''; 87 default = ""; 88 type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; 89 description = '' 90 Commands called at the start of the interface setup. 91 ''; 92 }; 93 94 postSetup = mkOption { 95 example = literalExpression '' 96 '''printf "nameserver 10.200.100.1" | ''${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0''' 97 ''; 98 default = ""; 99 type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; 100 description = "Commands called at the end of the interface setup."; 101 }; 102 103 preShutdown = mkOption { 104 example = literalExpression ''"''${pkgs.iproute2}/bin/ip netns del foo"''; 105 default = ""; 106 type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; 107 description = '' 108 Commands called before shutting down the interface. 109 ''; 110 }; 111 112 postShutdown = mkOption { 113 example = literalExpression ''"''${pkgs.openresolv}/bin/resolvconf -d wg0"''; 114 default = ""; 115 type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; 116 description = "Commands called after shutting down the interface."; 117 }; 118 119 table = mkOption { 120 default = "main"; 121 type = types.str; 122 description = '' 123 The kernel routing table to add this interface's 124 associated routes to. Setting this is useful for e.g. policy routing 125 ("ip rule") or virtual routing and forwarding ("ip vrf"). Both 126 numeric table IDs and table names (/etc/rt_tables) can be used. 127 Defaults to "main". 128 ''; 129 }; 130 131 peers = mkOption { 132 default = [ ]; 133 description = "Peers linked to the interface."; 134 type = with types; listOf (submodule peerOpts); 135 }; 136 137 allowedIPsAsRoutes = mkOption { 138 example = false; 139 default = true; 140 type = types.bool; 141 description = '' 142 Determines whether to add allowed IPs as routes or not. 143 ''; 144 }; 145 146 socketNamespace = mkOption { 147 default = null; 148 type = with types; nullOr str; 149 example = "container"; 150 description = '' 151 The pre-existing network namespace in which the 152 WireGuard interface is created, and which retains the socket even if the 153 interface is moved via {option}`interfaceNamespace`. When 154 `null`, the interface is created in the init namespace. 155 See [documentation](https://www.wireguard.com/netns/). 156 ''; 157 }; 158 159 interfaceNamespace = mkOption { 160 default = null; 161 type = with types; nullOr str; 162 example = "init"; 163 description = '' 164 The pre-existing network namespace the WireGuard 165 interface is moved to. The special value `init` means 166 the init namespace. When `null`, the interface is not 167 moved. 168 See [documentation](https://www.wireguard.com/netns/). 169 ''; 170 }; 171 172 fwMark = mkOption { 173 default = null; 174 type = with types; nullOr str; 175 example = "0x6e6978"; 176 description = '' 177 Mark all wireguard packets originating from 178 this interface with the given firewall mark. The firewall mark can be 179 used in firewalls or policy routing to filter the wireguard packets. 180 This can be useful for setup where all traffic goes through the 181 wireguard tunnel, because the wireguard packets need to be routed 182 differently. 183 ''; 184 }; 185 186 mtu = mkOption { 187 default = null; 188 type = with types; nullOr int; 189 example = 1280; 190 description = '' 191 Set the maximum transmission unit in bytes for the wireguard 192 interface. Beware that the wireguard packets have a header that may 193 add up to 80 bytes to the mtu. By default, the MTU is (1500 - 80) = 194 1420. However, if the MTU of the upstream network is lower, the MTU 195 of the wireguard network has to be adjusted as well. 196 ''; 197 }; 198 199 metric = mkOption { 200 default = null; 201 type = with types; nullOr int; 202 example = 700; 203 description = '' 204 Set the metric of routes related to this Wireguard interface. 205 ''; 206 }; 207 208 dynamicEndpointRefreshSeconds = mkOption { 209 default = 0; 210 example = 300; 211 type = with types; int; 212 description = '' 213 Periodically refresh the endpoint hostname or address for all peers. 214 Allows WireGuard to notice DNS and IPv4/IPv6 connectivity changes. 215 This option can be set or overridden for individual peers. 216 217 Setting this to `0` disables periodic refresh. 218 219 ::: {.warning} 220 When {option}`networking.wireguard.useNetworkd` is enabled, this 221 option deletes the Wireguard interface and brings it back up by 222 reconfiguring the network with `networkctl reload` on every refresh. 223 This could have adverse effects on your network and cause brief 224 connectivity blips. See [systemd/systemd#9911](https://github.com/systemd/systemd/issues/9911) 225 for an upstream feature request that can make this less hacky. 226 ::: 227 ''; 228 }; 229 230 extraOptions = mkOption { 231 type = 232 with types; 233 attrsOf (oneOf [ 234 str 235 int 236 ]); 237 default = { }; 238 example = { 239 Jc = 5; 240 Jmin = 10; 241 Jmax = 42; 242 S1 = 60; 243 S2 = 90; 244 H4 = 12345; 245 }; 246 description = '' 247 Extra options to append to the interface section. Can be used to define AmneziaWG-specific options. 248 ''; 249 }; 250 }; 251 252 }; 253 254 # peer options 255 256 peerOpts = self: { 257 258 options = { 259 260 name = mkOption { 261 default = 262 replaceStrings [ "/" "-" " " "+" "=" ] [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ] 263 self.config.publicKey; 264 defaultText = literalExpression "publicKey"; 265 example = "bernd"; 266 type = types.str; 267 description = "Name used to derive peer unit name."; 268 }; 269 270 publicKey = mkOption { 271 example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; 272 type = types.singleLineStr; 273 description = "The base64 public key of the peer."; 274 }; 275 276 presharedKey = mkOption { 277 default = null; 278 example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I="; 279 type = with types; nullOr str; 280 description = '' 281 Base64 preshared key generated by {command}`wg genpsk`. 282 Optional, and may be omitted. This option adds an additional layer of 283 symmetric-key cryptography to be mixed into the already existing 284 public-key cryptography, for post-quantum resistance. 285 286 Warning: Consider using presharedKeyFile instead if you do not 287 want to store the key in the world-readable Nix store. 288 ''; 289 }; 290 291 presharedKeyFile = mkOption { 292 default = null; 293 example = "/private/wireguard_psk"; 294 type = with types; nullOr str; 295 description = '' 296 File pointing to preshared key as generated by {command}`wg genpsk`. 297 Optional, and may be omitted. This option adds an additional layer of 298 symmetric-key cryptography to be mixed into the already existing 299 public-key cryptography, for post-quantum resistance. 300 ''; 301 }; 302 303 allowedIPs = mkOption { 304 example = [ 305 "10.192.122.3/32" 306 "10.192.124.1/24" 307 ]; 308 type = with types; listOf str; 309 description = '' 310 List of IP (v4 or v6) addresses with CIDR masks from 311 which this peer is allowed to send incoming traffic and to which 312 outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may 313 be specified for matching all IPv4 addresses, and ::/0 may be specified 314 for matching all IPv6 addresses.''; 315 }; 316 317 endpoint = mkOption { 318 default = null; 319 example = "demo.wireguard.io:12913"; 320 type = with types; nullOr str; 321 description = '' 322 Endpoint IP or hostname of the peer, followed by a colon, 323 and then a port number of the peer. 324 325 Warning for endpoints with changing IPs: 326 The WireGuard kernel side cannot perform DNS resolution. 327 Thus DNS resolution is done once by the `wg` userspace 328 utility, when setting up WireGuard. Consequently, if the IP address 329 behind the name changes, WireGuard will not notice. 330 This is especially common for dynamic-DNS setups, but also applies to 331 any other DNS-based setup. 332 If you do not use IP endpoints, you likely want to set 333 {option}`networking.wireguard.dynamicEndpointRefreshSeconds` 334 to refresh the IPs periodically. 335 ''; 336 }; 337 338 dynamicEndpointRefreshSeconds = mkOption { 339 default = null; 340 defaultText = literalExpression "config.networking.wireguard.interfaces.<name>.dynamicEndpointRefreshSeconds"; 341 example = 5; 342 type = with types; nullOr int; 343 description = '' 344 Periodically re-execute the `wg` utility every 345 this many seconds in order to let WireGuard notice DNS / hostname 346 changes. 347 348 Setting this to `0` disables periodic reexecution. 349 350 ::: {.note} 351 This peer-level setting is not available when {option}`networking.wireguard.useNetworkd` 352 is enabled. The interface-level setting may be used instead. 353 ::: 354 ''; 355 }; 356 357 dynamicEndpointRefreshRestartSeconds = mkOption { 358 default = null; 359 example = 5; 360 type = with types; nullOr ints.unsigned; 361 description = '' 362 When the dynamic endpoint refresh that is configured via 363 dynamicEndpointRefreshSeconds exits (likely due to a failure), 364 restart that service after this many seconds. 365 366 If set to `null` the value of 367 {option}`networking.wireguard.dynamicEndpointRefreshSeconds` 368 will be used as the default. 369 ''; 370 }; 371 372 persistentKeepalive = mkOption { 373 default = null; 374 type = with types; nullOr int; 375 example = 25; 376 description = '' 377 This is optional and is by default off, because most 378 users will not need it. It represents, in seconds, between 1 and 65535 379 inclusive, how often to send an authenticated empty packet to the peer, 380 for the purpose of keeping a stateful firewall or NAT mapping valid 381 persistently. For example, if the interface very rarely sends traffic, 382 but it might at anytime receive traffic from a peer, and it is behind 383 NAT, the interface might benefit from having a persistent keepalive 384 interval of 25 seconds; however, most users will not need this.''; 385 }; 386 387 }; 388 389 }; 390 391 wgBins = { 392 wireguard = "wg"; 393 amneziawg = "awg"; 394 }; 395 396 wgPackages = { 397 wireguard = pkgs.wireguard-tools; 398 amneziawg = pkgs.amneziawg-tools; 399 }; 400 401 generateKeyServiceUnit = 402 name: values: 403 assert values.generatePrivateKeyFile; 404 nameValuePair "wireguard-${name}-key" { 405 description = "WireGuard Tunnel - ${name} - Key Generator"; 406 wantedBy = [ "wireguard-${name}.service" ]; 407 requiredBy = [ "wireguard-${name}.service" ]; 408 before = [ "wireguard-${name}.service" ]; 409 path = [ wgPackages.${values.type} ]; 410 411 serviceConfig = { 412 Type = "oneshot"; 413 RemainAfterExit = true; 414 }; 415 416 script = '' 417 set -e 418 419 # If the parent dir does not already exist, create it. 420 # Otherwise, does nothing, keeping existing permissions intact. 421 mkdir -p --mode 0755 "${dirOf values.privateKeyFile}" 422 423 if [ ! -f "${values.privateKeyFile}" ]; then 424 # Write private key file with atomically-correct permissions. 425 (set -e; umask 077; ${wgBins.${values.type}} genkey > "${values.privateKeyFile}") 426 fi 427 ''; 428 }; 429 430 peerUnitServiceName = 431 interfaceName: peerName: dynamicRefreshEnabled: 432 let 433 refreshSuffix = optionalString dynamicRefreshEnabled "-refresh"; 434 in 435 "wireguard-${interfaceName}-peer-${peerName}${refreshSuffix}"; 436 437 dynamicRefreshSeconds = 438 interfaceCfg: peer: 439 if peer.dynamicEndpointRefreshSeconds != null then 440 peer.dynamicEndpointRefreshSeconds 441 else 442 interfaceCfg.dynamicEndpointRefreshSeconds; 443 444 generatePeerUnit = 445 { 446 interfaceName, 447 interfaceCfg, 448 peer, 449 }: 450 let 451 psk = 452 if peer.presharedKey != null then 453 pkgs.writeText "wg-psk" peer.presharedKey 454 else 455 peer.presharedKeyFile; 456 src = interfaceCfg.socketNamespace; 457 dst = interfaceCfg.interfaceNamespace; 458 ip = nsWrap "ip" src dst; 459 wg = nsWrap wgBins.${interfaceCfg.type} src dst; 460 dynamicEndpointRefreshSeconds = dynamicRefreshSeconds interfaceCfg peer; 461 dynamicRefreshEnabled = dynamicEndpointRefreshSeconds != 0; 462 # We generate a different name (a `-refresh` suffix) when `dynamicEndpointRefreshSeconds` 463 # to avoid that the same service switches `Type` (`oneshot` vs `simple`), 464 # with the intent to make scripting more obvious. 465 serviceName = peerUnitServiceName interfaceName peer.name dynamicRefreshEnabled; 466 in 467 nameValuePair serviceName { 468 description = 469 "WireGuard Peer - ${interfaceName} - ${peer.name}" 470 + optionalString (peer.name != peer.publicKey) " (${peer.publicKey})"; 471 requires = [ "wireguard-${interfaceName}.service" ]; 472 wants = [ "network-online.target" ]; 473 after = [ 474 "wireguard-${interfaceName}.service" 475 "network-online.target" 476 ]; 477 wantedBy = [ "wireguard-${interfaceName}.service" ]; 478 environment.DEVICE = interfaceName; 479 environment.WG_ENDPOINT_RESOLUTION_RETRIES = "infinity"; 480 path = with pkgs; [ 481 iproute2 482 wgPackages.${interfaceCfg.type} 483 ]; 484 485 serviceConfig = 486 if !dynamicRefreshEnabled then 487 { 488 Type = "oneshot"; 489 RemainAfterExit = true; 490 } 491 else 492 { 493 Type = "simple"; # re-executes 'wg' indefinitely 494 # Note that `Type = "oneshot"` services with `RemainAfterExit = true` 495 # cannot be used with systemd timers (see `man systemd.timer`), 496 # which is why `simple` with a loop is the best choice here. 497 # It also makes starting and stopping easiest. 498 # 499 # Restart if the service exits (e.g. when wireguard gives up after "Name or service not known" dns failures): 500 Restart = "always"; 501 RestartSec = 502 if null != peer.dynamicEndpointRefreshRestartSeconds then 503 peer.dynamicEndpointRefreshRestartSeconds 504 else 505 dynamicEndpointRefreshSeconds; 506 }; 507 unitConfig = lib.optionalAttrs dynamicRefreshEnabled { 508 StartLimitIntervalSec = 0; 509 }; 510 511 script = 512 let 513 wg_setup = concatStringsSep " " ( 514 [ ''${wg} set ${interfaceName} peer "${peer.publicKey}"'' ] 515 ++ optional (psk != null) ''preshared-key "${psk}"'' 516 ++ optional (peer.endpoint != null) ''endpoint "${peer.endpoint}"'' 517 ++ optional ( 518 peer.persistentKeepalive != null 519 ) ''persistent-keepalive "${toString peer.persistentKeepalive}"'' 520 ++ optional (peer.allowedIPs != [ ]) ''allowed-ips "${concatStringsSep "," peer.allowedIPs}"'' 521 ); 522 route_setup = optionalString interfaceCfg.allowedIPsAsRoutes ( 523 concatMapStringsSep "\n" ( 524 allowedIP: 525 ''${ip} route replace "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}" ${ 526 optionalString (interfaceCfg.metric != null) "metric ${toString interfaceCfg.metric}" 527 }'' 528 ) peer.allowedIPs 529 ); 530 in 531 '' 532 ${wg_setup} 533 ${route_setup} 534 535 ${optionalString (dynamicEndpointRefreshSeconds != 0) '' 536 # Re-execute 'wg' periodically to notice DNS / hostname changes. 537 # Note this will not time out on transient DNS failures such as DNS names 538 # because we have set 'WG_ENDPOINT_RESOLUTION_RETRIES=infinity'. 539 # Also note that 'wg' limits its maximum retry delay to 20 seconds as of writing. 540 while ${wg_setup}; do 541 sleep "${toString dynamicEndpointRefreshSeconds}"; 542 done 543 ''} 544 ''; 545 546 postStop = 547 let 548 route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes ( 549 concatMapStringsSep "\n" ( 550 allowedIP: 551 ''${ip} route delete "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}"'' 552 ) peer.allowedIPs 553 ); 554 in 555 '' 556 ${wg} set "${interfaceName}" peer "${peer.publicKey}" remove 557 ${route_destroy} 558 ''; 559 }; 560 561 # the target is required to start new peer units when they are added 562 generateInterfaceTarget = 563 name: values: 564 let 565 mkPeerUnit = 566 peer: (peerUnitServiceName name peer.name (dynamicRefreshSeconds values peer != 0)) + ".service"; 567 in 568 nameValuePair "wireguard-${name}" rec { 569 description = "WireGuard Tunnel - ${name}"; 570 wantedBy = [ "multi-user.target" ]; 571 wants = [ "wireguard-${name}.service" ] ++ map mkPeerUnit values.peers; 572 after = wants; 573 }; 574 575 generateInterfaceUnit = 576 name: values: 577 # exactly one way to specify the private key must be set 578 #assert (values.privateKey != null) != (values.privateKeyFile != null); 579 let 580 privKey = 581 if values.privateKeyFile != null then 582 values.privateKeyFile 583 else 584 pkgs.writeText "wg-key" values.privateKey; 585 src = values.socketNamespace; 586 dst = values.interfaceNamespace; 587 ipPreMove = nsWrap "ip" src null; 588 ipPostMove = nsWrap "ip" src dst; 589 wg = nsWrap wgBins.${values.type} src dst; 590 ns = if dst == "init" then "1" else dst; 591 592 in 593 nameValuePair "wireguard-${name}" { 594 description = "WireGuard Tunnel - ${name}"; 595 after = [ "network-pre.target" ]; 596 wants = [ "network.target" ]; 597 before = [ "network.target" ]; 598 environment.DEVICE = name; 599 path = with pkgs; [ 600 kmod 601 iproute2 602 wgPackages.${values.type} 603 ]; 604 605 serviceConfig = { 606 Type = "oneshot"; 607 RemainAfterExit = true; 608 }; 609 610 script = concatStringsSep "\n" ( 611 optional (!config.boot.isContainer) "modprobe ${values.type} || true" 612 ++ [ 613 values.preSetup 614 ''${ipPreMove} link add dev "${name}" type ${values.type}'' 615 ] 616 ++ optional ( 617 values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace 618 ) ''${ipPreMove} link set "${name}" netns "${ns}"'' 619 ++ optional (values.mtu != null) ''${ipPostMove} link set "${name}" mtu ${toString values.mtu}'' 620 ++ (map (ip: ''${ipPostMove} address add "${ip}" dev "${name}"'') values.ips) 621 ++ [ 622 (concatStringsSep " " ( 623 [ ''${wg} set "${name}" private-key "${privKey}"'' ] 624 ++ optional (values.listenPort != null) ''listen-port "${toString values.listenPort}"'' 625 ++ optional (values.fwMark != null) ''fwmark "${values.fwMark}"'' 626 ++ mapAttrsToList (k: v: ''${toLower k} "${toString v}"'') values.extraOptions 627 )) 628 ''${ipPostMove} link set up dev "${name}"'' 629 values.postSetup 630 ] 631 ); 632 633 postStop = '' 634 ${values.preShutdown} 635 ${ipPostMove} link del dev "${name}" 636 ${values.postShutdown} 637 ''; 638 }; 639 640 nsWrap = 641 cmd: src: dst: 642 let 643 nsList = filter (ns: ns != null) [ 644 src 645 dst 646 ]; 647 ns = last nsList; 648 in 649 if (length nsList > 0 && ns != "init") then ''ip netns exec "${ns}" "${cmd}"'' else cmd; 650 651 usingWg = any (x: x.type == "wireguard") (attrValues cfg.interfaces); 652 usingAwg = any (x: x.type == "amneziawg") (attrValues cfg.interfaces); 653in 654 655{ 656 657 ###### interface 658 659 options = { 660 661 networking.wireguard = { 662 663 enable = mkOption { 664 description = '' 665 Whether to enable WireGuard. 666 667 ::: {.note} 668 By default, this module is powered by a script-based backend. You can 669 enable the networkd backend with {option}`networking.wireguard.useNetworkd`. 670 ::: 671 ''; 672 type = types.bool; 673 # 2019-05-25: Backwards compatibility. 674 default = cfg.interfaces != { }; 675 defaultText = literalExpression "config.${opt.interfaces} != { }"; 676 example = true; 677 }; 678 679 interfaces = mkOption { 680 description = '' 681 WireGuard interfaces. 682 ''; 683 default = { }; 684 example = { 685 wg0 = { 686 ips = [ "192.168.20.4/24" ]; 687 privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; 688 peers = [ 689 { 690 allowedIPs = [ "192.168.20.1/32" ]; 691 publicKey = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; 692 endpoint = "demo.wireguard.io:12913"; 693 } 694 ]; 695 }; 696 }; 697 type = with types; attrsOf (submodule interfaceOpts); 698 }; 699 700 }; 701 702 }; 703 704 ###### implementation 705 706 config = mkIf cfg.enable ( 707 let 708 all_peers = flatten ( 709 mapAttrsToList ( 710 interfaceName: interfaceCfg: 711 map (peer: { inherit interfaceName interfaceCfg peer; }) interfaceCfg.peers 712 ) cfg.interfaces 713 ); 714 in 715 { 716 717 assertions = 718 (attrValues ( 719 mapAttrs (name: value: { 720 assertion = (value.privateKey != null) != (value.privateKeyFile != null); 721 message = "Either networking.wireguard.interfaces.${name}.privateKey or networking.wireguard.interfaces.${name}.privateKeyFile must be set."; 722 }) cfg.interfaces 723 )) 724 ++ (attrValues ( 725 mapAttrs (name: value: { 726 assertion = value.generatePrivateKeyFile -> (value.privateKey == null); 727 message = "networking.wireguard.interfaces.${name}.generatePrivateKeyFile must not be set if networking.wireguard.interfaces.${name}.privateKey is set."; 728 }) cfg.interfaces 729 )) 730 ++ map ( 731 { interfaceName, peer, ... }: 732 { 733 assertion = (peer.presharedKey == null) || (peer.presharedKeyFile == null); 734 message = "networking.wireguard.interfaces.${interfaceName} peer «${peer.publicKey}» has both presharedKey and presharedKeyFile set, but only one can be used."; 735 } 736 ) all_peers; 737 738 boot.extraModulePackages = 739 optional (usingWg && (versionOlder kernel.kernel.version "5.6")) kernel.wireguard 740 ++ optional usingAwg kernel.amneziawg; 741 boot.kernelModules = optional usingWg "wireguard" ++ optional usingAwg "amneziawg"; 742 environment.systemPackages = 743 optional usingWg pkgs.wireguard-tools 744 ++ optional usingAwg pkgs.amneziawg-tools; 745 746 systemd.services = mkIf (!cfg.useNetworkd) ( 747 (mapAttrs' generateInterfaceUnit cfg.interfaces) 748 // (listToAttrs (map generatePeerUnit all_peers)) 749 // (mapAttrs' generateKeyServiceUnit ( 750 filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces 751 )) 752 ); 753 754 systemd.targets = mkIf (!cfg.useNetworkd) (mapAttrs' generateInterfaceTarget cfg.interfaces); 755 } 756 ); 757 758}