vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / cloudflared.nix
blob7023e79d31f92f410fe960888ab1213f8768dd19
1 { config, lib, pkgs, ... }:
2 let
3   cfg = config.services.cloudflared;
5   originRequest = {
6     connectTimeout = lib.mkOption {
7       type = with lib.types; nullOr str;
8       default = null;
9       example = "30s";
10       description = ''
11         Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [tlsTimeout](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout).
12       '';
13     };
15     tlsTimeout = lib.mkOption {
16       type = with lib.types; nullOr str;
17       default = null;
18       example = "10s";
19       description = ''
20         Timeout for completing a TLS handshake to your origin server, if you have chosen to connect Tunnel to an HTTPS server.
21       '';
22     };
24     tcpKeepAlive = lib.mkOption {
25       type = with lib.types; nullOr str;
26       default = null;
27       example = "30s";
28       description = ''
29         The timeout after which a TCP keepalive packet is sent on a connection between Tunnel and the origin server.
30       '';
31     };
33     noHappyEyeballs = lib.mkOption {
34       type = with lib.types; nullOr bool;
35       default = null;
36       example = false;
37       description = ''
38         Disable the “happy eyeballs” algorithm for IPv4/IPv6 fallback if your local network has misconfigured one of the protocols.
39       '';
40     };
42     keepAliveConnections = lib.mkOption {
43       type = with lib.types; nullOr int;
44       default = null;
45       example = 100;
46       description = ''
47         Maximum number of idle keepalive connections between Tunnel and your origin. This does not restrict the total number of concurrent connections.
48       '';
49     };
51     keepAliveTimeout = lib.mkOption {
52       type = with lib.types; nullOr str;
53       default = null;
54       example = "1m30s";
55       description = ''
56         Timeout after which an idle keepalive connection can be discarded.
57       '';
58     };
60     httpHostHeader = lib.mkOption {
61       type = with lib.types; nullOr str;
62       default = null;
63       example = "";
64       description = ''
65         Sets the HTTP `Host` header on requests sent to the local service.
66       '';
67     };
69     originServerName = lib.mkOption {
70       type = with lib.types; nullOr str;
71       default = null;
72       example = "";
73       description = ''
74         Hostname that `cloudflared` should expect from your origin server certificate.
75       '';
76     };
78     caPool = lib.mkOption {
79       type = with lib.types; nullOr (either str path);
80       default = null;
81       example = "";
82       description = ''
83         Path to the certificate authority (CA) for the certificate of your origin. This option should be used only if your certificate is not signed by Cloudflare.
84       '';
85     };
87     noTLSVerify = lib.mkOption {
88       type = with lib.types; nullOr bool;
89       default = null;
90       example = false;
91       description = ''
92         Disables TLS verification of the certificate presented by your origin. Will allow any certificate from the origin to be accepted.
93       '';
94     };
96     disableChunkedEncoding = lib.mkOption {
97       type = with lib.types; nullOr bool;
98       default = null;
99       example = false;
100       description = ''
101         Disables chunked transfer encoding. Useful if you are running a WSGI server.
102       '';
103     };
105     proxyAddress = lib.mkOption {
106       type = with lib.types; nullOr str;
107       default = null;
108       example = "127.0.0.1";
109       description = ''
110         `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen address for that proxy.
111       '';
112     };
114     proxyPort = lib.mkOption {
115       type = with lib.types; nullOr int;
116       default = null;
117       example = 0;
118       description = ''
119         `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen port for that proxy. If set to zero, an unused port will randomly be chosen.
120       '';
121     };
123     proxyType = lib.mkOption {
124       type = with lib.types; nullOr (enum [ "" "socks" ]);
125       default = null;
126       example = "";
127       description = ''
128         `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures what type of proxy will be started. Valid options are:
130         - `""` for the regular proxy
131         - `"socks"` for a SOCKS5 proxy. Refer to the [tutorial on connecting through Cloudflare Access using kubectl](https://developers.cloudflare.com/cloudflare-one/tutorials/kubectl/) for more information.
132       '';
133     };
134   };
137   options.services.cloudflared = {
138     enable = lib.mkEnableOption "Cloudflare Tunnel client daemon (formerly Argo Tunnel)";
140     user = lib.mkOption {
141       type = lib.types.str;
142       default = "cloudflared";
143       description = "User account under which Cloudflared runs.";
144     };
146     group = lib.mkOption {
147       type = lib.types.str;
148       default = "cloudflared";
149       description = "Group under which cloudflared runs.";
150     };
152     package = lib.mkPackageOption pkgs "cloudflared" { };
154     tunnels = lib.mkOption {
155       description = ''
156         Cloudflare tunnels.
157       '';
158       type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
159         options = {
160           inherit originRequest;
162           credentialsFile = lib.mkOption {
163             type = lib.types.str;
164             description = ''
165               Credential file.
167               See [Credentials file](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#credentials-file).
168             '';
169           };
171           warp-routing = {
172             enabled = lib.mkOption {
173               type = with lib.types; nullOr bool;
174               default = null;
175               description = ''
176                 Enable warp routing.
178                 See [Connect from WARP to a private network on Cloudflare using Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/).
179               '';
180             };
181           };
183           default = lib.mkOption {
184             type = lib.types.str;
185             description = ''
186               Catch-all service if no ingress matches.
188               See `service`.
189             '';
190             example = "http_status:404";
191           };
193           ingress = lib.mkOption {
194             type = with lib.types; attrsOf (either str (submodule ({ hostname, ... }: {
195               options = {
196                 inherit originRequest;
198                 service = lib.mkOption {
199                   type = with lib.types; nullOr str;
200                   default = null;
201                   description = ''
202                     Service to pass the traffic.
204                     See [Supported protocols](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#supported-protocols).
205                   '';
206                   example = "http://localhost:80, tcp://localhost:8000, unix:/home/production/echo.sock, hello_world or http_status:404";
207                 };
209                 path = lib.mkOption {
210                   type = with lib.types; nullOr str;
211                   default = null;
212                   description = ''
213                     Path filter.
215                     If not specified, all paths will be matched.
216                   '';
217                   example = "/*.(jpg|png|css|js)";
218                 };
220               };
221             })));
222             default = { };
223             description = ''
224               Ingress rules.
226               See [Ingress rules](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/).
227             '';
228             example = {
229               "*.domain.com" = "http://localhost:80";
230               "*.anotherone.com" = "http://localhost:80";
231             };
232           };
233         };
234       }));
236       default = { };
237       example = {
238         "00000000-0000-0000-0000-000000000000" = {
239           credentialsFile = "/tmp/test";
240           ingress = {
241             "*.domain1.com" = {
242               service = "http://localhost:80";
243             };
244           };
245           default = "http_status:404";
246         };
247       };
248     };
249   };
251   config = lib.mkIf cfg.enable {
252     systemd.targets =
253       lib.mapAttrs'
254         (name: tunnel:
255           lib.nameValuePair "cloudflared-tunnel-${name}" {
256             description = "Cloudflare tunnel '${name}' target";
257             requires = [ "cloudflared-tunnel-${name}.service" ];
258             after = [ "cloudflared-tunnel-${name}.service" ];
259             unitConfig.StopWhenUnneeded = true;
260           }
261         )
262         config.services.cloudflared.tunnels;
264     systemd.services =
265       lib.mapAttrs'
266         (name: tunnel:
267           let
268             filterConfig = lib.attrsets.filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]);
270             filterIngressSet = lib.filterAttrs (_: v: builtins.typeOf v == "set");
271             filterIngressStr = lib.filterAttrs (_: v: builtins.typeOf v == "string");
273             ingressesSet = filterIngressSet tunnel.ingress;
274             ingressesStr = filterIngressStr tunnel.ingress;
276             fullConfig = filterConfig {
277               tunnel = name;
278               "credentials-file" = tunnel.credentialsFile;
279               warp-routing = filterConfig tunnel.warp-routing;
280               originRequest = filterConfig tunnel.originRequest;
281               ingress =
282                 (map
283                   (key: {
284                     hostname = key;
285                   } // lib.getAttr key (filterConfig (filterConfig ingressesSet)))
286                   (lib.attrNames ingressesSet))
287                 ++
288                 (map
289                   (key: {
290                     hostname = key;
291                     service = lib.getAttr key ingressesStr;
292                   })
293                   (lib.attrNames ingressesStr))
294                 ++ [{ service = tunnel.default; }];
295             };
297             mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
298           in
299           lib.nameValuePair "cloudflared-tunnel-${name}" ({
300             after = [ "network.target" "network-online.target" ];
301             wants = [ "network.target" "network-online.target" ];
302             wantedBy = [ "multi-user.target" ];
303             serviceConfig = {
304               User = cfg.user;
305               Group = cfg.group;
306               ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
307               Restart = "on-failure";
308             };
309           })
310         )
311         config.services.cloudflared.tunnels;
313     users.users = lib.mkIf (cfg.user == "cloudflared") {
314       cloudflared = {
315         group = cfg.group;
316         isSystemUser = true;
317       };
318     };
320     users.groups = lib.mkIf (cfg.group == "cloudflared") {
321       cloudflared = { };
322     };
323   };
325   meta.maintainers = with lib.maintainers; [ bbigras anpin ];