{ config, lib, pkgs, ... }: let # Cloudflare tunnel definitions tunnels = { # Define each tunnel with its configuration warp = { name = "ftlhost-ssh"; # Tunnel name (used for service name and config) id = ""; # Leave empty initially, you'll need to create and fill this hostname = "warp.ftl.host"; # Cloudflare DNS hostname service = "ssh://localhost:22"; # Local service to route traffic to }; # Add more tunnels here as needed }; # Function to generate config file content for each tunnel tunnelConfigFor = name: tunnel: ''{ "tunnel": "${tunnel.id}", "credentials-file": "/var/lib/cloudflared/${name}.json", "logfile": "/var/lib/cloudflared/${name}.log", "loglevel": "info", "ingress": [ { "hostname": "${tunnel.hostname}", "service": "${tunnel.service}" }, {"service": "http_status:404"} ] }''; in { # Install cloudflared environment.systemPackages = with pkgs; [ cloudflared ]; # Define cloudflared user and group users.users.cloudflared = { isSystemUser = true; group = "cloudflared"; description = "Cloudflared service user"; home = "/var/lib/cloudflared"; createHome = true; }; users.groups.cloudflared = {}; # Ensure /var/lib/cloudflared exists with proper permissions systemd.tmpfiles.rules = [ "d /var/lib/cloudflared 0755 cloudflared cloudflared -" ]; # Create a systemd service for each tunnel systemd.services = lib.mapAttrs (name: tunnel: { description = "Cloudflare Tunnel for ${tunnel.hostname}"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; # Create config file before starting the service preStart = '' # Create config file if it doesn't exist if [ ! -f "/var/lib/cloudflared/${name}.yml" ]; then echo '${tunnelConfigFor name tunnel}' > /var/lib/cloudflared/${name}.yml chown cloudflared:cloudflared /var/lib/cloudflared/${name}.yml chmod 600 /var/lib/cloudflared/${name}.yml fi ''; # Service configuration serviceConfig = { Type = "simple"; ExecStart = "${pkgs.cloudflared}/bin/cloudflared tunnel --config /var/lib/cloudflared/${name}.yml run"; Restart = "always"; RestartSec = "5s"; User = "cloudflared"; Group = "cloudflared"; StandardOutput = "journal"; StandardError = "journal"; # Security hardening ProtectSystem = "strict"; ProtectHome = "read-only"; PrivateTmp = true; }; }) tunnels; # Documentation metadata meta.doc = '' This module defines persistent Cloudflare Tunnels using the tunnel command. Before using this module: 1. Create a tunnel for each service with: $ sudo -u cloudflared cloudflared tunnel create 2. Get the tunnel ID with: $ sudo -u cloudflared cloudflared tunnel list 3. Add the tunnel ID to the corresponding tunnel definition in this file 4. Create a credentials file for each tunnel at /var/lib/cloudflared/.json This will be created automatically when you create the tunnel 5. Configure DNS for your hostname to point to the tunnel with: $ sudo -u cloudflared cloudflared tunnel route dns Add more tunnels by adding entries to the tunnels attribute set with: - name: Descriptive name for the tunnel - id: The tunnel ID (from cloudflared tunnel create) - hostname: The hostname to expose via Cloudflare - service: The local service to route to (e.g., ssh://localhost:22) ''; }