diff --git a/hosts/sentinel/oauth2.nix b/hosts/sentinel/oauth2.nix index 649c067..dc5e732 100644 --- a/hosts/sentinel/oauth2.nix +++ b/hosts/sentinel/oauth2.nix @@ -17,7 +17,6 @@ }; services.oauth2_proxy = { - # TODO cookie refresh provider = "oidc"; scope = "openid"; loginURL = "https://${config.proxiedDomains.kanidm}/ui/oauth2"; @@ -25,9 +24,16 @@ validateURL = "https://${config.proxiedDomains.kanidm}/oauth2/openid/web-sentinel/userinfo"; clientID = "web-sentinel"; keyFile = config.age.secrets.oauth2-proxy-secret.path; - email.domains = ["*"]; - extraConfig.skip-provider-button = true; + extraConfig = { + # TODO good idea? would fail when offline + # TODO autorestart after 30 minutes, infinite times. + oidc-issuer-url = "https://${config.proxiedDomains.kanidm}/oauth2/openid/web-sentinel"; + skip-provider-button = true; + + # TODO away + show-debug-on-error = true; + }; }; } diff --git a/hosts/sentinel/secrets/oauth2-proxy-secret.age b/hosts/sentinel/secrets/oauth2-proxy-secret.age index 4b09340..8208406 100644 Binary files a/hosts/sentinel/secrets/oauth2-proxy-secret.age and b/hosts/sentinel/secrets/oauth2-proxy-secret.age differ diff --git a/hosts/ward/microvms/adguardhome/default.nix b/hosts/ward/microvms/adguardhome/default.nix index 6f6aeb4..82b9936 100644 --- a/hosts/ward/microvms/adguardhome/default.nix +++ b/hosts/ward/microvms/adguardhome/default.nix @@ -24,7 +24,6 @@ in { nodes.sentinel = { proxiedDomains.adguard = adguardhomeDomain; - extra.oauth2_proxy.nginx.virtualHosts."${adguardhomeDomain}".allowedGroups = ["adguardhome"]; services.nginx = { upstreams.adguardhome = { servers."${config.services.adguardhome.settings.bind_host}:${toString config.services.adguardhome.settings.bind_port}" = {}; @@ -36,8 +35,10 @@ in { virtualHosts.${adguardhomeDomain} = { forceSSL = true; useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert adguardhomeDomain; + oauth2.enable = true; + oauth2.allowedGroups = ["adguardhome"]; locations."/" = { - proxyPass = "https://adguardhome"; + proxyPass = "http://adguardhome"; proxyWebsockets = true; }; }; diff --git a/hosts/ward/microvms/grafana/default.nix b/hosts/ward/microvms/grafana/default.nix index 7a1fa23..922528b 100644 --- a/hosts/ward/microvms/grafana/default.nix +++ b/hosts/ward/microvms/grafana/default.nix @@ -54,7 +54,7 @@ in { forceSSL = true; useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert grafanaDomain; locations."/" = { - proxyPass = "https://grafana"; + proxyPass = "http://grafana"; proxyWebsockets = true; }; }; diff --git a/hosts/ward/microvms/loki/default.nix b/hosts/ward/microvms/loki/default.nix index ea64340..740af48 100644 --- a/hosts/ward/microvms/loki/default.nix +++ b/hosts/ward/microvms/loki/default.nix @@ -45,7 +45,7 @@ in { forceSSL = true; useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert lokiDomain; locations."/" = { - proxyPass = "https://loki"; + proxyPass = "http://loki"; proxyWebsockets = true; extraConfig = '' auth_basic "Authentication required"; @@ -58,7 +58,7 @@ in { ''; }; locations."= /ready" = { - proxyPass = "https://loki"; + proxyPass = "http://loki"; extraConfig = '' auth_basic off; access_log off; diff --git a/hosts/ward/microvms/loki/secrets/loki-basic-auth-hashes.age b/hosts/ward/microvms/loki/secrets/loki-basic-auth-hashes.age index 56c4719..8c1a251 100644 Binary files a/hosts/ward/microvms/loki/secrets/loki-basic-auth-hashes.age and b/hosts/ward/microvms/loki/secrets/loki-basic-auth-hashes.age differ diff --git a/modules/extra.nix b/modules/extra.nix index 8041604..97aa425 100644 --- a/modules/extra.nix +++ b/modules/extra.nix @@ -37,6 +37,29 @@ in { }; }; + options.services.nginx.virtualHosts = mkOption { + type = types.attrsOf (types.submodule ({config, ...}: { + options.recommendedSecurityHeaders = mkOption { + type = types.bool; + default = true; + description = mdDoc ''Whether to add additional security headers to the "/" location.''; + }; + config = mkIf config.recommendedSecurityHeaders { + locations."/".extraConfig = '' + # Enable HTTP Strict Transport Security (HSTS) + add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; + + # Minimize information leaked to other domains + add_header Referrer-Policy "origin-when-cross-origin"; + + add_header X-XSS-Protection "1; mode=block"; + add_header X-Frame-Options "DENY"; + add_header X-Content-Type-Options "nosniff"; + ''; + }; + })); + }; + config = { lib.extra = { # For a given domain, this searches for a matching wildcard acme domain that @@ -80,16 +103,6 @@ in { error_log syslog:server=unix:/dev/log; access_log syslog:server=unix:/dev/log; ssl_ecdh_curve secp384r1; - - # Enable HTTP Strict Transport Security (HSTS) - add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; - - # Minimize information leaked to other domains - add_header Referrer-Policy "origin-when-cross-origin"; - - add_header X-XSS-Protection "1; mode=block"; - add_header X-Frame-Options "DENY"; - add_header X-Content-Type-Options "nosniff"; ''; }; diff --git a/modules/oauth2-proxy.nix b/modules/oauth2-proxy.nix index c4dbd1e..19439a4 100644 --- a/modules/oauth2-proxy.nix +++ b/modules/oauth2-proxy.nix @@ -37,13 +37,24 @@ in { nginx.virtualHosts = mkOption { default = {}; - description = mdDoc '' - Access to all virtualHostst that have an entry here will be put behind - the oauth2 proxy, requiring authentication before users can access the resource. - Also set `allowedGroups` for granuar access control. - ''; - type = types.attrsOf (types.submodule { - options.allowedGroups = mkOption { + description = + mdDoc '' + ''; + type = + types.attrsOf (types.submodule { + }); + }; + }; + + options.services.nginx.virtualHosts = mkOption { + type = types.attrsOf (types.submodule ({ + name, + config, + ... + }: { + options.oauth2 = { + enable = mkEnableOption (mdDoc "access protection of this resource using oauth2_proxy."); + allowedGroups = mkOption { type = types.listOf types.str; default = []; description = mdDoc '' @@ -51,21 +62,73 @@ in { empty list to allow any authenticated client. ''; }; - }); - }; + }; + config = mkIf config.oauth2.enable { + locations."/".extraConfig = '' + auth_request /oauth2/auth; + error_page 401 = /oauth2/sign_in; + + # pass information via X-User and X-Email headers to backend, + # requires running with --set-xauthrequest flag + auth_request_set $user $upstream_http_x_auth_request_user; + auth_request_set $email $upstream_http_x_auth_request_email; + proxy_set_header X-User $user; + proxy_set_header X-Email $email; + + # if you enabled --cookie-refresh, this is needed for it to work with auth_request + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + ''; + + locations."/oauth2/" = { + proxyPass = "https://${cfg.authProxyDomain}"; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + ''; + }; + + locations."= /oauth2/auth" = { + proxyPass = + "https://${cfg.authProxyDomain}" + + optionalString (config.oauth2.allowedGroups != []) + "?allowed_groups=${concatStringsSep "," config.oauth2.allowedGroups}"; + extraConfig = '' + internal; + + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; + }; + })); }; config = mkIf cfg.enable { services.oauth2_proxy = { enable = true; + + cookie.domain = ".${cfg.cookieDomain}"; cookie.secure = true; cookie.httpOnly = false; + cookie.refresh = "5m"; + cookie.expire = "30m"; + reverseProxy = true; httpAddress = "unix:///run/oauth2_proxy/oauth2_proxy.sock"; - - # Share the cookie with all subpages - extraConfig.whitelist-domain = ".${cfg.cookieDomain}"; setXauthrequest = true; + + extraConfig = { + # Share the cookie with all subpages + whitelist-domain = ".${cfg.cookieDomain}"; + set-authorization-header = true; + pass-access-token = true; + skip-jwt-bearer-tokens = true; + upstream = "static://202"; + # TODO allowed group? + }; }; systemd.services.oauth2_proxy.serviceConfig = { @@ -84,78 +147,33 @@ in { ''; }; - virtualHosts = - { - ${cfg.authProxyDomain} = { - forceSSL = true; - useACMEHost = config.lib.extra.matchingWildcardCert cfg.authProxyDomain; + virtualHosts.${cfg.authProxyDomain} = { + forceSSL = true; + useACMEHost = config.lib.extra.matchingWildcardCert cfg.authProxyDomain; - locations."/".extraConfig = '' - deny all; - return 404; - ''; + locations."/".extraConfig = '' + deny all; + return 404; + ''; - locations."/oauth2/" = { - proxyPass = "http://oauth2_proxy"; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; - ''; - }; - - locations."= /oauth2/auth" = { - proxyPass = "http://oauth2_proxy"; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - # nginx auth_request includes headers but not body - proxy_set_header Content-Length ""; - proxy_pass_request_body off; - ''; - }; - }; - } - // flip mapAttrs cfg.nginx.virtualHosts - (vhost: vhostCfg: let - authQuery = - optionalString (vhostCfg.allowedGroups != []) - "?allowed_groups=${concatStringsSep "," vhostCfg.allowedGroups}"; - in { - locations."/".extraConfig = '' - auth_request /oauth2/auth; - error_page 401 = /oauth2/sign_in; - - # pass information via X-User and X-Email headers to backend, - # requires running with --set-xauthrequest flag - auth_request_set $user $upstream_http_x_auth_request_user; - auth_request_set $email $upstream_http_x_auth_request_email; - proxy_set_header X-User $user; - proxy_set_header X-Email $email; - - # if you enabled --cookie-refresh, this is needed for it to work with auth_request - auth_request_set $auth_cookie $upstream_http_set_cookie; - add_header Set-Cookie $auth_cookie; + locations."/oauth2/" = { + proxyPass = "http://oauth2_proxy"; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; ''; + }; - locations."/oauth2/" = { - proxyPass = "https://${cfg.authProxyDomain}"; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; - ''; - }; - - locations."= /oauth2/auth" = { - proxyPass = "https://${cfg.authProxyDomain}${authQuery}"; - extraConfig = '' - internal; - - proxy_set_header X-Scheme $scheme; - # nginx auth_request includes headers but not body - proxy_set_header Content-Length ""; - proxy_pass_request_body off; - ''; - }; - }); + locations."= /oauth2/auth" = { + proxyPass = "http://oauth2_proxy"; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; + }; }; }; }