{ config, lib, ... }: let inherit (lib) mkOption types mapAttrs concatMapStringsSep optionalString mkIf; cfg = config.pepe.core.vhost; in { options.pepe.core.vhost = { hosts = mkOption { type = types.attrsOf (types.submodule { options = { locations = mkOption { type = types.attrsOf (types.submodule { options = { extraConfig = mkOption { type = types.str; description = "Extra configuration for the location."; default = ""; }; proxyWebsockets = mkOption { type = types.bool; default = false; }; host = mkOption { type = types.str; description = "The host for the location."; default = "127.0.0.1"; }; port = mkOption { type = types.int; description = "The port number for the location."; }; allow = mkOption { type = types.listOf types.str; default = [ ]; description = "IP address or CIDR block to allow."; }; allowVPN = mkOption { type = types.bool; default = false; description = "If set to true, allow VPN traffic."; }; allowLAN = mkOption { type = types.bool; default = false; description = "If set to true, allow LAN traffic."; }; allowWAN = mkOption { type = types.bool; default = false; description = "If set to true, allow WAN traffic. If false, deny all WAN traffic."; }; path = mkOption { type = types.str; default = ""; }; recommendedProxySettings = mkOption { type = types.bool; default = true; description = "Force the use of recommended proxy configuration."; }; }; }); default = { }; description = "An attribute set of location configurations."; }; resolveLocally = mkOption { type = types.bool; default = true; description = "If true, ensure the vhost is resolvable to 127.0.0.1 locally on this server."; }; }; }); default = { }; description = "An attribute set of domain configurations."; }; }; config = { pepe.core.firewall.openTCP = [ 80 443 ]; # Configure DNS entries for vhosts when DNS is enabled pepe.core.dns = mkIf config.pepe.core.dns.enable { extraDomains = mapAttrs (domain: conf: { dnsInterfaces = (lib.optionals (lib.any (loc: loc.allowLAN) (lib.attrValues conf.locations)) config.pepe.core.network.interfacesByType.lan) ++ (lib.optionals (lib.any (loc: loc.allowVPN) (lib.attrValues conf.locations)) config.pepe.core.network.interfacesByType.vpn); }) cfg.hosts; }; services.nginx.virtualHosts = mapAttrs (domain: conf: { forceSSL = true; useACMEHost = "giugl.io"; locations = mapAttrs (path: location: { proxyPass = "http://${location.host}:${toString location.port}${location.path}"; proxyWebsockets = location.proxyWebsockets; recommendedProxySettings = location.recommendedProxySettings; extraConfig = '' ${concatMapStringsSep "\n" (allowCIDR: "allow ${allowCIDR};") location.allow} ${optionalString location.allowLAN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.lan)} ${optionalString location.allowVPN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.vpn)} ${optionalString (!location.allowWAN) "deny all;"} '' + location.extraConfig; }) conf.locations; }) cfg.hosts; networking.extraHosts = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList (domain: hostConf: lib.optionals hostConf.resolveLocally "127.0.0.1 ${domain}" ) cfg.hosts )); }; }