1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.cloudflare-warp;
9in
10{
11 options.services.cloudflare-warp = {
12 enable = lib.mkEnableOption "Cloudflare Zero Trust client daemon";
13
14 package = lib.mkPackageOption pkgs "cloudflare-warp" { };
15
16 rootDir = lib.mkOption {
17 type = lib.types.str;
18 default = "/var/lib/cloudflare-warp";
19 description = ''
20 Working directory for the warp-svc daemon.
21 '';
22 };
23
24 udpPort = lib.mkOption {
25 type = lib.types.port;
26 default = 2408;
27 description = ''
28 The UDP port to open in the firewall. Warp uses port 2408 by default, but fallback ports can be used
29 if that conflicts with another service. See the [firewall documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/deployment/firewall#warp-udp-ports)
30 for the pre-configured available fallback ports.
31 '';
32 };
33
34 openFirewall = lib.mkEnableOption "opening UDP ports in the firewall" // {
35 default = true;
36 };
37 };
38
39 config = lib.mkIf cfg.enable {
40 environment.systemPackages = [ cfg.package ];
41
42 networking.firewall = lib.mkIf cfg.openFirewall {
43 allowedUDPPorts = [ cfg.udpPort ];
44 };
45
46 systemd.tmpfiles.rules = [
47 "d ${cfg.rootDir} - root root"
48 "z ${cfg.rootDir} - root root"
49 ];
50
51 systemd.services.cloudflare-warp = {
52 enable = true;
53 description = "Cloudflare Zero Trust Client Daemon";
54
55 # lsof is used by the service to determine which UDP port to bind to
56 # in the case that it detects collisions.
57 path = [ pkgs.lsof ];
58 requires = [ "network.target" ];
59 wantedBy = [ "multi-user.target" ];
60
61 serviceConfig =
62 let
63 caps = [
64 "CAP_NET_ADMIN"
65 "CAP_NET_BIND_SERVICE"
66 "CAP_SYS_PTRACE"
67 ];
68 in
69 {
70 Type = "simple";
71 ExecStart = "${cfg.package}/bin/warp-svc";
72 ReadWritePaths = [
73 "${cfg.rootDir}"
74 "/etc/resolv.conf"
75 ];
76 CapabilityBoundingSet = caps;
77 AmbientCapabilities = caps;
78 Restart = "always";
79 RestartSec = 5;
80 Environment = [ "RUST_BACKTRACE=full" ];
81 WorkingDirectory = cfg.rootDir;
82
83 # See the systemd.exec docs for the canonicalized paths, the service
84 # makes use of them for logging, and account state info tracking.
85 # https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=
86 StateDirectory = "cloudflare-warp";
87 RuntimeDirectory = "cloudflare-warp";
88 LogsDirectory = "cloudflare-warp";
89
90 # The service needs to write to /etc/resolv.conf to configure DNS, so that file would have to
91 # be world read/writable to run as anything other than root.
92 User = "root";
93 Group = "root";
94 };
95 };
96 };
97
98 meta.maintainers = with lib.maintainers; [ treyfortmuller ];
99}