diff --git a/services/wireguard.nix b/services/wireguard.nix index bc5ea12..9de1226 100644 --- a/services/wireguard.nix +++ b/services/wireguard.nix @@ -19,6 +19,43 @@ let lib.filterAttrs shouldConnectToPeer allOthers; extIface = config.my.networking.externalInterface; + + mkInterface = clientAllowedIPs: { + listenPort = cfg.port; + address = with cfg.net; with lib; [ + "${v4.subnet}.${toString thisPeer.clientNum}/${toString v4.mask}" + "${v6.subnet}::${toString thisPeer.clientNum}/${toHexString v6.mask}" + ]; + # Insecure, I don't care + privateKey = thisPeer.privateKey; + + peers = + let + mkPeer = _: peer: lib.mkMerge [ + { + inherit (peer) publicKey; + } + + (lib.optionalAttrs thisPeerIsServer { + # Only forward from server to clients + allowedIPs = with cfg.net; [ + "${v4.subnet}.${toString peer.clientNum}/32" + "${v6.subnet}::${toString peer.clientNum}/128" + ]; + }) + + (lib.optionalAttrs (!thisPeerIsServer) { + # Forward all traffic through wireguard to server + allowedIPs = clientAllowedIPs; + # Roaming clients need to keep NAT-ing active + persistentKeepalive = 10; + # We know that `peer` is a server, set up the endpoint + endpoint = "${peer.externalIp}:${toString cfg.port}"; + }) + ]; + in + lib.mapAttrsToList mkPeer otherPeers; + }; in { options.my.services.wireguard = with lib; { @@ -94,67 +131,68 @@ in }; }; }; + + internal = { + enable = mkEnableOption '' + Additional interface which does not route WAN traffic, but gives access + to wireguard peers. + Is useful for accessing DNS and other internal services, without having + to route all traffic through wireguard. + Is automatically disabled on server, and enabled otherwise. + '' // { + default = !thisPeerIsServer; + }; + + name = mkOption { + type = types.str; + default = "lan"; + example = "internal"; + description = "Which name to use for this interface"; + }; + + startAtBoot = my.mkDisableOption '' + Should the internal VPN service be started at boot. + ''; + }; }; config = lib.mkIf cfg.enable (lib.mkMerge [ + # Normal interface should route all traffic from client through server { - networking.wg-quick.interfaces."${cfg.iface}" = { - listenPort = cfg.port; - address = with cfg.net; with lib; [ - "${v4.subnet}.${toString thisPeer.clientNum}/${toString v4.mask}" - "${v6.subnet}::${toString thisPeer.clientNum}/${toHexString v6.mask}" - ]; - # Insecure, I don't care - privateKey = thisPeer.privateKey; - - peers = - let - mkPeer = _: peer: lib.mkMerge [ - { - inherit (peer) publicKey; - } - - (lib.optionalAttrs thisPeerIsServer { - # Only forward from server to clients - allowedIPs = with cfg.net; [ - "${v4.subnet}.${toString peer.clientNum}/32" - "${v6.subnet}::${toString peer.clientNum}/128" - ]; - }) - - (lib.optionalAttrs (!thisPeerIsServer) { - # Forward all traffic through wireguard to server - allowedIPs = with cfg.net; [ - "0.0.0.0/0" - "::/0" - ]; - # Roaming clients need to keep NAT-ing active - persistentKeepalive = 10; - # We know that `peer` is a server, set up the endpoint - endpoint = "${peer.externalIp}:${toString cfg.port}"; - }) - ]; - in - lib.mapAttrsToList mkPeer otherPeers; - }; + networking.wg-quick.interfaces."${cfg.iface}" = mkInterface [ + "0.0.0.0/0" + "::/0" + ]; } - # Set up clients to use configured DNS servers - (lib.mkIf (!thisPeerIsServer) { - networking.wg-quick.interfaces."${cfg.iface}".dns = - let - toInternalIps = peer: [ - "${cfg.net.v4.subnet}.${toString peer.clientNum}" - "${cfg.net.v6.subnet}::${toString peer.clientNum}" - ]; - # We know that `otherPeers` is an attribute set of servers - internalIps = lib.flatten - (lib.mapAttrsToList (_: peer: toInternalIps peer) otherPeers); - internalServers = lib.optionals cfg.dns.useInternal internalIps; - in - internalServers ++ cfg.dns.additionalServers; + # Additional inteface is only used to get access to "LAN" from wireguard + (lib.mkIf cfg.internal.enable { + networking.wg-quick.interfaces."${cfg.internal.name}" = mkInterface [ + "${cfg.net.v4.subnet}.0/${toString cfg.net.v4.mask}" + "${cfg.net.v6.subnet}::/${toString cfg.net.v6.mask}" + ]; }) + # Set up clients to use configured DNS servers on both interfaces + ( + let + toInternalIps = peer: [ + "${cfg.net.v4.subnet}.${toString peer.clientNum}" + "${cfg.net.v6.subnet}::${toString peer.clientNum}" + ]; + # We know that `otherPeers` is an attribute set of servers + internalIps = lib.flatten + (lib.mapAttrsToList (_: peer: toInternalIps peer) otherPeers); + internalServers = lib.optionals cfg.dns.useInternal internalIps; + dns = internalServers ++ cfg.dns.additionalServers; + in + lib.mkIf (!thisPeerIsServer) { + networking.wg-quick.interfaces."${cfg.iface}".dns = dns; + networking.wg-quick.interfaces."${cfg.internal.name}".dns = + lib.mkIf cfg.internal.enable dns; + } + ) + # Expose port { networking.firewall.allowedUDPPorts = [ cfg.port ]; @@ -199,5 +237,10 @@ in (lib.mkIf (!cfg.startAtBoot) { systemd.services."wg-quick-${cfg.iface}".wantedBy = lib.mkForce [ ]; }) + + # Same idea, for internal-only interface + (lib.mkIf (cfg.internal.enable && !cfg.internal.startAtBoot) { + systemd.services."wg-quick-${cfg.internal.name}".wantedBy = lib.mkForce [ ]; + }) ]); }