summaryrefslogtreecommitdiffstats
path: root/nixos/modules/services/security/oauth2-proxy-nginx.nix
blob: d174bedbf5fbf1972fc25f3865244a26f6107c95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
{ 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 = ''
              auth_request off;
              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 = {
            "/".extraConfig = ''
              auth_request_set $user   $upstream_http_x_auth_request_user;
              auth_request_set $email  $upstream_http_x_auth_request_email;
              auth_request_set $auth_cookie $upstream_http_set_cookie;

              # pass information via X-User and X-Email headers to backend, requires running with --set-xauthrequest flag
              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
              add_header Set-Cookie $auth_cookie;
            '';

            "= /oauth2/auth" =
              let
                maybeQueryArg =
                  name: value:
                  if value == null then null else "${name}=${lib.concatStringsSep "," (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;
          '';
        };
      }) cfg.virtualHosts)
    )
  );
}