Self-host your own digital island
1{ pkgs, config, lib, ... }:
2
3with lib;
4let cfg = config.wireguard;
5in {
6 options.wireguard = {
7 enable = mkEnableOption "wireguard";
8 server = mkOption {
9 type = with types; bool;
10 default = if cfg.hosts ? config.networking.hostName then
11 cfg.hosts.${config.networking.hostName}.server
12 else
13 false;
14 };
15 hosts = let
16 hostOps = { ... }: {
17 options = {
18 ip = mkOption { type = types.str; };
19 publicKey = mkOption { type = types.str; };
20 server = mkOption {
21 type = types.bool;
22 default = false;
23 };
24 endpoint = mkOption {
25 type = with types; nullOr str;
26 default = null;
27 # should not be null when server = true
28 };
29 persistentKeepalive = mkOption {
30 type = with types; nullOr int;
31 default = null;
32 };
33 privateKeyFile = mkOption {
34 type = types.nullOr types.str;
35 default = null;
36 };
37 };
38 };
39 in mkOption {
40 type = with types; attrsOf (submodule hostOps);
41 default = { };
42 };
43 };
44
45 config = mkIf cfg.enable {
46 environment.systemPackages = with pkgs; [ wireguard-tools ];
47 networking = mkMerge [
48 {
49 # populate /etc/hosts with hostnames and IPs
50 extraHosts = builtins.concatStringsSep "\n" (attrsets.mapAttrsToList
51 (hostName: values: "${values.ip} ${hostName}") cfg.hosts);
52
53 firewall = {
54 allowedUDPPorts = [ 51820 ];
55 checkReversePath = false;
56 };
57
58 wireguard = {
59 enable = true;
60 interfaces.wg0 = let hostName = config.networking.hostName;
61 in {
62 ips = if cfg.hosts ? hostname then
63 [ "${cfg.hosts."${hostName}".ip}/24" ]
64 else
65 [ ];
66 listenPort = 51820;
67 privateKeyFile = cfg.hosts."${hostName}".privateKeyFile;
68 peers = let
69 serverPeers = attrsets.mapAttrsToList (hostName: values:
70 if values.server then {
71 allowedIPs = [ "10.0.0.0/24" ];
72 publicKey = values.publicKey;
73 endpoint = "${values.endpoint}:51820";
74 persistentKeepalive = values.persistentKeepalive;
75 } else
76 { }) cfg.hosts;
77 # remove empty elements
78 cleanedServerPeers = lists.remove { } serverPeers;
79 in mkIf (!cfg.server) cleanedServerPeers;
80 };
81 };
82 }
83
84 (mkIf cfg.server {
85 nat = {
86 enable = true;
87 externalInterface = "enp1s0";
88 internalInterfaces = [ "wg0" ];
89 };
90 firewall = {
91 extraCommands = ''
92 iptables -I FORWARD -i wg0 -o wg0 -j ACCEPT
93 '';
94 trustedInterfaces = [ "wg0" ];
95 };
96
97 wireguard.interfaces.wg0 = {
98 # Route from wireguard to public internet, allowing server to act as VPN
99 postSetup = ''
100 ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
101 '';
102
103 postShutdown = ''
104 ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
105 '';
106
107 # add clients
108 peers = with lib.attrsets;
109 mapAttrsToList (hostName: values: {
110 allowedIPs = [ "${values.ip}/32" ];
111 publicKey = values.publicKey;
112 persistentKeepalive = values.persistentKeepalive;
113 }) cfg.hosts;
114 };
115 })
116 ];
117 };
118}