{ pkgs, lib, config, nodes ? { }, ... }: with lib; let redirected-ports-mod.options = { external = mkOption { type = types.port; }; internal = mkOption { type = types.port; }; }; redirection-port-type = types.coercedTo types.port (port: { external = port; internal = port; }) (types.submodule redirected-ports-mod); fqdn = config.kat.fqdn; hostname = config.networking.hostName; cfg = config.kat.proxies; error-webroot = pkgs.runCommand "${hostname}-error-webroot" { } '' mkdir $out cp ${./418.html} $out/418.html cp ${./599.html} $out/599.html substituteInPlace $out/* \ --replace-fail 'config.hostname' "${hostname}" ''; redirections = lib.fold (a: b: { domains = a.domains ++ b.domains; tcp = a.tcp ++ b.tcp; udp = a.udp ++ b.udp; vhosts = a.vhosts // b.vhosts; }) { domains = [ ]; tcp = [ ]; udp = [ ]; vhosts = { }; } ( map ( host: let fqdn = nodes.${host}.config.kat.fqdn; host-cfg = nodes.${host}.config.kat.proxies; in { domains = [ fqdn ] ++ host-cfg.aliases; tcp = map ( { external, internal }: { input = external; ip = host-cfg.ip; out = internal; } ) host-cfg.open-tcp; udp = map ( { external, internal }: { input = external; ip = host-cfg.ip; out = internal; } ) host-cfg.open-udp; vhosts.${fqdn} = { ip = host-cfg.ip; aliases = host-cfg.aliases; }; } ) cfg.redirects ); in { options.kat.proxies = { enable = mkEnableOption "kat-proxies autoconfiguration" // { default = cfg.redirects != [ ]; defaultText = ''config.kat.proxies.redirects != [ ]''; }; ip = mkOption { type = types.str; }; aliases = mkOption { type = types.listOf types.str; default = [ ]; }; open-tcp = mkOption { type = types.listOf redirection-port-type; default = [ ]; }; open-udp = mkOption { type = types.listOf redirection-port-type; default = [ ]; }; redirects = mkOption { type = types.listOf types.str; default = [ ]; }; test = mkOption { type = types.raw; }; }; config = mkIf cfg.enable { kat.proxies = { test = redirections; aliases = redirections.domains; open-tcp = map ({ input, ... }: input) redirections.tcp; open-udp = map ({ input, ... }: input) redirections.udp; }; networking.firewall = { allowedTCPPorts = [ 80 443 ] ++ map ({ internal, ... }: internal) cfg.open-tcp; allowedUDPPorts = map ({ internal, ... }: internal) cfg.open-udp; }; security.acme.certs.${fqdn}.extraDomainNames = cfg.aliases; services.nginx = { enable = true; virtualHosts = mapAttrs ( _: { aliases, ip }: { useACMEHost = fqdn; forceSSL = true; acmeFallbackHost = ip; acmeFallbackRecommendedProxySettings = true; serverAliases = aliases; locations = { "/.${hostname}/" = { extraConfig = '' internal; error_page 404 =418 /.${hostname}/418.html; ''; alias = "${error-webroot}/"; }; "/" = { recommendedProxySettings = true; proxyPass = "https://${ip}/"; extraConfig = '' proxy_set_header Connection '''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; error_page 502 =599 "/.${hostname}/599.html"; ''; }; }; } ) redirections.vhosts // { ${fqdn} = { default = true; enableACME = true; addSSL = true; locations = { "/.${hostname}/" = { extraConfig = '' internal; error_page 404 =418 /.${hostname}/418.html; ''; alias = "${error-webroot}/"; }; "/" = { extraConfig = '' return 418; error_page 418 =418 /.${hostname}/418.html; ''; }; }; }; }; streamConfig = concatStringsSep "\n" ( map ( { input, ip, out, }: '' server { listen ${toString input}; listen [::]:${toString input}; proxy_pass ${ip}:${toString out}; } '' ) redirections.tcp ++ map ( { input, ip, out, }: '' server { listen ${toString input} udp; listen [::]:${toString input} udp; proxy_pass ${ip}:${toString out}; } '' ) redirections.udp ); }; }; }