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}