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}