From b16d6faee7de86e0594d7e08e9e2bc5add368371 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Fri, 22 Mar 2024 14:02:17 -0700 Subject: [PATCH] fix(vouch): local access --- modules/nixos/nginx/name.nix | 13 +++- modules/nixos/nginx/vouch.nix | 41 +++++++---- nixos/access/barcodebuddy.nix | 2 +- nixos/access/invidious.nix | 2 +- nixos/access/kanidm.nix | 131 ---------------------------------- nixos/access/keycloak.nix | 8 +-- nixos/access/vouch.nix | 46 +++++++----- systems/hakurei/nixos.nix | 60 ++++++++++------ 8 files changed, 110 insertions(+), 193 deletions(-) delete mode 100644 nixos/access/kanidm.nix diff --git a/modules/nixos/nginx/name.nix b/modules/nixos/nginx/name.nix index 01eb5cc3..da342dca 100644 --- a/modules/nixos/nginx/name.nix +++ b/modules/nixos/nginx/name.nix @@ -5,7 +5,8 @@ }: let inherit (lib.options) mkOption; inherit (lib.modules) mkIf mkDefault mkOptionDefault; - inherit (lib.strings) optionalString; + inherit (lib.lists) filter; + inherit (lib.strings) optionalString hasPrefix; inherit (config.services) tailscale; inherit (config) networking; hostModule = {config, ...}: let @@ -37,6 +38,9 @@ allServerNames = mkOption { type = listOf str; }; + otherServerNames = mkOption { + type = listOf str; + }; }; config = { @@ -66,9 +70,12 @@ (mkIf (cfg.localName != null) cfg.localName) (mkIf (cfg.tailscaleName != null) cfg.tailscaleName) ]); - allServerNames = mkOptionDefault ( + allServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) ( [ config.serverName ] ++ config.serverAliases - ); + )); + otherServerNames = mkOptionDefault (filter (name: ! hasPrefix "@" name) ( + config.serverAliases + )); }; }; in { diff --git a/modules/nixos/nginx/vouch.nix b/modules/nixos/nginx/vouch.nix index 0d3d6a3b..701a425a 100644 --- a/modules/nixos/nginx/vouch.nix +++ b/modules/nixos/nginx/vouch.nix @@ -22,7 +22,7 @@ }; }; config = let - enableVouchLocal = vouch.localSso.enable && config.local.enable; + enableVouchLocal = virtualHost.vouch.localSso.enable; enableVouchTail = enableVouchLocal && tailscale.enable; allowOrigin = url: "add_header Access-Control-Allow-Origin ${url};"; in mkIf config.vouch.requireAuth { @@ -57,6 +57,9 @@ }; vouch = { enable = mkEnableOption "vouch auth proxy"; + localSso.enable = mkEnableOption "lan-local vouch" // { + default = vouch.localSso.enable && config.local.enable; + }; requireAuth = mkEnableOption "require auth to access this host" // { default = true; }; @@ -155,8 +158,8 @@ (mkBefore '' set $vouch_url ${vouch.url}; '') - (mkIf (vouch.localSso.enable && config.local.enable or false) localVouchUrl) - (mkIf (vouch.localSso.enable && config.local.enable or false && tailscale.enable) tailVouchUrl) + (mkIf cfg.localSso.enable localVouchUrl) + (mkIf (cfg.localSso.enable && tailscale.enable) tailVouchUrl) ]; in mkIf cfg.enable (mkMerge ( [ @@ -184,8 +187,8 @@ proxied.rewriteReferer = false; extraConfig = let # nginx-proxied vouch must use X-Forwarded-Host, but vanilla vouch requires Host - vouchProxyHost = if vouch.doubleProxy - then "${config.proxy.host}" + vouchProxyHost = if vouch.doubleProxy.enable + then (if cfg.localSso.enable then vouch.doubleProxy.localServerName else vouch.doubleProxy.serverName) else "$x_forwarded_host"; in '' proxy_set_header Host ${vouchProxyHost}; @@ -204,17 +207,31 @@ in { services.nginx = { vouch = { enable = mkEnableOption "vouch auth proxy"; + enableLocal = mkEnableOption "use local vouch instance" // { + default = true; + }; localSso = { - # NOTE: this won't work without multiple vouch-proxy instances with different auth urls... - enable = mkEnableOption "lan-local auth"; + enable = mkEnableOption "lan-local auth" // { + default = true; + }; }; proxyOrigin = mkOption { type = str; default = "https://login.local.${networking.domain}"; }; - doubleProxy = mkOption { - type = bool; - default = true; + doubleProxy = { + enable = mkOption { + type = bool; + default = true; + }; + serverName = mkOption { + type = str; + default = "@vouch_internal"; + }; + localServerName = mkOption { + type = str; + default = "@vouch_internal_local"; + }; }; authUrl = mkOption { type = str; @@ -245,7 +262,7 @@ in { mkAlmostOptionDefault "http://login.tail.${networking.domain}" ); } - (mkIf vouch-proxy.enable { + (mkIf (vouch.enableLocal && vouch-proxy.enable) { proxyOrigin = let inherit (vouch-proxy.settings.vouch) listen port; host = @@ -256,7 +273,7 @@ in { mkAlmostOptionDefault "http://${host}:${toString port}"; authUrl = mkAlmostOptionDefault vouch-proxy.authUrl; url = mkAlmostOptionDefault vouch-proxy.url; - doubleProxy = mkAlmostOptionDefault false; + doubleProxy.enable = mkAlmostOptionDefault false; }) ]; }; diff --git a/nixos/access/barcodebuddy.nix b/nixos/access/barcodebuddy.nix index 40920efc..496e5577 100644 --- a/nixos/access/barcodebuddy.nix +++ b/nixos/access/barcodebuddy.nix @@ -6,7 +6,7 @@ inherit (lib.modules) mkIf mkDefault; inherit (config.services) barcodebuddy nginx; name.shortServer = mkDefault "bbuddy"; - serverName = "bbuddy_internal"; + serverName = "@bbuddy_internal"; extraConfig = '' set $x_proxy_host ${serverName}; ''; diff --git a/nixos/access/invidious.nix b/nixos/access/invidious.nix index 00bf8676..5999e02b 100644 --- a/nixos/access/invidious.nix +++ b/nixos/access/invidious.nix @@ -53,7 +53,7 @@ in { }; }; invidious'int = { config, ... }: { - serverName = "invidious_internal"; + serverName = "@invidious_internal"; proxied.enable = true; local.denyGlobal = true; # TODO: consider disabling registration then redirecting to login if `SID` cookie is unset instead of using vouch diff --git a/nixos/access/kanidm.nix b/nixos/access/kanidm.nix deleted file mode 100644 index e3bcc6f8..00000000 --- a/nixos/access/kanidm.nix +++ /dev/null @@ -1,131 +0,0 @@ -{ - config, - meta, - lib, - ... -}: let - inherit (lib.options) mkOption; - inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; - inherit (config) networking; - inherit (config.services) tailscale nginx; - inherit (nginx) virtualHosts; - cfg = config.services.kanidm; - access = nginx.access.kanidm; - proxyPass = mkDefault "https://${access.host}:${toString access.port}"; - locations = { - "/" = { - inherit proxyPass; - }; - "=/ca.pem" = mkIf cfg.server.unencrypted.enable { - alias = "${cfg.server.unencrypted.package.ca}"; - }; - }; - localLocations = vouchDomain: { - "/".extraConfig = '' - proxy_redirect $scheme://${nginx.access.vouch.domain or "login.${networking.domain}"}/ $scheme://${vouchDomain}/; - ''; - }; -in { - imports = let - inherit (meta) nixos; - in [ - nixos.access.ldap - ]; - - options.services.nginx.access.kanidm = with lib.types; { - host = mkOption { - type = str; - }; - domain = mkOption { - type = str; - default = "id.${networking.domain}"; - }; - localDomain = mkOption { - type = str; - default = "id.local.${networking.domain}"; - }; - tailDomain = mkOption { - type = str; - default = "id.tail.${networking.domain}"; - }; - port = mkOption { - type = port; - }; - ldapHost = mkOption { - type = str; - default = access.host; - }; - ldapPort = mkOption { - type = port; - }; - ldapEnable = mkOption { - type = bool; - default = false; - }; - useACMEHost = mkOption { - type = nullOr str; - default = virtualHosts.${access.domain}.useACMEHost; - }; - }; - config = { - services.nginx = { - access.kanidm = mkIf cfg.enableServer { - domain = mkOptionDefault cfg.server.frontend.domain; - host = mkOptionDefault "localhost"; - port = mkOptionDefault cfg.server.frontend.port; - ldapPort = mkOptionDefault cfg.server.ldap.port; - ldapEnable = mkDefault cfg.server.ldap.enable; - }; - access.ldap = mkIf (cfg.enableServer && cfg.ldapEnable) { - enable = mkDefault true; - host = mkOptionDefault access.kanidm.ldapHost; - port = mkOptionDefault access.kanidm.ldapPort; - useACMEHost = mkDefault access.kanidm.useACMEHost; - }; - virtualHosts = { - ${access.domain} = { - inherit locations; - }; - ${access.localDomain} = { - inherit (virtualHosts.${access.domain}) useACMEHost; - addSSL = mkDefault (access.useACMEHost != null || virtualHosts.${access.domain}.forceSSL); - local.enable = true; - locations = mkMerge [ - locations - (localLocations nginx.access.vouch.localDomain or "login.local.${networking.domain}") - ]; - }; - ${access.tailDomain} = mkIf tailscale.enable { - inherit (virtualHosts.${access.domain}) useACMEHost; - addSSL = mkDefault (access.useACMEHost != null || virtualHosts.${access.domain}.forceSSL); - local.enable = true; - locations = mkMerge [ - locations - (localLocations nginx.access.vouch.tailDomain or "login.tail.${networking.domain}") - ]; - }; - }; - }; - - services.kanidm.server.unencrypted.domain = mkMerge [ - [ - access.localDomain - config.networking.fqdn - config.lib.access.hostnameForNetwork.local - ] - (mkIf tailscale.enable [ - "id.tail.${config.networking.domain}" - config.lib.access.hostnameForNetwork.tail - ]) - ]; - - networking.firewall = { - interfaces.local.allowedTCPPorts = [ - 389 - ]; - allowedTCPPorts = [ - 636 - ]; - }; - }; -} diff --git a/nixos/access/keycloak.nix b/nixos/access/keycloak.nix index b2b5b952..6a900754 100644 --- a/nixos/access/keycloak.nix +++ b/nixos/access/keycloak.nix @@ -44,11 +44,11 @@ in { }; local.enable = true; inherit locations; - extraConfig = mkIf nginx.vouch.localSso.enable '' + extraConfig = mkIf false '' set $vouch_local_url ${nginx.vouch.localUrl}; - if ($x_forwarded_host ~ "\.tail\.${networking.domain}$") { - set $vouch_local_url $x_scheme://${nginx.vouch.tailDomain}; - } + #if ($x_forwarded_host ~ "\.tail\.${networking.domain}$") { + # set $vouch_local_url $x_scheme://${nginx.vouch.tailDomain}; + #} proxy_redirect ${nginx.vouch.url}/ $vouch_local_url/; ''; }; diff --git a/nixos/access/vouch.nix b/nixos/access/vouch.nix index a6261a20..94c41d8d 100644 --- a/nixos/access/vouch.nix +++ b/nixos/access/vouch.nix @@ -3,40 +3,31 @@ lib, ... }: let - inherit (lib.options) mkOption; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (config) networking; inherit (config.services) tailscale nginx; cfg = config.services.vouch-proxy; - access = nginx.access.vouch; in { - options.services.nginx.access.vouch = with lib.types; { - url = mkOption { - type = str; - }; - }; config.services.nginx = { - access.vouch = mkIf cfg.enable { - url = let + virtualHosts = let + localVouchUrl = let inherit (cfg.settings.vouch) listen; host = if listen == "0.0.0.0" || listen == "[::]" then "localhost" else listen; in - mkOptionDefault "http://${host}:${toString cfg.settings.vouch.port}"; - }; - virtualHosts = let + "http://${host}:${toString cfg.settings.vouch.port}"; locations = { "/" = { - proxyPass = mkDefault access.url; extraConfig = '' proxy_redirect default; + set $x_proxy_host $x_forwarded_host; ''; }; - "/validate" = {config, ...}: { + "/validate" = {config, virtualHost, ...}: { proxied.enable = true; - proxyPass = mkDefault (access.url + "/validate"); + proxyPass = mkDefault (virtualHost.locations."/".proxyPass + "/validate"); proxy.headers.enableRecommended = true; local.denyGlobal = true; extraConfig = '' @@ -44,7 +35,7 @@ in { ''; }; }; - localLocations = kanidmDomain: mkIf nginx.vouch.localSso.enable { + localLocations = kanidmDomain: mkIf (nginx.vouch.localSso.enable && false) { "/" = { proxied.xvars.enable = true; extraConfig = '' @@ -55,14 +46,25 @@ in { name.shortServer = mkDefault "login"; in { vouch = { - inherit name locations; + inherit name; + serverAliases = [ nginx.vouch.doubleProxy.serverName ]; + proxied.enable = true; + local.denyGlobal = true; + locations = mkMerge [ + locations + { + "/".proxyPass = mkIf cfg.enable (mkDefault localVouchUrl); + } + ]; ssl.force = true; }; vouch'local = { name = { inherit (name) shortServer; - includeTailscale = false; + includeTailscale = mkDefault false; }; + serverAliases = mkIf cfg.enable [ nginx.vouch.doubleProxy.localServerName ]; + proxied.enable = true; local.enable = true; ssl = { force = true; @@ -70,11 +72,14 @@ in { }; locations = mkMerge [ locations + { + "/".proxyPass = mkDefault nginx.virtualHosts.vouch.locations."/".proxyPass; + } (localLocations "sso.local.${networking.domain}") ]; }; vouch'tail = { - enable = mkDefault tailscale.enable; + enable = mkDefault (tailscale.enable && !nginx.virtualHosts.vouch'local.name.includeTailscale); ssl.cert.copyFromVhost = "vouch'local"; name = { inherit (name) shortServer; @@ -83,6 +88,9 @@ in { local.enable = true; locations = mkMerge [ locations + { + "/".proxyPass = mkDefault nginx.virtualHosts.vouch'local.locations."/".proxyPass; + } (localLocations "sso.tail.${networking.domain}") ]; }; diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index 4a2f2605..83bb8c96 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -11,10 +11,10 @@ tei = access.nixosFor "tei"; utsuho = access.nixosFor "utsuho"; inherit (mediabox.services) plex; - inherit (keycloak.services) vouch-proxy; inherit (tei.services) home-assistant zigbee2mqtt; inherit (utsuho.services) unifi; inherit (config.services) nginx; + inherit (nginx) virtualHosts; in { imports = let inherit (meta) nixos; @@ -31,6 +31,7 @@ in { nixos.ddclient nixos.acme nixos.nginx + nixos.vouch nixos.access.nginx nixos.access.global nixos.access.gensokyo @@ -56,7 +57,7 @@ in { }; services.cloudflared = let - inherit (nginx) virtualHosts defaultHTTPListenPort; + inherit (nginx) defaultHTTPListenPort; tunnelId = "964121e3-b3a9-4cc1-8480-954c4728b604"; localNginx = "http://localhost:${toString defaultHTTPListenPort}"; in { @@ -70,9 +71,14 @@ in { }; }; - security.acme.certs = let - inherit (nginx) virtualHosts; - in { + # configure a secondary vouch instance for local clients, but don't use it by default + services.vouch-proxy = { + authUrl = "https://${virtualHosts.keycloak'local.serverName}/realms/${config.networking.domain}"; + domain = "login.local.${config.networking.domain}"; + #cookie.domain = "local.${config.networking.domain}"; + }; + + security.acme.certs = { hakurei = { inherit (nginx) group; domain = config.networking.fqdn; @@ -85,7 +91,7 @@ in { inherit (nginx) group; domain = virtualHosts.keycloak.serverName; extraDomainNames = mkMerge [ - virtualHosts.keycloak.serverAliases + virtualHosts.keycloak.otherServerNames virtualHosts.keycloak'local.allServerNames ]; }; @@ -93,7 +99,7 @@ in { inherit (nginx) group; domain = virtualHosts.home-assistant.serverName; extraDomainNames = mkMerge [ - virtualHosts.home-assistant.serverAliases + virtualHosts.home-assistant.otherServerNames virtualHosts.home-assistant'local.allServerNames ]; }; @@ -101,7 +107,7 @@ in { inherit (nginx) group; domain = virtualHosts.zigbee2mqtt.serverName; extraDomainNames = mkMerge [ - virtualHosts.zigbee2mqtt.serverAliases + virtualHosts.zigbee2mqtt.otherServerNames virtualHosts.zigbee2mqtt'local.allServerNames ]; }; @@ -109,7 +115,7 @@ in { inherit (nginx) group; domain = virtualHosts.grocy.serverName; extraDomainNames = mkMerge [ - virtualHosts.grocy.serverAliases + virtualHosts.grocy.otherServerNames virtualHosts.grocy'local.allServerNames ]; }; @@ -117,7 +123,7 @@ in { inherit (nginx) group; domain = virtualHosts.barcodebuddy.serverName; extraDomainNames = mkMerge [ - virtualHosts.barcodebuddy.serverAliases + virtualHosts.barcodebuddy.otherServerNames virtualHosts.barcodebuddy'local.allServerNames ]; }; @@ -125,7 +131,7 @@ in { inherit (nginx) group; domain = virtualHosts.vouch.serverName; extraDomainNames = mkMerge [ - virtualHosts.vouch.serverAliases + virtualHosts.vouch.otherServerNames virtualHosts.vouch'local.allServerNames (mkIf virtualHosts.vouch'tail.enable virtualHosts.vouch'tail.allServerNames) ]; @@ -134,7 +140,7 @@ in { inherit (nginx) group; domain = virtualHosts.unifi.serverName; extraDomainNames = mkMerge [ - virtualHosts.unifi.serverAliases + virtualHosts.unifi.otherServerNames virtualHosts.unifi'local.allServerNames ]; }; @@ -142,7 +148,7 @@ in { inherit (nginx) group; domain = virtualHosts.freeipa.serverName; extraDomainNames = mkMerge [ - virtualHosts.freeipa.serverAliases + virtualHosts.freeipa.otherServerNames virtualHosts.freeipa'web.allServerNames virtualHosts.freeipa'web'local.allServerNames virtualHosts.freeipa'ldap.allServerNames @@ -154,7 +160,7 @@ in { inherit (nginx) group; domain = virtualHosts.freepbx.serverName; extraDomainNames = mkMerge [ - virtualHosts.freepbx.serverAliases + virtualHosts.freepbx.otherServerNames virtualHosts.freepbx'local.allServerNames ]; }; @@ -162,7 +168,7 @@ in { inherit (nginx) group; domain = virtualHosts.prox.serverName; extraDomainNames = mkMerge [ - virtualHosts.prox.serverAliases + virtualHosts.prox.otherServerNames virtualHosts.prox'local.allServerNames (mkIf virtualHosts.prox'tail.enable virtualHosts.prox'tail.allServerNames) ]; @@ -171,7 +177,7 @@ in { inherit (nginx) group; domain = virtualHosts.plex.serverName; extraDomainNames = mkMerge [ - virtualHosts.plex.serverAliases + virtualHosts.plex.otherServerNames virtualHosts.plex'local.allServerNames ]; }; @@ -179,7 +185,7 @@ in { inherit (nginx) group; domain = virtualHosts.kitchencam.serverName; extraDomainNames = mkMerge [ - virtualHosts.kitchencam.serverAliases + virtualHosts.kitchencam.otherServerNames virtualHosts.kitchencam'local.allServerNames ]; }; @@ -187,7 +193,7 @@ in { inherit (nginx) group; domain = virtualHosts.invidious.serverName; extraDomainNames = mkMerge [ - virtualHosts.invidious.serverAliases + virtualHosts.invidious.otherServerNames virtualHosts.invidious'local.allServerNames ]; }; @@ -196,13 +202,11 @@ in { services.nginx = let inherit (nginx) access; in { + vouch.enableLocal = false; access.plex = assert plex.enable; { url = "http://${mediabox.lib.access.hostnameForNetwork.local}:${toString plex.port}"; externalPort = 41324; }; - access.vouch = assert vouch-proxy.enable; { - url = "http://${keycloak.lib.access.hostnameForNetwork.local}:${toString vouch-proxy.settings.vouch.port}"; - }; access.unifi = assert unifi.enable; { host = utsuho.lib.access.hostnameForNetwork.local; }; @@ -224,7 +228,19 @@ in { local.denyGlobal = true; ssl.cert.enable = true; }; - vouch.ssl.cert.enable = true; + vouch = let + inherit (keycloak.services) vouch-proxy; + in assert vouch-proxy.enable; { + ssl.cert.enable = true; + locations."/".proxyPass = "http://${keycloak.lib.access.hostnameForNetwork.local}:${toString vouch-proxy.settings.vouch.port}"; + }; + vouch'local = let + vouch-proxy = config.services.vouch-proxy; + in assert vouch-proxy.enable; { + locations."/".proxyPass = "http://localhost:${toString vouch-proxy.settings.vouch.port}"; + # we're not running another for tailscale sorry... + name.includeTailscale = true; + }; unifi = { # we're not the real unifi record-holder, so don't respond globally.. local.denyGlobal = true;