about summary refs log tree commit diff
path: root/nixos/modules/services/security/oauth2-proxy-nginx.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/security/oauth2-proxy-nginx.nix')
-rw-r--r--nixos/modules/services/security/oauth2-proxy-nginx.nix130
1 files changed, 130 insertions, 0 deletions
diff --git a/nixos/modules/services/security/oauth2-proxy-nginx.nix b/nixos/modules/services/security/oauth2-proxy-nginx.nix
new file mode 100644
index 0000000000000..c05bd304752d1
--- /dev/null
+++ b/nixos/modules/services/security/oauth2-proxy-nginx.nix
@@ -0,0 +1,130 @@
+{ config, lib, ... }:
+let
+  cfg = config.services.oauth2-proxy.nginx;
+in
+{
+  options.services.oauth2-proxy.nginx = {
+    proxy = lib.mkOption {
+      type = lib.types.str;
+      default = config.services.oauth2-proxy.httpAddress;
+      defaultText = lib.literalExpression "config.services.oauth2-proxy.httpAddress";
+      description = ''
+        The address of the reverse proxy endpoint for oauth2-proxy
+      '';
+    };
+
+    domain = lib.mkOption {
+      type = lib.types.str;
+      description = ''
+        The domain under which the oauth2-proxy will be accesible and the path of cookies are set to.
+        This setting must be set to ensure back-redirects are working properly
+        if oauth2-proxy is configured with {option}`services.oauth2-proxy.cookie.domain`
+        or multiple {option}`services.oauth2-proxy.nginx.virtualHosts` that are not on the same domain.
+      '';
+    };
+
+    virtualHosts = lib.mkOption {
+      type = let
+        vhostSubmodule = lib.types.submodule {
+          options = {
+            allowed_groups = lib.mkOption {
+              type = lib.types.nullOr (lib.types.listOf lib.types.str);
+              description = "List of groups to allow access to this vhost, or null to allow all.";
+              default = null;
+            };
+            allowed_emails = lib.mkOption {
+              type = lib.types.nullOr (lib.types.listOf lib.types.str);
+              description = "List of emails to allow access to this vhost, or null to allow all.";
+              default = null;
+            };
+            allowed_email_domains = lib.mkOption {
+              type = lib.types.nullOr (lib.types.listOf lib.types.str);
+              description = "List of email domains to allow access to this vhost, or null to allow all.";
+              default = null;
+            };
+          };
+        };
+        oldType = lib.types.listOf lib.types.str;
+        convertFunc = x:
+          lib.warn "services.oauth2-proxy.nginx.virtualHosts should be an attrset, found ${lib.generators.toPretty {} x}"
+          lib.genAttrs x (_: {});
+        newType = lib.types.attrsOf vhostSubmodule;
+      in lib.types.coercedTo oldType convertFunc newType;
+      default = {};
+      example = {
+        "protected.foo.com" = {
+          allowed_groups = ["admins"];
+          allowed_emails = ["boss@foo.com"];
+        };
+      };
+      description = ''
+        Nginx virtual hosts to put behind the oauth2 proxy.
+        You can exclude specific locations by setting `auth_request off;` in the locations extraConfig setting.
+      '';
+    };
+  };
+
+  config.services.oauth2-proxy = lib.mkIf (cfg.virtualHosts != [] && (lib.hasPrefix "127.0.0.1:" cfg.proxy)) {
+    enable = true;
+  };
+
+  config.services.nginx = lib.mkIf (cfg.virtualHosts != [] && config.services.oauth2-proxy.enable) (lib.mkMerge ([
+    {
+      virtualHosts.${cfg.domain}.locations."/oauth2/" = {
+        proxyPass = cfg.proxy;
+        extraConfig = ''
+          proxy_set_header X-Scheme                $scheme;
+          proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
+        '';
+      };
+    }
+  ] ++ lib.optional (cfg.virtualHosts != []) {
+    recommendedProxySettings = true; # needed because duplicate headers
+  } ++ (lib.mapAttrsToList (vhost: conf: {
+    virtualHosts.${vhost} = {
+      locations = {
+        "/oauth2/auth" = let
+          maybeQueryArg = name: value:
+            if value == null then null
+            else "${name}=${lib.concatStringsSep "," (builtins.map lib.escapeURL value)}";
+          allArgs = lib.mapAttrsToList maybeQueryArg conf;
+          cleanArgs = builtins.filter (x: x != null) allArgs;
+          cleanArgsStr = lib.concatStringsSep "&" cleanArgs;
+        in {
+          # nginx doesn't support passing query string arguments to auth_request,
+          # so pass them here instead
+          proxyPass = "${cfg.proxy}/oauth2/auth?${cleanArgsStr}";
+          extraConfig = ''
+            auth_request off;
+            proxy_set_header X-Scheme         $scheme;
+            # nginx auth_request includes headers but not body
+            proxy_set_header Content-Length   "";
+            proxy_pass_request_body           off;
+          '';
+        };
+        "@redirectToAuth2ProxyLogin" = {
+          return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri";
+          extraConfig = ''
+            auth_request off;
+          '';
+        };
+      };
+
+      extraConfig = ''
+        auth_request /oauth2/auth;
+        error_page 401 = @redirectToAuth2ProxyLogin;
+
+        # 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;
+      '';
+    };
+  }) cfg.virtualHosts)));
+}