main
1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 inherit (lib)
9 mkEnableOption
10 mkIf
11 mkOption
12 types
13 ;
14 cfg = config.services.wireguard.server;
15
16 # Detect if nftables is enabled
17 usingNftables = config.networking.nftables.enable;
18in
19{
20 options = {
21 services.wireguard.server = {
22 enable = mkEnableOption "Enable a wireguard server";
23 ips = mkOption {
24 type = with types; listOf str;
25 description = ''
26 The peer IPs
27 '';
28 };
29 peers = mkOption {
30 default = [ ];
31 description = "Peers linked to the interface.";
32 type = with types; listOf anything;
33 };
34 interface = mkOption {
35 type = types.str;
36 default = "wg0";
37 description = ''
38 Name of the wireguard interface. Override when a host already uses
39 wg0 as a client (e.g. lutece, which is a mesh client on wg0 and a
40 travel exit server on wg1).
41 '';
42 };
43 listenPort = mkOption {
44 type = types.int;
45 default = 51820;
46 description = ''
47 UDP port the server listens on. Opened in the firewall.
48 '';
49 };
50 privateKeyFile = mkOption {
51 type = types.str;
52 default = "/etc/wireguard/private.key";
53 description = ''
54 Path to the interface private key. Must differ from the client key
55 when a host runs both roles.
56 '';
57 };
58 natSource = mkOption {
59 type = types.str;
60 default = "10.100.0.0/24";
61 description = ''
62 Source subnet to masquerade. Applied on POSTROUTING without an output
63 interface, so it covers both internet egress and traffic forwarded
64 into another wireguard interface.
65 '';
66 };
67 mtu = mkOption {
68 type = with types; nullOr int;
69 default = 1420;
70 description = ''
71 MTU size for the WireGuard interface.
72 Common values: 1420 (conservative), 1380 (for PPPoE).
73 If null, uses system default.
74 '';
75 };
76 };
77 };
78 config = mkIf cfg.enable {
79 environment.systemPackages = [ pkgs.wireguard-tools ];
80 boot.kernel.sysctl."net.ipv4.ip_forward" = lib.mkForce 1; # FIXME should probably be mkDefault
81
82 # Firewall configuration - supports both iptables and nftables
83 networking.firewall = {
84 allowedUDPPorts = [ cfg.listenPort ];
85 trustedInterfaces = [ cfg.interface ];
86
87 # iptables rules (used when nftables is disabled)
88 extraCommands = mkIf (!usingNftables) ''
89 iptables -t nat -A POSTROUTING -s ${cfg.natSource} -j MASQUERADE
90 iptables -A FORWARD -i wg+ -j ACCEPT
91 '';
92 };
93
94 # nftables rules (used when nftables is enabled)
95 networking.nftables.tables = mkIf usingNftables {
96 wireguard-nat = {
97 family = "ip";
98 content = ''
99 chain postrouting {
100 type nat hook postrouting priority srcnat; policy accept;
101 ip saddr ${cfg.natSource} masquerade
102 }
103
104 chain forward {
105 type filter hook forward priority filter; policy accept;
106 iifname "wg*" accept
107 }
108 '';
109 };
110 };
111
112 networking.wireguard.enable = true;
113 networking.wireguard.interfaces = {
114 "${cfg.interface}" = {
115 inherit (cfg)
116 ips
117 peers
118 listenPort
119 privateKeyFile
120 ;
121 }
122 // lib.optionalAttrs (cfg.mtu != null) { inherit (cfg) mtu; };
123 };
124 };
125}