proxy/intermediate/nginx.nix
Katharina Heidenreich eee6905637 feat: add livekit
2026-04-05 20:25:28 +02:00

188 lines
No EOL
6.1 KiB
Nix

let
lib = import <nixpkgs/lib>;
servicesConfig = import ../config/services.nix;
endpointsConfig = (import ../validation/endpoints.nix).getEndpointsConfig (import ../config/endpoints.nix);
tunnelOffset = 10000;
proxyOffset = servicesConfig.nginxProxyOffset;
forwardingEndpoints = builtins.filter (endpoint: endpoint.type == "forwarding") endpointsConfig;
proxyEndpoints = builtins.filter (endpoint: endpoint.type == "proxy") endpointsConfig;
forwardingTlsEndpoints = builtins.filter (endpoint: endpoint.content.tls or false) forwardingEndpoints;
forwardingHttpEndpoints = builtins.filter (endpoint: !(endpoint.content.tls or false)) forwardingEndpoints;
proxyTlsEndpoints = builtins.filter (endpoint: endpoint.force_ssl or false) proxyEndpoints;
forwardingTarget = endpoint: "127.0.0.1:${toString endpoint.content.port}";
proxyBackendPort = endpoint: proxyOffset + endpoint.listenPort;
proxyBackendTarget = endpoint: "127.0.0.1:${toString (proxyBackendPort endpoint)}";
mkTlsRouteEntryForForwarding = endpoint: {
domain = endpoint.domain;
listenPort = endpoint.listenPort;
upstream = forwardingTarget endpoint;
};
mkTlsRouteEntryForProxy = endpoint: {
domain = endpoint.domain;
listenPort = endpoint.listenPort;
upstream = proxyBackendTarget endpoint;
};
tlsRouteEntriesRaw =
map mkTlsRouteEntryForForwarding forwardingTlsEndpoints
++ map mkTlsRouteEntryForProxy proxyTlsEndpoints;
groupedTlsRoutes =
lib.foldl'
(acc: entry:
let
key = toString entry.listenPort;
previous = acc.${key} or [];
in
acc // {
${key} = previous ++ [ entry ];
}
)
{}
tlsRouteEntriesRaw;
dedupeRouteEntriesByDomain = entries:
builtins.attrValues (builtins.listToAttrs (map (entry: {
name = entry.domain;
value = entry;
}) entries));
mkTlsRouteMapForListenPort = listenPort:
let
routesForPort = groupedTlsRoutes.${toString listenPort} or [];
routeEntries = dedupeRouteEntriesByDomain routesForPort;
defaultRoute = if routeEntries == [] then null else (builtins.head routeEntries).upstream;
in
if defaultRoute == null then null else {
inherit listenPort routeEntries defaultRoute;
};
tlsListenPorts = lib.unique (map (entry: entry.listenPort) tlsRouteEntriesRaw);
tlsRouteMaps = builtins.filter (mapDef: mapDef != null) (map mkTlsRouteMapForListenPort tlsListenPorts);
renderTlsServer = tlsRouteMap:
let
portString = toString tlsRouteMap.listenPort;
in
''
map $ssl_preread_server_name $proxy_upstream_${portString} {
${lib.concatStringsSep "\n" (map (entry: "${entry.domain} ${entry.upstream};") tlsRouteMap.routeEntries)}
default ${tlsRouteMap.defaultRoute};
}
server {
listen ${portString};
proxy_pass $proxy_upstream_${portString};
ssl_preread on;
}
'';
groupedProxyEndpoints =
lib.foldl'
(acc: endpoint:
let
key = "${endpoint.domain}:${toString endpoint.listenPort}";
previous = acc.${key} or {
domain = endpoint.domain;
publicListenPort = endpoint.listenPort;
listenPort = proxyBackendPort endpoint;
locations = {};
forceSSL = false;
};
location = {
proxyPass = "http://${endpoint.content.host}:${toString endpoint.content.port}/";
} // lib.optionalAttrs (endpoint.content ? websocket && endpoint.content.websocket) {
proxyWebsockets = true;
};
in
acc // {
${key} = previous // {
listenPort = proxyBackendPort endpoint;
forceSSL = previous.forceSSL || (endpoint.force_ssl or false);
locations = previous.locations // {
${endpoint.endpoint} = location;
};
};
}
)
{}
proxyEndpoints;
proxyBackendVirtualHosts = lib.mapAttrs (_: hostConfig: {
serverName = hostConfig.domain;
listen = [
{
addr = "127.0.0.1";
port = hostConfig.listenPort;
ssl = true;
}
];
onlySSL = true;
extraConfig = ''
absolute_redirect off;
port_in_redirect off;
'';
locations = hostConfig.locations;
} // lib.optionalAttrs (hostConfig.forceSSL && servicesConfig.nginx.acmeEmail != null) {
useACMEHost = hostConfig.domain;
}) groupedProxyEndpoints;
forwardingHttpVirtualHosts = builtins.listToAttrs (map (endpoint: {
name = "forward-http-${endpoint.domain}-${toString endpoint.listenPort}";
value = {
serverName = endpoint.domain;
listen = [
{
addr = "0.0.0.0";
port = endpoint.listenPort;
}
];
locations."/".proxyPass = "http://${forwardingTarget endpoint}";
};
}) forwardingHttpEndpoints);
forwardingHttpDomains = lib.unique (map (endpoint: endpoint.domain) forwardingHttpEndpoints);
localServiceAcmeDomains =
lib.unique
(map
(hostConfig: hostConfig.domain)
(builtins.attrValues (lib.filterAttrs (_: hostConfig: hostConfig.forceSSL) groupedProxyEndpoints)));
localAcmeHttpVirtualHosts = builtins.listToAttrs (map (domain: {
name = "acme-http-${domain}-80";
value = {
serverName = domain;
listen = [
{
addr = "0.0.0.0";
port = 80;
}
];
locations."^~ /.well-known/acme-challenge/" = {
root = "/var/lib/acme/acme-challenge";
extraConfig = ''
auth_basic off;
auth_request off;
'';
};
locations."/" = {
return = "301 https://$host$request_uri";
};
};
}) (builtins.filter (domain: !(builtins.elem domain forwardingHttpDomains)) localServiceAcmeDomains));
virtualHosts = forwardingHttpVirtualHosts // localAcmeHttpVirtualHosts // proxyBackendVirtualHosts;
streamConfig = lib.concatStringsSep "\n\n" (map renderTlsServer tlsRouteMaps);
publicListenPorts = lib.unique (map (endpoint: endpoint.listenPort) endpointsConfig);
in
{
inherit endpointsConfig forwardingEndpoints proxyEndpoints streamConfig virtualHosts proxyOffset publicListenPorts localServiceAcmeDomains;
}