1{ config, lib, ... }:
2
3let
4 inherit (lib)
5 genAttrs
6 maintainers
7 mkAliasOptionModule
8 mkEnableOption
9 mkIf
10 mkOption
11 types
12 ;
13 cfg = config.services.nginx.tailscaleAuth;
14 cfgAuth = config.services.tailscaleAuth;
15in
16{
17 imports = [
18 (mkAliasOptionModule
19 [ "services" "nginx" "tailscaleAuth" "package" ]
20 [ "services" "tailscaleAuth" "package" ]
21 )
22 (mkAliasOptionModule
23 [ "services" "nginx" "tailscaleAuth" "user" ]
24 [ "services" "tailscaleAuth" "user" ]
25 )
26 (mkAliasOptionModule
27 [ "services" "nginx" "tailscaleAuth" "group" ]
28 [ "services" "tailscaleAuth" "group" ]
29 )
30 (mkAliasOptionModule
31 [ "services" "nginx" "tailscaleAuth" "socketPath" ]
32 [ "services" "tailscaleAuth" "socketPath" ]
33 )
34 ];
35
36 options.services.nginx.tailscaleAuth = {
37 enable = mkEnableOption "tailscale.nginx-auth, to authenticate nginx users via tailscale";
38
39 expectedTailnet = mkOption {
40 default = "";
41 type = types.nullOr types.str;
42 example = "tailnet012345.ts.net";
43 description = ''
44 If you want to prevent node sharing from allowing users to access services
45 across tailnets, declare your expected tailnets domain here.
46 '';
47 };
48
49 virtualHosts = mkOption {
50 type = types.listOf types.str;
51 default = [ ];
52 description = ''
53 A list of nginx virtual hosts to put behind tailscale.nginx-auth
54 '';
55 };
56 };
57
58 config = mkIf cfg.enable {
59 services.tailscaleAuth.enable = true;
60 services.nginx.enable = true;
61
62 users.users.${config.services.nginx.user}.extraGroups = [ cfgAuth.group ];
63
64 systemd.services.tailscale-nginx-auth = {
65 after = [ "nginx.service" ];
66 wants = [ "nginx.service" ];
67 };
68
69 services.nginx.virtualHosts = genAttrs cfg.virtualHosts (vhost: {
70 locations."/auth" = {
71 extraConfig = ''
72 internal;
73
74 proxy_pass http://unix:${cfgAuth.socketPath};
75 proxy_pass_request_body off;
76
77 # Upstream uses $http_host here, but we are using gixy to check nginx configurations
78 # gixy wants us to use $host: https://github.com/yandex/gixy/blob/master/docs/en/plugins/hostspoofing.md
79 proxy_set_header Host $host;
80 proxy_set_header Remote-Addr $remote_addr;
81 proxy_set_header Remote-Port $remote_port;
82 proxy_set_header Original-URI $request_uri;
83 proxy_set_header X-Scheme $scheme;
84 proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
85 '';
86 };
87 locations."/".extraConfig = ''
88 auth_request /auth;
89 auth_request_set $auth_user $upstream_http_tailscale_user;
90 auth_request_set $auth_name $upstream_http_tailscale_name;
91 auth_request_set $auth_login $upstream_http_tailscale_login;
92 auth_request_set $auth_tailnet $upstream_http_tailscale_tailnet;
93 auth_request_set $auth_profile_picture $upstream_http_tailscale_profile_picture;
94
95 proxy_set_header X-Webauth-User "$auth_user";
96 proxy_set_header X-Webauth-Name "$auth_name";
97 proxy_set_header X-Webauth-Login "$auth_login";
98 proxy_set_header X-Webauth-Tailnet "$auth_tailnet";
99 proxy_set_header X-Webauth-Profile-Picture "$auth_profile_picture";
100
101 ${lib.optionalString (
102 cfg.expectedTailnet != ""
103 ) ''proxy_set_header Expected-Tailnet "${cfg.expectedTailnet}";''}
104 '';
105 });
106 };
107
108 meta.maintainers = with maintainers; [ phaer ];
109
110}