{ config, pkgs, lib, ... }: let network = import ../data/network.nix; virtualHostFn = name: service: let domain = if service ? domainOverride then service.domainOverride else "${name}.${network.local_domain}"; locationList = if service.reverse_proxy ? endpoints then service.reverse_proxy.endpoints else ["/"]; locationsData = builtins.listToAttrs (map (endpointName: { name = endpointName; value = { proxyPass = "http://127.0.0.1:${builtins.toString service.reverse_proxy.port}/"; proxyWebsockets = true; }; }) locationList); serverAlias = lib.optionalAttrs (service.reverse_proxy ? aliases) { serverAliases = map (alias: "${alias}.${domain}") service.reverse_proxy.aliases; }; myExtraConfig = if service.reverse_proxy ? extraConfig then service.reverse_proxy.extraConfig else {}; in { serverName = "${domain}"; listen = [ {addr = "0.0.0.0"; port = 80;} ]; locations = locationsData; extraConfig = '' allow ${network.network.subnet}; deny all; ''; } // serverAlias // myExtraConfig; rproxyServices = builtins.mapAttrs (virtualHostFn) network.reverse_proxy; serviceNamesMessage = builtins.toString (builtins.attrNames network.reverse_proxy); fallback = { serverName = "_"; listen = [ {addr = "0.0.0.0"; port = 80;} { addr = "0.0.0.0"; port = 443; ssl = true; }]; locations."/" = { return = "404"; extraConfig = '' add_header Content-Type text/plain; ''; }; extraConfig = '' return 404 "This domain is not configured. Available services: ${serviceNamesMessage}"; ''; }; acme = { serverName = "_"; listen = [ {addr = "0.0.0.0"; port = 80;} ]; locations."/.well-known/acme-challenge" = { root = "/var/lib/acme/.challenges"; }; }; in { services.nginx = { enable = true; recommendedProxySettings = true; recommendedTlsSettings = true; recommendedOptimisation = true; recommendedGzipSettings = true; virtualHosts = rproxyServices // {fallback = fallback;}; }; networking.firewall.allowedTCPPorts = [80 443]; security.acme = { acceptTerms = true; defaults.email = "katharina.heidenreich02@gmail.com"; }; }