Cloudflared
Cloudflared runs a Cloudflare Tunnel to expose services to the internet without opening inbound ports on the router. Unlike other services, it runs as a systemd service on the host, not in a NixOS container.
Source: server/containers/cloudflared.nix
Why Not a Container?
Cloudflared needs to establish outbound connections to Cloudflare's edge network. Running it on the host network is simpler and avoids NAT complications. The extensive systemd sandboxing provides isolation equivalent to a container.
Service Configuration
systemd.services.cloudflared-tunnel = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.cloudflared}/bin/cloudflared tunnel run --token-file ${config.age.secrets.cloudflare_tunnel_token.path}";
Restart = "on-failure";
RestartSec = 5;
User = "cloudflared";
Group = "cloudflared";
};
};The tunnel token is provided via agenix and read from a file at runtime.
Dedicated User
A dedicated system user and group are created:
users.users.cloudflared = {
isSystemUser = true;
group = "cloudflared";
};
users.groups.cloudflared = { };Systemd Sandboxing
Cloudflared has one of the most heavily sandboxed service configurations in the entire setup. See Systemd Sandboxing for the full breakdown.
Key restrictions:
| Category | Settings |
|---|---|
| Filesystem | ProtectSystem=strict, ProtectHome=true, PrivateTmp, PrivateMounts |
| Process | PrivatePIDs, PrivateUsers, PrivateDevices |
| Kernel | ProtectKernelTunables, ProtectKernelModules, ProtectKernelLogs |
| Network | Only AF_INET and AF_INET6 allowed |
| Capabilities | All dropped (CapabilityBoundingSet = null) |
| Syscalls | Restrictive filter (no @clock, @debug, @mount, @privileged, etc.) |
| Memory | MemoryDenyWriteExecute (no JIT, no writable+executable pages) |
| Other | NoNewPrivileges, LockPersonality, RestrictNamespaces, RemoveIPC |
Secret
age.secrets.cloudflare_tunnel_token = {
mode = "440";
owner = "cloudflared";
group = "cloudflared";
file = ../secrets/cloudflare_tunnel_token.age;
};The tunnel token is decrypted at activation time and made readable only by the cloudflared user.
Traffic Flow
Client (internet)
└── Cloudflare Edge
└── Cloudflare Tunnel (outbound from server)
└── Traefik (:80/:443 on localhost)
└── Service containersCloudflared establishes an outbound-only connection to Cloudflare's edge. External clients connect to Cloudflare, which proxies traffic through the tunnel to Traefik on the server. No inbound ports need to be forwarded on the router.