My Nix Configuration
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 inherit (lib) types; 9 cfg = config.networking.dn42.wg; 10 11 tunnelDef = { 12 options = { 13 enable = lib.mkOption { 14 description = "Whether to enable this wireguard tunnel"; 15 type = types.bool; 16 default = true; 17 example = false; 18 }; 19 listenPort = lib.mkOption { 20 description = "The port this tunnel listens on"; 21 type = types.port; 22 example = 42000; 23 }; 24 privateKeyFile = lib.mkOption { 25 description = "Path to the tunnel's private key"; 26 type = types.nullOr types.path; 27 example = "/path/to/private/key"; 28 default = null; 29 }; 30 peerPubKey = lib.mkOption { 31 description = "Public key of the peer you're connecting to"; 32 type = types.str; 33 example = "e6kp9sca4XIzncKa9GEQwyOnMjje299Xg9ZdgXWMwHg="; 34 }; 35 peerEndpoint = lib.mkOption { 36 description = "The endpoint of the peer you're connecting to"; 37 type = types.str; 38 example = "example.com:42000"; 39 }; 40 peerAddrs = { 41 v4 = lib.mkOption { 42 description = "The peer IPv4 address to connect to in the tunnel"; 43 type = types.nullOr types.str; 44 example = "192.168.1.1"; 45 default = null; 46 }; 47 v6 = lib.mkOption { 48 description = "The peer IPv6 address to connect to in the tunnel"; 49 type = types.nullOr types.str; 50 example = "fe80::42"; 51 default = null; 52 }; 53 }; 54 localAddrs = { 55 v4 = lib.mkOption { 56 description = "The local IPv4 address to listen on in the tunnel"; 57 type = types.nullOr types.str; 58 example = "192.168.1.1"; 59 default = null; 60 }; 61 v6 = lib.mkOption { 62 description = "The local IPv6 address to listen on in the tunnel"; 63 type = types.nullOr types.str; 64 example = "fe80::42"; 65 default = null; 66 }; 67 }; 68 }; 69 }; 70in 71{ 72 options.networking.dn42.wg = { 73 tunnelDefaults = lib.mkOption { 74 description = "The default settings to apply to all tunnels"; 75 type = types.submodule tunnelDef; 76 }; 77 tunnels = lib.mkOption { 78 description = "DN42 WireGuard tunnels configuration"; 79 type = types.attrsOf (types.submodule tunnelDef); 80 }; 81 }; 82 config.networking = { 83 wireguard.interfaces = lib.mapAttrs' ( 84 name: value: 85 let 86 # Merge defaults with tunnel config, right side has priority 87 # so tunnel config overrides defaults 88 fc = cfg.tunnelDefaults // (lib.filterAttrs (_: v: v != null) value); 89 in 90 lib.nameValuePair "wg42_${name}" { 91 inherit (fc) listenPort privateKeyFile; 92 allowedIPsAsRoutes = false; 93 peers = [ 94 { 95 endpoint = fc.peerEndpoint; 96 publicKey = fc.peerPubKey; 97 allowedIPs = [ 98 "0.0.0.0/0" 99 "::/0" 100 ]; 101 dynamicEndpointRefreshSeconds = 5; 102 persistentKeepalive = 15; 103 } 104 ]; 105 postSetup = '' 106 ${lib.optionalString ( 107 fc.peerAddrs.v4 != null && fc.localAddrs.v4 != null 108 ) "${pkgs.iproute2}/bin/ip addr add ${fc.localAddrs.v4} peer ${fc.peerAddrs.v4} dev wg42_${name}"} 109 ${lib.optionalString ( 110 fc.peerAddrs.v6 != null && fc.localAddrs.v6 != null 111 ) "${pkgs.iproute2}/bin/ip addr add ${fc.localAddrs.v6} peer ${fc.peerAddrs.v6} dev wg42_${name}"} 112 ''; 113 } 114 ) (lib.filterAttrs (_: v: v.enable) cfg.tunnels); 115 firewall = { 116 trustedInterfaces = lib.mapAttrsToList (name: _: "wg42_" + name) (lib.filterAttrs (_: v: v.enable) cfg.tunnels); 117 checkReversePath = false; 118 extraInputRules = '' 119 ip saddr 172.20.0.0/14 accept 120 ip6 saddr fd00::/8 accept 121 ip6 saddr fe80::/64 accept 122 ''; 123 }; 124 }; 125}