1 { config, lib, pkgs, ... }:
3 cfg = config.services.cloudflared;
6 connectTimeout = lib.mkOption {
7 type = with lib.types; nullOr str;
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).
15 tlsTimeout = lib.mkOption {
16 type = with lib.types; nullOr str;
20 Timeout for completing a TLS handshake to your origin server, if you have chosen to connect Tunnel to an HTTPS server.
24 tcpKeepAlive = lib.mkOption {
25 type = with lib.types; nullOr str;
29 The timeout after which a TCP keepalive packet is sent on a connection between Tunnel and the origin server.
33 noHappyEyeballs = lib.mkOption {
34 type = with lib.types; nullOr bool;
38 Disable the “happy eyeballs” algorithm for IPv4/IPv6 fallback if your local network has misconfigured one of the protocols.
42 keepAliveConnections = lib.mkOption {
43 type = with lib.types; nullOr int;
47 Maximum number of idle keepalive connections between Tunnel and your origin. This does not restrict the total number of concurrent connections.
51 keepAliveTimeout = lib.mkOption {
52 type = with lib.types; nullOr str;
56 Timeout after which an idle keepalive connection can be discarded.
60 httpHostHeader = lib.mkOption {
61 type = with lib.types; nullOr str;
65 Sets the HTTP `Host` header on requests sent to the local service.
69 originServerName = lib.mkOption {
70 type = with lib.types; nullOr str;
74 Hostname that `cloudflared` should expect from your origin server certificate.
78 caPool = lib.mkOption {
79 type = with lib.types; nullOr (either str path);
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.
87 noTLSVerify = lib.mkOption {
88 type = with lib.types; nullOr bool;
92 Disables TLS verification of the certificate presented by your origin. Will allow any certificate from the origin to be accepted.
96 disableChunkedEncoding = lib.mkOption {
97 type = with lib.types; nullOr bool;
101 Disables chunked transfer encoding. Useful if you are running a WSGI server.
105 proxyAddress = lib.mkOption {
106 type = with lib.types; nullOr str;
108 example = "127.0.0.1";
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.
114 proxyPort = lib.mkOption {
115 type = with lib.types; nullOr int;
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.
123 proxyType = lib.mkOption {
124 type = with lib.types; nullOr (enum [ "" "socks" ]);
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.
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.";
146 group = lib.mkOption {
147 type = lib.types.str;
148 default = "cloudflared";
149 description = "Group under which cloudflared runs.";
152 package = lib.mkPackageOption pkgs "cloudflared" { };
154 tunnels = lib.mkOption {
158 type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
160 inherit originRequest;
162 credentialsFile = lib.mkOption {
163 type = lib.types.str;
167 See [Credentials file](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#credentials-file).
172 enabled = lib.mkOption {
173 type = with lib.types; nullOr bool;
178 See [Connect from WARP to a private network on Cloudflare using Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/).
183 default = lib.mkOption {
184 type = lib.types.str;
186 Catch-all service if no ingress matches.
190 example = "http_status:404";
193 ingress = lib.mkOption {
194 type = with lib.types; attrsOf (either str (submodule ({ hostname, ... }: {
196 inherit originRequest;
198 service = lib.mkOption {
199 type = with lib.types; nullOr str;
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).
206 example = "http://localhost:80, tcp://localhost:8000, unix:/home/production/echo.sock, hello_world or http_status:404";
209 path = lib.mkOption {
210 type = with lib.types; nullOr str;
215 If not specified, all paths will be matched.
217 example = "/*.(jpg|png|css|js)";
226 See [Ingress rules](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/).
229 "*.domain.com" = "http://localhost:80";
230 "*.anotherone.com" = "http://localhost:80";
238 "00000000-0000-0000-0000-000000000000" = {
239 credentialsFile = "/tmp/test";
242 service = "http://localhost:80";
245 default = "http_status:404";
251 config = lib.mkIf cfg.enable {
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;
262 config.services.cloudflared.tunnels;
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 {
278 "credentials-file" = tunnel.credentialsFile;
279 warp-routing = filterConfig tunnel.warp-routing;
280 originRequest = filterConfig tunnel.originRequest;
285 } // lib.getAttr key (filterConfig (filterConfig ingressesSet)))
286 (lib.attrNames ingressesSet))
291 service = lib.getAttr key ingressesStr;
293 (lib.attrNames ingressesStr))
294 ++ [{ service = tunnel.default; }];
297 mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
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" ];
306 ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
307 Restart = "on-failure";
311 config.services.cloudflared.tunnels;
313 users.users = lib.mkIf (cfg.user == "cloudflared") {
320 users.groups = lib.mkIf (cfg.group == "cloudflared") {
325 meta.maintainers = with lib.maintainers; [ bbigras anpin ];