diff --git a/modules/nixos/nftables.nix b/modules/nixos/nftables.nix index e3660534..7c2958a0 100644 --- a/modules/nixos/nftables.nix +++ b/modules/nixos/nftables.nix @@ -111,7 +111,7 @@ }; conditions = mkOption { type = types.listOf types.str; - default = "iifname ${name}"; + default = [ "iifname ${name}" ]; }; }; }; diff --git a/modules/nixos/nginx/proxied.nix b/modules/nixos/nginx/proxied.nix index addcfe15..06ccb0d4 100644 --- a/modules/nixos/nginx/proxied.nix +++ b/modules/nixos/nginx/proxied.nix @@ -1,11 +1,4 @@ -{ - lib, - inputs, - ... -}: let - inherit (inputs.self.lib.lib) mkJustBefore mkAlmostOptionDefault orderJustBefore; - inherit (lib.options) mkOption; - inherit (lib.modules) mkIf mkMerge mkOrder mkDefault mkOptionDefault; +let xHeadersProxied = { xvars }: '' ${xvars.init "forwarded_for" "$proxy_add_x_forwarded_for"} if ($http_x_forwarded_proto) { @@ -25,7 +18,10 @@ ${xvars.init "forwarded_server" "$http_x_forwarded_server"} } ''; - locationModule = { config, virtualHost, xvars, ... }: let + locationModule = { config, virtualHost, xvars, gensokyo-zone, lib, ... }: let + inherit (gensokyo-zone.lib) mkJustBefore mkAlmostOptionDefault; + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf mkMerge mkOptionDefault; cfg = config.proxied; in { options = with lib.types; { @@ -69,7 +65,11 @@ ]; }; }; - hostModule = { config, xvars, ... }: let + hostModule = { config, nixosConfig, xvars, gensokyo-zone, lib, ... }: let + inherit (gensokyo-zone.lib) mkAlmostOptionDefault orderJustBefore unmerged; + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf mkOrder mkDefault; + inherit (nixosConfig.services) nginx; cfg = config.proxied; in { options = with lib.types; { @@ -82,6 +82,14 @@ type = bool; default = cfg.enable != false; }; + cloudflared = { + ingressSettings = mkOption { + type = unmerged.types.attrs; + }; + getIngress = mkOption { + type = functionTo unspecified; + }; + }; }; locations = mkOption { type = attrsOf (submoduleWith { @@ -91,18 +99,83 @@ }; }; - config = { + config = let + listenProxied = cfg.enabled; + in { + proxied = { + cloudflared = let + listen = config.listen'.proxied; + scheme = if listen.ssl then "https" else "http"; + in mkIf (cfg.enable == "cloudflared") { + ingressSettings.${config.serverName} = { + service = "${scheme}://localhost:${toString listen.port}"; + originRequest.${if scheme == "https" then "noTLSVerify" else null} = true; + }; + getIngress = {}: unmerged.mergeAttrs cfg.cloudflared.ingressSettings; + }; + }; xvars.enable = mkIf cfg.enabled true; - local.denyGlobal = mkIf (cfg.enable == "cloudflared") (mkDefault true); + local.denyGlobal = mkIf listenProxied (mkDefault true); + listen' = mkIf listenProxied { + proxied = { + addr = "[::]"; + port = mkAlmostOptionDefault nginx.proxied.listenPort; + }; + }; extraConfig = mkIf (cfg.enabled && config.xvars.enable) ( mkOrder (orderJustBefore + 25) (xHeadersProxied { inherit xvars; }) ); }; }; in { - options = with lib.types; { - services.nginx.virtualHosts = mkOption { + config, + system, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf mkOptionDefault; + inherit (lib.attrsets) attrValues; + inherit (lib.lists) any; + inherit (config.services) nginx; + cfg = nginx.proxied; +in { + options.services.nginx = with lib.types; { + proxied = { + enabled = mkOption { + type = bool; + }; + listenPort = mkOption { + type = port; + default = 9080; + }; + }; + virtualHosts = mkOption { type = attrsOf (submodule [hostModule]); }; }; + config = { + services.nginx = let + hasProxiedHosts = any (virtualHost: virtualHost.enable && virtualHost.proxied.enabled) (attrValues nginx.virtualHosts); + in { + proxied = { + enabled = mkOptionDefault hasProxiedHosts; + }; + upstreams' = { + nginx'proxied = mkIf cfg.enabled { + servers.local = { + accessService = { + system = system.name; + name = "nginx"; + port = "proxied"; + }; + }; + }; + }; + # TODO: virtualHosts.fallback'proxied.reuseport = true; + }; + networking.firewall.interfaces.lan = mkIf nginx.enable { + allowedTCPPorts = mkIf cfg.enabled [ cfg.listenPort ]; + }; + }; } diff --git a/modules/nixos/nginx/vouch.nix b/modules/nixos/nginx/vouch.nix index 866658c4..7797af0f 100644 --- a/modules/nixos/nginx/vouch.nix +++ b/modules/nixos/nginx/vouch.nix @@ -297,22 +297,23 @@ in { }; vouch'proxy = { enable = vouch.enable && vouch.doubleProxy.enable; + # TODO: need exported hosts options for this to detect the correct host/port/etc servers = { lan = { upstream, ... }: { enable = mkAlmostOptionDefault (!upstream.servers.int.enable); addr = mkAlmostOptionDefault "login.local.${networking.domain}"; - port = mkOptionDefault null; + port = mkOptionDefault 9080; ssl.enable = mkAlmostOptionDefault true; }; int = { upstream, ... }: { enable = mkAlmostOptionDefault system.network.networks.int.enable or false; addr = mkAlmostOptionDefault "login.int.${networking.domain}"; - port = mkOptionDefault null; + port = mkOptionDefault 9080; }; tail = { upstream, ... }: { enable = mkAlmostOptionDefault (tailscale.enable && !upstream.servers.lan.enable && !upstream.servers.int.enable); addr = mkAlmostOptionDefault "login.tail.${networking.domain}"; - port = mkOptionDefault null; + port = mkOptionDefault 9080; }; }; }; diff --git a/modules/nixos/postgres.nix b/modules/nixos/postgres.nix index ee25fe56..9a63262a 100644 --- a/modules/nixos/postgres.nix +++ b/modules/nixos/postgres.nix @@ -93,7 +93,7 @@ in { local = mkIf cfg.enable { allowedTCPPorts = mkIf (any (user: user.authentication.local.allow) cfg.ensureUsers) [cfg.settings.port]; }; - int = mkIf cfg.enable { + lan = mkIf cfg.enable { allowedTCPPorts = mkIf (any (user: user.authentication.int.allow) cfg.ensureUsers) [cfg.settings.port]; }; }; diff --git a/modules/system/exports/nginx.nix b/modules/system/exports/nginx.nix index bdadca7f..c6893066 100644 --- a/modules/system/exports/nginx.nix +++ b/modules/system/exports/nginx.nix @@ -11,12 +11,22 @@ in { assertion = config.ports.http.port == cfg.defaultHTTPListenPort && config.ports.https.port == cfg.defaultSSLListenPort; message = "ports mismatch"; }; + assertProxied = nixosConfig: cfg: { + assertion = config.ports.proxied.enable == cfg.proxied.enabled; + message = "proxied mismatch"; + }; + assertProxiedPort = nixosConfig: cfg: { + assertion = !config.ports.proxied.enable || config.ports.proxied.port == cfg.proxied.listenPort; + message = "proxied.port mismatch"; + }; in { nixos = { serviceAttr = "nginx"; - assertions = mkIf config.enable [ - (mkAssertion assertPorts) - ]; + assertions = mkIf config.enable (map mkAssertion [ + assertPorts + assertProxied + assertProxiedPort + ]); }; defaults.port.listen = mkAlmostOptionDefault "lan"; ports = mapAttrs (_: mapAlmostOptionDefaults) { @@ -29,6 +39,12 @@ in { port = 443; protocol = "https"; }; + proxied = { + enable = false; + port = 9080; + protocol = "http"; + listen = "lan"; + }; }; }; } diff --git a/nixos/access/barcodebuddy.nix b/nixos/access/barcodebuddy.nix index 1fb416b7..3caa222c 100644 --- a/nixos/access/barcodebuddy.nix +++ b/nixos/access/barcodebuddy.nix @@ -22,8 +22,8 @@ in { requireAuth = false; }; proxy = { - url = mkIf barcodebuddy.enable (mkDefault - "http://localhost:${toString nginx.defaultHTTPListenPort}" + upstream = mkIf barcodebuddy.enable (mkDefault + "nginx'proxied" ); host = mkDefault serverName; }; @@ -42,7 +42,7 @@ in { ssl.cert.copyFromVhost = "barcodebuddy"; local.enable = mkDefault true; proxy = { - url = mkDefault nginx.virtualHosts.barcodebuddy.proxy.url; + upstream = mkDefault nginx.virtualHosts.barcodebuddy.proxy.upstream; host = mkDefault nginx.virtualHosts.barcodebuddy.proxy.host; }; locations."/" = { config, ... }: { diff --git a/nixos/access/grocy.nix b/nixos/access/grocy.nix index 737cdb80..fa84e334 100644 --- a/nixos/access/grocy.nix +++ b/nixos/access/grocy.nix @@ -59,8 +59,8 @@ in { inherit name extraConfig locations; vouch.enable = true; proxy = { - url = mkIf grocy.enable (mkDefault - "http://localhost:${toString nginx.defaultHTTPListenPort}" + upstream = mkIf grocy.enable (mkDefault + "nginx'proxied" ); host = mkDefault serverName; }; @@ -70,7 +70,7 @@ in { local.enable = mkDefault true; ssl.cert.copyFromVhost = "grocy"; proxy = { - url = mkDefault "http://localhost:${toString nginx.defaultHTTPListenPort}"; + upstream = mkDefault "nginx'proxied"; host = nginx.virtualHosts.grocy'local'int.serverName; }; locations."/" = { @@ -82,7 +82,7 @@ in { serverName = serverName'local; inherit name extraConfig locations; proxy = { - url = mkDefault nginx.virtualHosts.grocy.proxy.url; + upstream = mkDefault nginx.virtualHosts.grocy.proxy.upstream; host = mkDefault nginx.virtualHosts.grocy.proxy.host; }; proxied.enable = true; diff --git a/nixos/access/invidious.nix b/nixos/access/invidious.nix index d9e8a2e9..f368cc5d 100644 --- a/nixos/access/invidious.nix +++ b/nixos/access/invidious.nix @@ -1,5 +1,6 @@ { config, + system, lib, ... }: let @@ -29,8 +30,9 @@ in { enable = mkDefault nginx.virtualHosts.invidious'int.enable; host = mkDefault nginx.virtualHosts.invidious'int.serverName; servers.local = { - addr = mkDefault "localhost"; - port = nginx.defaultHTTPListenPort; + accessService = { + inherit (nginx.upstreams'.nginx'proxied.servers.local.accessService) system name id port; + }; }; }; }; diff --git a/nixos/invidious.nix b/nixos/invidious.nix index 792353fe..ab7c77c1 100644 --- a/nixos/invidious.nix +++ b/nixos/invidious.nix @@ -16,7 +16,7 @@ in { invidious_hmac_key = commonSecret; }; - networking.firewall.interfaces.int.allowedTCPPorts = [cfg.port]; + networking.firewall.interfaces.lan.allowedTCPPorts = [cfg.port]; users.groups.invidious = {}; users.users.invidious = { isSystemUser = true; diff --git a/nixos/keycloak.nix b/nixos/keycloak.nix index 2c953454..c2b36d46 100644 --- a/nixos/keycloak.nix +++ b/nixos/keycloak.nix @@ -1,9 +1,8 @@ -{inputs, system, config, lib, ...}: let +{inputs, system, access, config, lib, ...}: let inherit (lib.modules) mkIf mkForce mkDefault; inherit (lib.lists) optional; - inherit (config.lib.access) mkSnakeOil; cfg = config.services.keycloak; - cert = mkSnakeOil { + cert = access.mkSnakeOil { name = "keycloak-selfsigned"; domain = hostname; }; @@ -33,7 +32,7 @@ in { }; }; - networking.firewall.interfaces.int.allowedTCPPorts = mkIf cfg.enable [ + networking.firewall.interfaces.lan.allowedTCPPorts = mkIf cfg.enable [ cfg.port ]; systemd.services.keycloak = mkIf cfg.enable { @@ -43,11 +42,15 @@ in { services.keycloak = { enable = true; - database = { - host = "postgresql.int.${config.networking.domain}"; + database = let + system = access.systemForService "postgresql"; + inherit (system.exports.services) postgresql; + in { + host = access.getAddressFor system.name "lan"; + port = postgresql.ports.default.port; passwordFile = config.sops.secrets.keycloak_db_password.path; createLocally = false; - useSSL = false; + useSSL = postgresql.ports.default.ssl; }; settings = { diff --git a/nixos/reisen-ct/proxmox.nix b/nixos/reisen-ct/proxmox.nix index 73f9ae3b..4cfe4993 100644 --- a/nixos/reisen-ct/proxmox.nix +++ b/nixos/reisen-ct/proxmox.nix @@ -1,13 +1,15 @@ { - lib, - inputs, - modulesPath, + config, system, + gensokyo-zone, + lib, + modulesPath, ... }: let - inherit (inputs.self.lib.lib) unmerged; + inherit (gensokyo-zone.lib) unmerged coalesce; inherit (lib.modules) mkIf mkMerge mkDefault; inherit (lib.attrsets) mapAttrsToList; + inherit (lib.trivial) mapNullable; inherit (system) proxmox; in { imports = [ @@ -28,11 +30,15 @@ in { networks.${interface.networkd.name} = unmerged.mergeAttrs interface.networkd.networkSettings; }) proxmox.network.interfaces)); - networking.firewall.interfaces.int = let - inherit (proxmox.network.internal) interface; - in mkIf (interface != null) { - nftables.conditions = [ - "iifname ${interface.name}" + networking.firewall.interfaces.lan = let + inherit (proxmox.network) internal local; + conditions = coalesce [ + (mapNullable (interface: [ "iifname ${interface.name}" ]) internal.interface) + (mapNullable (interface: config.networking.interfaces.local.nftables.conditions) local.interface) ]; + in mkIf (conditions != null) { + nftables = { + inherit conditions; + }; }; } diff --git a/nixos/unifi.nix b/nixos/unifi.nix index fe9aa9d8..c626da33 100644 --- a/nixos/unifi.nix +++ b/nixos/unifi.nix @@ -25,7 +25,7 @@ in { }; networking.firewall = mkIf cfg.enable { - interfaces.int = { + interfaces.lan = { allowedTCPPorts = [ 8443 # remote login ]; diff --git a/systems/hakurei/default.nix b/systems/hakurei/default.nix index 1e97c024..8a4f9da6 100644 --- a/systems/hakurei/default.nix +++ b/systems/hakurei/default.nix @@ -40,6 +40,7 @@ listen = mkIf (!preread) "wan"; }; http.listen = "wan"; + proxied.enable = true; }; }; sshd = { diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index fdbdc213..3b77a4c6 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -53,18 +53,16 @@ in { }; services.cloudflared = let - inherit (nginx) defaultHTTPListenPort; tunnelId = "964121e3-b3a9-4cc1-8480-954c4728b604"; - localNginx = "http://localhost:${toString defaultHTTPListenPort}"; in { tunnels.${tunnelId} = { default = "http_status:404"; credentialsFile = config.sops.secrets.cloudflared-tunnel-hakurei.path; - ingress = { - ${virtualHosts.prox.serverName}.service = localNginx; - ${virtualHosts.gensokyoZone.serverName}.service = localNginx; - ${virtualHosts.freeipa'web.serverName}.service = localNginx; - }; + ingress = mkMerge [ + (virtualHosts.freeipa'web.proxied.cloudflared.getIngress {}) + (virtualHosts.prox.proxied.cloudflared.getIngress {}) + (virtualHosts.gensokyoZone.proxied.cloudflared.getIngress {}) + ]; }; }; @@ -219,6 +217,12 @@ in { upstreams' = { vouch'auth.servers.local.enable = false; vouch'auth'local.servers.local.enable = true; + tei'nginx'proxied.servers.nginx.accessService = { + # TODO: host exports + system = "tei"; + name = "nginx"; + port = "proxied"; + }; }; stream.servers = { mosquitto.ssl.cert.name = "mosquitto"; @@ -261,13 +265,13 @@ in { # not the real grocy record-holder, so don't respond globally.. local.denyGlobal = true; ssl.cert.enable = true; - proxy.url = "http://${mkAddress6 (access.getAddressFor "tei" "lan")}"; + proxy.upstream = "tei'nginx'proxied"; }; barcodebuddy = { # not the real bbuddy record-holder, so don't respond globally.. local.denyGlobal = true; ssl.cert.enable = true; - proxy.url = "http://${mkAddress6 (access.getAddressFor "tei" "lan")}"; + proxy.upstream = "tei'nginx'proxied"; }; freepbx = { ssl.cert.enable = true; diff --git a/systems/tei/cloudflared.nix b/systems/tei/cloudflared.nix index 831c836e..b6974f9e 100644 --- a/systems/tei/cloudflared.nix +++ b/systems/tei/cloudflared.nix @@ -4,11 +4,10 @@ access, ... }: let - inherit (lib.modules) mkIf; + inherit (lib.modules) mkMerge; inherit (config.services) home-assistant nginx; cfg = config.services.cloudflared; apartment = "5e85d878-c6b2-4b15-b803-9aeb63d63543"; - localNginx = "http://localhost:${toString nginx.defaultHTTPListenPort}"; in { sops.secrets.cloudflared-tunnel-apartment.owner = cfg.user; services.cloudflared = { @@ -16,28 +15,17 @@ in { ${apartment} = { credentialsFile = config.sops.secrets.cloudflared-tunnel-apartment.path; default = "http_status:404"; - ingress = { - ${nginx.virtualHosts.zigbee2mqtt.serverName} = { - service = localNginx; - }; - ${nginx.virtualHosts.grocy.serverName} = { - service = localNginx; - }; - ${nginx.virtualHosts.barcodebuddy.serverName} = { - service = localNginx; - }; - ${home-assistant.domain} = assert home-assistant.enable; { - service = access.proxyUrlFor { serviceName = "home-assistant"; }; - }; - }; + ingress = mkMerge [ + (nginx.virtualHosts.zigbee2mqtt.proxied.cloudflared.getIngress {}) + (nginx.virtualHosts.grocy.proxied.cloudflared.getIngress {}) + (nginx.virtualHosts.barcodebuddy.proxied.cloudflared.getIngress {}) + { + ${home-assistant.domain} = assert home-assistant.enable; { + service = access.proxyUrlFor { serviceName = "home-assistant"; }; + }; + } + ]; }; }; }; - - systemd.services."cloudflared-tunnel-${apartment}" = rec { - wants = mkIf config.services.tailscale.enable [ - "tailscaled.service" - ]; - after = wants; - }; } diff --git a/systems/tei/default.nix b/systems/tei/default.nix index a43b433c..1ab09242 100644 --- a/systems/tei/default.nix +++ b/systems/tei/default.nix @@ -10,7 +10,10 @@ _: { exports = { services = { sshd.enable = true; - nginx.enable = true; + nginx = { + enable = true; + ports.proxied.enable = true; + }; tailscale.enable = true; home-assistant.enable = true; zigbee2mqtt.enable = true; diff --git a/systems/utsuho/default.nix b/systems/utsuho/default.nix index e72175c0..82f39361 100644 --- a/systems/utsuho/default.nix +++ b/systems/utsuho/default.nix @@ -10,7 +10,10 @@ _: { exports = { services = { sshd.enable = true; - nginx.enable = true; + nginx = { + enable = true; + ports.proxied.enable = true; + }; unifi.enable = true; mosquitto.enable = true; dnsmasq.enable = true; diff --git a/systems/utsuho/nixos.nix b/systems/utsuho/nixos.nix index f1ec2cb4..f61d766e 100644 --- a/systems/utsuho/nixos.nix +++ b/systems/utsuho/nixos.nix @@ -18,18 +18,13 @@ in { ]; services.cloudflared = let - inherit (nginx) virtualHosts defaultHTTPListenPort; + inherit (nginx) virtualHosts; tunnelId = "28bcd3fc-3467-4997-806b-546ba9995028"; - localNginx = "http://localhost:${toString defaultHTTPListenPort}"; in { tunnels.${tunnelId} = { default = "http_status:404"; credentialsFile = config.sops.secrets.cloudflared-tunnel-utsuho.path; - ingress = { - ${virtualHosts.unifi.serverName} = { - service = localNginx; - }; - }; + ingress = virtualHosts.unifi.proxied.cloudflared.getIngress {}; }; };