{ config, lib, ... }: with lib; let cfg = config.modules.expose; in { options.modules.expose = { enable = mkEnableOption "expose services to network"; routes = mkOption { type = types.attrsOf types.str; }; ssl = { enable = mkEnableOption "SSL on all routes"; acmeHost = mkOption { type = types.nullOr types.str; default = null; description = "ACMEHost for the certificate"; }; }; webmasterEmail = mkOption { type = types.str; description = "Email of the webmaster to be contacted for ACME events"; }; tailscaleIp = mkOption { type = types.str; description = "Tailscale IP for this node"; }; cloudflareUUID = mkOption { type = types.str; description = "UUID of the Cloudflare Tunnel"; }; secrets = { acme-credentials = mkOption { type = types.path; description = "Path to the acme environment file"; }; cloudflare-credentials = mkOption { type = types.path; description = "Path to the cloudflare tunnel credentials"; }; }; }; config = mkIf cfg.enable { assertions = [ { assertion = cfg.ssl.enable -> cfg.ssl.acmeHost != null; message = "ssl.acmeHost must be set when enabling SSL"; } ]; services.nginx.virtualHosts = mapAttrs (_: internal: { locations."/".proxyPass = internal; } // optionalAttrs (cfg.ssl.enable) { forceSSL = true; useACMEHost = cfg.ssl.acmeHost; }) (filterAttrs # Assume that reverse proxy is configured externally (_: v: (!hasSuffix ":80" v) && (!hasSuffix ":443" v)) cfg.routes); # Discard non-localhost mappings, and replace destination with tailscale IP services.blocky.settings.customDNS.mapping = mapAttrs (_: v: cfg.tailscaleIp) (filterAttrs (_: v: hasInfix "localhost" v) cfg.routes); services.cloudflared.tunnels."${cfg.cloudflareUUID}" = { credentialsFile = cfg.secrets.cloudflare-credentials; ingress = cfg.routes; } // optionalAttrs (cfg.ssl.enable) { # TODO: This seems to have no effect. Remove? originRequest.originServerName = "*.${cfg.ssl.acmeHost}"; originRequest.caPool = config.security.acme.certs.${cfg.ssl.acmeHost}.directory; }; }; }