188 lines
No EOL
6.1 KiB
Nix
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;
|
|
} |