diff --git a/modules/nixos/nginx-vouch.nix b/modules/nixos/nginx-vouch.nix index d32a335a..922651a2 100644 --- a/modules/nixos/nginx-vouch.nix +++ b/modules/nixos/nginx-vouch.nix @@ -3,73 +3,115 @@ lib, ... }: -with lib; let - inherit (config.services) vouch-proxy; -in { - options = with types; { - services.nginx.virtualHosts = let - vouchModule = { config, ... }: { - options = { - vouch = { - enable = mkEnableOption "vouch auth proxy"; - proxyOrigin = mkOption { - type = str; - }; - authUrl = mkOption { - type = str; - }; - url = mkOption { - type = str; - }; +let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.modules) mkIf mkMerge mkBefore mkDefault; + inherit (config) networking; + inherit (config.services) vouch-proxy tailscale; + vouchModule = { config, ... }: { + options = with lib.types; { + vouch = { + enable = mkEnableOption "vouch auth proxy"; + proxyOrigin = mkOption { + type = str; + default = "https://login.local.${networking.domain}"; + }; + authUrl = mkOption { + type = str; + default = "https://id.${networking.domain}"; + }; + url = mkOption { + type = str; + default = "https://login.${networking.domain}"; + }; + localUrl = mkOption { + type = str; + default = "https://login.local.${networking.domain}"; + }; + tailDomain = mkOption { + type = str; + default = "login.tail.${networking.domain}"; + }; + authRequestDirective = mkOption { + type = lines; + default = '' + auth_request /validate; + ''; + }; + }; + }; + config = mkMerge [ + { + vouch = mkIf vouch-proxy.enable { + proxyOrigin = let + inherit (vouch-proxy.settings.vouch) listen port; + host = if listen == "0.0.0.0" || listen == "[::]" then "localhost" else listen; + in mkDefault "http://${host}:${toString port}"; + authUrl = mkDefault vouch-proxy.authUrl; + url = mkDefault vouch-proxy.url; + }; + } + { + vouch.proxyOrigin = mkIf (tailscale.enable && !vouch-proxy.enable) (mkDefault + "http://login.tail.${networking.domain}" + ); + } + (mkIf config.vouch.enable { + extraConfig = '' + ${config.vouch.authRequestDirective} + error_page 401 = @error401; + ''; + locations = { + "/" = { + extraConfig = '' + add_header Access-Control-Allow-Origin ${config.vouch.url}; + add_header Access-Control-Allow-Origin ${config.vouch.authUrl}; + proxy_set_header X-Vouch-User $auth_resp_x_vouch_user; + ''; + }; + "@error401" = { + extraConfig = let + localVouchUrl = '' + if ($http_host ~ "\.local\.${networking.domain}$") { + set $vouch_url ${config.vouch.localUrl}; + } + ''; + tailVouchUrl = '' + if ($http_host ~ "\.tail\.${networking.domain}$") { + set $vouch_url $scheme://${config.vouch.tailDomain}; + } + ''; + in mkMerge [ + (mkBefore '' + set $vouch_url ${config.vouch.url}; + '') + (mkIf (config.local.enable or false) localVouchUrl) + (mkIf (config.local.enable or false && tailscale.enable) tailVouchUrl) + '' + return 302 $vouch_url/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; + '' + ]; + }; + "/validate" = { + recommendedProxySettings = false; + proxyPass = "${config.vouch.proxyOrigin}/validate"; + extraConfig = '' + proxy_set_header Host $host; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; + auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; + auth_request_set $auth_resp_err $upstream_http_x_vouch_err; + auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; + ''; }; }; - config = mkMerge [ - { - vouch = mkIf vouch-proxy.enable { - proxyOrigin = let - inherit (vouch-proxy.settings.vouch) listen port; - host = if listen == "0.0.0.0" || listen == "[::]" then "localhost" else listen; - in mkOptionDefault "http://${host}:${toString port}"; - authUrl = mkOptionDefault vouch-proxy.authUrl; - url = mkOptionDefault vouch-proxy.url; - }; - } - (mkIf config.vouch.enable { - extraConfig = '' - auth_request /validate; - error_page 401 = @error401; - ''; - locations = { - "/" = { - extraConfig = '' - add_header Access-Control-Allow-Origin ${config.vouch.url}; - add_header Access-Control-Allow-Origin ${config.vouch.authUrl}; - proxy_set_header X-Vouch-User $auth_resp_x_vouch_user; - ''; - }; - "@error401" = { - extraConfig = '' - return 302 ${config.vouch.url}/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; - ''; - }; - "/validate" = { - recommendedProxySettings = false; - proxyPass = "${config.vouch.proxyOrigin}/validate"; - extraConfig = '' - proxy_set_header Host $host; - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; - auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; - auth_request_set $auth_resp_err $upstream_http_x_vouch_err; - auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; - ''; - }; - }; - }) - ]; - }; - in mkOption { + }) + ]; + }; +in { + options = with lib.types; { + services.nginx.virtualHosts = mkOption { type = attrsOf (submodule vouchModule); }; }; diff --git a/nixos/access/vouch.nix b/nixos/access/vouch.nix new file mode 100644 index 00000000..056ea81a --- /dev/null +++ b/nixos/access/vouch.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + ... +}: let + inherit (lib.options) mkOption; + inherit (lib.modules) mkIf mkDefault mkOptionDefault; + inherit (config.services) tailscale; + cfg = config.services.vouch-proxy; + access = config.services.nginx.access.vouch; +in { + options.services.nginx.access.vouch = with lib.types; { + url = mkOption { + type = str; + }; + domain = mkOption { + type = str; + default = "login.${config.networking.domain}"; + }; + localDomain = mkOption { + type = str; + default = "login.local.${config.networking.domain}"; + }; + tailDomain = mkOption { + type = str; + default = "login.tail.${config.networking.domain}"; + }; + useACMEHost = mkOption { + type = nullOr str; + default = null; + }; + }; + config.services.nginx = { + access.vouch = mkIf cfg.enable { + url = let + inherit (cfg.settings.vouch) listen; + host = if listen == "0.0.0.0" || listen == "[::]" then "localhost" else listen; + in mkOptionDefault "http://${host}:${toString cfg.port}"; + }; + virtualHosts = let + location = { + proxy.websocket.enable = true; + proxyPass = access.url; + recommendedProxySettings = false; + }; + in { + ${access.localDomain} = mkIf (access.useACMEHost != null) { + local.enable = true; + locations."/" = location; + useACMEHost = mkDefault access.useACMEHost; + forceSSL = true; + }; + ${access.tailDomain} = mkIf tailscale.enable { + local.enable = true; + locations."/" = location; + useACMEHost = mkDefault access.useACMEHost; + addSSL = mkIf (access.useACMEHost != null) (mkDefault true); + }; + }; + }; +} diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index e752a15b..6b7a12a9 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -28,6 +28,7 @@ in { nixos.access.nginx nixos.access.global nixos.access.gensokyo + nixos.access.vouch nixos.access.kanidm nixos.access.freeipa nixos.access.kitchencam @@ -59,6 +60,14 @@ in { inherit (config.services) nginx tailscale; inherit (nginx) access; in { + ${access.vouch.localDomain} = { + inherit (nginx) group; + extraDomainNames = mkMerge [ + (mkIf tailscale.enable [ + access.vouch.tailDomain + ]) + ]; + }; ${access.kanidm.domain} = { inherit (nginx) group; extraDomainNames = mkMerge [ @@ -128,15 +137,14 @@ in { services.nginx = let inherit (config.services.nginx) access; - vouch = { - authUrl = vouch-proxy.authUrl; - url = vouch-proxy.url; - proxyOrigin = "http://${tei.networking.access.hostnameForNetwork.tail}:${toString vouch-proxy.settings.vouch.port}"; - }; in { access.plex = assert plex.enable; { url = "http://${mediabox.networking.access.hostnameForNetwork.local}:32400"; }; + access.vouch = assert vouch-proxy.enable; { + url = "http://${tei.networking.access.hostnameForNetwork.tail}:${toString vouch-proxy.settings.vouch.port}"; + useACMEHost = access.vouch.localDomain; + }; access.kanidm = assert kanidm.enableServer; { inherit (kanidm.server.frontend) domain port; host = tei.networking.access.hostnameForNetwork.local; @@ -168,10 +176,8 @@ in { useACMEHost = access.plex.domain; }; ${access.kitchencam.domain} = { - inherit vouch; }; ${access.invidious.domain} = { - inherit vouch; useACMEHost = access.invidious.domain; forceSSL = true; }; diff --git a/systems/tei/nixos.nix b/systems/tei/nixos.nix index 687bec18..82e10feb 100644 --- a/systems/tei/nixos.nix +++ b/systems/tei/nixos.nix @@ -2,7 +2,6 @@ config, lib, meta, - pkgs, ... }: let inherit (lib.modules) mkIf mkMerge; diff --git a/tf/cloudflare_records.tf b/tf/cloudflare_records.tf index b31f9526..ecd6c683 100644 --- a/tf/cloudflare_records.tf +++ b/tf/cloudflare_records.tf @@ -18,6 +18,7 @@ module "hakurei_system_records" { local_subdomains = [ "prox", "id", + "login", "ldap", "freeipa", "smb",