diff --git a/configs/nixos/hosts/pikachu/default.nix b/configs/nixos/hosts/pikachu/default.nix index 8df5a32..703c7b3 100644 --- a/configs/nixos/hosts/pikachu/default.nix +++ b/configs/nixos/hosts/pikachu/default.nix @@ -65,6 +65,13 @@ }; registry.address.pikachu.self = "10.23.10.102"; + registry.route.pikachu.self = { + domain = "valheim.alexion.dev"; + udp.ports = [ + config.modules.services.games.valheim.port + (config.modules.services.games.valheim.port + 1) + ]; + }; modules.services.games.valheim = { enable = true; diff --git a/configs/nixos/modules/services/reverse-proxy.nix b/configs/nixos/modules/services/reverse-proxy.nix index 5670d62..916ca29 100644 --- a/configs/nixos/modules/services/reverse-proxy.nix +++ b/configs/nixos/modules/services/reverse-proxy.nix @@ -30,7 +30,9 @@ let generatedVirtualHostList = lib.concatLists ( lib.mapAttrsToList ( host: routes: - lib.mapAttrsToList (entry: _route: mkVirtualHost host entry) routes + lib.mapAttrsToList (entry: _route: mkVirtualHost host entry) ( + lib.filterAttrs (_entry: route: route.port != null) routes + ) ) registry.route ); @@ -59,6 +61,31 @@ let throw (builtins.head duplicateDomainMessages) else lib.listToAttrs generatedVirtualHostList; + + mkUdpStream = host: entry: port: + let + compositeEntry = registry.join host entry; + address = compositeEntry.address or (throw "no matching entry in registry.address for ${mkRegistryPath "route" host entry}"); + in + { + inherit host entry port address; + }; + + generatedUdpStreams = lib.concatLists ( + lib.mapAttrsToList ( + host: routes: + lib.concatLists ( + lib.mapAttrsToList (entry: route: map (mkUdpStream host entry) route.udp.ports) routes + ) + ) registry.route + ); + + generatedStreamConfig = lib.concatMapStringsSep "\n" (stream: '' + server { + listen ${toString stream.port} udp; + proxy_pass ${stream.address}:${toString stream.port}; + } + '') generatedUdpStreams; in { options.modules.services.reverse-proxy = { @@ -70,6 +97,7 @@ in 80 443 ]; + networking.firewall.allowedUDPPorts = map (stream: stream.port) generatedUdpStreams; security.acme = { acceptTerms = true; @@ -89,6 +117,7 @@ in recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; + streamConfig = lib.mkIf (generatedUdpStreams != [ ]) generatedStreamConfig; virtualHosts = generatedVirtualHosts; }; }; diff --git a/configs/nixos/registry/route.nix b/configs/nixos/registry/route.nix index bee18a9..b4ff6dc 100644 --- a/configs/nixos/registry/route.nix +++ b/configs/nixos/registry/route.nix @@ -5,6 +5,13 @@ assertion = lib.registry.predicates.uniqueValue (entry: entry.value.domain); message = "Route domains must be unique."; } + { + assertion = entries: + lib.registry.predicates.uniqueValue (entry: entry.value) ( + lib.concatMap (entry: map (port: entry // { value = port; }) entry.value.udp.ports) entries + ); + message = "Route UDP ports must be unique."; + } ]; type = lib.types.submodule { @@ -12,14 +19,25 @@ domain = lib.mkOption { type = lib.types.str; example = "files.alexion.dev"; - description = "Public domain served by this reverse-proxy route entry."; + description = "Public domain served by this route entry."; }; port = lib.mkOption { - type = lib.types.ints.between 1 65535; + type = lib.types.nullOr lib.types.port; + default = null; example = 3923; description = "Backend TCP port served by the matching address registry entry."; }; + + udp.ports = lib.mkOption { + type = lib.types.listOf lib.types.port; + default = [ ]; + example = [ + 2456 + 2457 + ]; + description = "UDP ports served by the matching address registry entry."; + }; }; }; }