{ config, lib, registry, ... }: let cfg = config.modules.services.reverse-proxy; mkRegistryPath = registryName: host: entry: "registry.${registryName}.${host}.${entry}"; mkBackend = address: route: "http://${address}:${toString route.port}"; mkVirtualHost = host: entry: let compositeEntry = registry.join host entry; route = compositeEntry.route; address = compositeEntry.address or (throw "no matching entry in registry.address for ${mkRegistryPath "route" host entry}"); in { inherit host entry; name = route.domain; value = { forceSSL = true; enableACME = true; locations."/".proxyPass = mkBackend address route; }; }; generatedVirtualHostList = lib.concatLists ( lib.mapAttrsToList ( host: routes: lib.mapAttrsToList (entry: _route: mkVirtualHost host entry) routes ) registry.route ); virtualHostsByDomain = lib.foldl' ( acc: virtualHost: acc // { ${virtualHost.name} = (acc.${virtualHost.name} or [ ]) ++ [ virtualHost ]; } ) { } generatedVirtualHostList; duplicateDomainMessages = lib.concatLists ( lib.mapAttrsToList ( domain: virtualHosts: if lib.length virtualHosts > 1 then [ "duplicate route domain ${domain} in ${lib.concatMapStringsSep " and " (virtualHost: mkRegistryPath "route" virtualHost.host virtualHost.entry) virtualHosts}" ] else [ ] ) virtualHostsByDomain ); generatedVirtualHosts = if duplicateDomainMessages != [ ] then throw (builtins.head duplicateDomainMessages) else lib.listToAttrs generatedVirtualHostList; in { options.modules.services.reverse-proxy = { enable = lib.mkEnableOption "the public edge reverse proxy"; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 80 443 ]; security.acme = { acceptTerms = true; defaults.email = "contact@alexion.dev"; }; services.nginx = { enable = true; commonHttpConfig = '' server { listen 0.0.0.0:443 ssl default_server; listen [::0]:443 ssl default_server; ssl_reject_handshake on; } ''; recommendedGzipSettings = true; recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; virtualHosts = generatedVirtualHosts; }; }; }