From fd898df5903e459982426a61a6e1b4d8e989baa3 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 30 Aug 2021 14:53:13 +0200 Subject: [PATCH] modules: services: nginx: add SSO --- modules/services/nginx.nix | 125 +++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/modules/services/nginx.nix b/modules/services/nginx.nix index c2c3a4d..f78c2df 100644 --- a/modules/services/nginx.nix +++ b/modules/services/nginx.nix @@ -34,6 +34,10 @@ let ''; }; + sso = { + enable = mkEnableOption "SSO authentication"; + }; + extraConfig = mkOption { type = types.attrs; # FIXME: forward type of virtualHosts example = litteralExample '' @@ -89,6 +93,22 @@ in List of virtual hosts to set-up using default settings. ''; }; + + sso = { + subdomain = mkOption { + type = types.str; + default = "login"; + example = "auth"; + description = "Which subdomain, to use for SSO."; + }; + + port = mkOption { + type = types.port; + default = 8082; + example = 8080; + description = "Port to use for internal webui."; + }; + }; }; config = lib.mkIf cfg.enable { @@ -170,12 +190,117 @@ in }) # VHost specific configuration args.extraConfig + # SSO configuration + (lib.optionalAttrs args.sso.enable { + extraConfig = (args.extraConfig.extraConfig or "") + '' + error_page 401 = @error401; + ''; + + locations."@error401".return = '' + 302 https://${cfg.sso.subdomain}.${config.networking.domain}/login?go=$scheme://$http_host$request_uri + ''; + + locations."/" = { + extraConfig = + (args.extraConfig.locations."/".extraConfig or "") + '' + # Use SSO + auth_request /sso-auth; + + # Set username through header + auth_request_set $username $upstream_http_x_username; + proxy_set_header X-User $username; + + # Renew SSO cookie on request + auth_request_set $cookie $upstream_http_set_cookie; + add_header Set-Cookie $cookie; + ''; + }; + + locations."/sso-auth" = { + proxyPass = "http://localhost:${toString cfg.sso.port}/auth"; + extraConfig = '' + # Do not allow requests from outside + internal; + + # Do not forward the request body + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + + # Set X-Application according to subdomain for matching + proxy_set_header X-Application "${subdomain}"; + + # Set origin URI for matching + proxy_set_header X-Origin-URI $request_uri; + ''; + }; + }) ]) ); in lib.my.genAttrs' cfg.virtualHosts mkVHost; + + sso = { + enable = true; + + configuration = { + listen = { + addr = "127.0.0.1"; + inherit (cfg.sso) port; + }; + + cookie = { + domain = ".${config.networking.domain}"; + secure = true; + authentication_key = config.my.secrets.sso.auth_key; + }; + + login = { + title = "Ambroisie's SSO"; + default_method = "simple"; + hide_mfa_field = false; + names = { + simple = "Username / Password"; + }; + }; + + providers = { + simple = + let + applyUsers = lib.flip lib.mapAttrs config.my.secrets.sso.users; + in + { + users = applyUsers (_: v: v.passwordHash); + + mfa = applyUsers (_: v: [{ + provider = "totp"; + attributes = { + secret = v.totpSecret; + }; + }]); + + inherit (config.my.secrets.sso) groups; + }; + }; + + acl = { + rule_sets = [ + { + rules = [{ field = "x-application"; present = true; }]; + allow = [ "@root" ]; + } + ]; + }; + }; + }; }; + my.services.nginx.virtualHosts = [ + { + subdomain = "login"; + inherit (cfg.sso) port; + } + ]; + networking.firewall.allowedTCPPorts = [ 80 443 ]; # Nginx needs to be able to read the certificates