{ config, lib, pkgs, homeyConfig, ... }: # Cloudflare Tunnel (cloudflared) — remote access without open inbound ports. # # Architecture: # Internet → Cloudflare edge → cloudflared tunnel (outbound from Pi) # → Caddy on localhost → service containers # # The tunnel is configured to route each hostname to Caddy's HTTPS listener. # Caddy handles TLS and forward_auth; cloudflared just carries the traffic. # # Setup steps (one-time, done from the Cloudflare dashboard): # 1. Go to Zero Trust → Networks → Tunnels → Create a tunnel # 2. Name it (e.g. "pi-main") # 3. Copy the tunnel token — add it to secrets.yaml as cloudflare/tunnel_token # 4. In the tunnel's "Public Hostnames" config, add routes: # auth.zakobar.com → http://localhost:80 (or https://localhost:443) # git.zakobar.com → https://localhost:443 # nextcloud.zakobar.com → https://localhost:443 # ldapadmin.zakobar.com → https://localhost:443 # jellyfin.zakobar.com → https://localhost:443 # torrent.zakobar.com → https://localhost:443 # uptime.zakobar.com → https://localhost:443 # ntfy.zakobar.com → https://localhost:443 # grafana.zakobar.com → https://localhost:443 # Set "No TLS Verify" = true (Caddy's cert is from Let's Encrypt but # the hostname seen by cloudflared is localhost, so hostname verification # would fail without this flag). # # The tunnel_token approach (--token) is the simplest: one secret, no config # file needed on the Pi. # # Secrets consumed from sops: # cloudflare/tunnel_token let cfg = config.homey.cloudflared; in { options.homey.cloudflared = { enable = lib.mkEnableOption "Cloudflare Tunnel for remote access"; }; config = lib.mkIf cfg.enable { # ----------------------------------------------------------------------- # Secrets # ----------------------------------------------------------------------- sops.secrets."cloudflare/tunnel_token" = { owner = "cloudflared"; }; # ----------------------------------------------------------------------- # cloudflared service # # We use the token-based tunnel approach (cloudflared tunnel run --token). # This needs no credentials file and no local tunnel config — just the # token from the Cloudflare dashboard. # # Rather than using services.cloudflared.tunnels (which requires a # credentialsFile), we create a plain systemd service that runs cloudflared # directly with the token read from the sops secret. # ----------------------------------------------------------------------- users.users.cloudflared = { isSystemUser = true; group = "cloudflared"; description = "cloudflared tunnel daemon"; }; users.groups.cloudflared = {}; systemd.services."cloudflared-tunnel" = { description = "Cloudflare Tunnel (token-based)"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" "caddy.service" ]; wants = [ "network-online.target" "caddy.service" ]; serviceConfig = { Type = "simple"; User = "cloudflared"; Group = "cloudflared"; Restart = "on-failure"; RestartSec = "5s"; ExecStart = pkgs.writeShellScript "cloudflared-start" '' exec ${pkgs.cloudflared}/bin/cloudflared tunnel \ --no-autoupdate \ run \ --token "$(cat ${config.sops.secrets."cloudflare/tunnel_token".path})" ''; # Hardening NoNewPrivileges = true; PrivateTmp = true; }; }; }; }