diff --git a/desktop/ftl.host.nix b/desktop/ftl.host.nix index b32268d..adfbd4c 100644 --- a/desktop/ftl.host.nix +++ b/desktop/ftl.host.nix @@ -3,18 +3,36 @@ let # Cloudflare tunnel definitions tunnels = { + # Define each tunnel with its configuration warp = { - remoteHost = "warp.ftl.host"; # Cloudflare hostname - remoteTarget = "localhost:22"; # Where traffic routes to - localBindPort = 4401; # Local port exposed by cloudflared - logDir = "/var/lib/cloudflared"; # Directory for logs + 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 at the top level + # Define cloudflared user and group users.users.cloudflared = { isSystemUser = true; group = "cloudflared"; @@ -30,41 +48,65 @@ in { "d /var/lib/cloudflared 0755 cloudflared cloudflared -" ]; - # Systemd services per tunnel + # Create a systemd service for each tunnel systemd.services = lib.mapAttrs (name: tunnel: { - description = "Cloudflared Access TCP Tunnel for ${tunnel.remoteHost}"; + 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 = { - # The key setting to ensure the service stays running Type = "simple"; - ExecStart = "${pkgs.cloudflared}/bin/cloudflared access tcp --hostname ${tunnel.remoteHost} --url ${tunnel.remoteTarget} --port ${toString tunnel.localBindPort} --logfile ${tunnel.logDir}/${name}.log"; - - # Automatic restart + ExecStart = "${pkgs.cloudflared}/bin/cloudflared tunnel --config /var/lib/cloudflared/${name}.yml run"; Restart = "always"; RestartSec = "5s"; - - # Run as cloudflared user User = "cloudflared"; Group = "cloudflared"; - - # Standard outputs StandardOutput = "journal"; StandardError = "journal"; + + # Security hardening + ProtectSystem = "strict"; + ProtectHome = "read-only"; + PrivateTmp = true; }; }) tunnels; # Documentation metadata meta.doc = '' - This module defines persistent cloudflared TCP tunnels using access tcp. - Add tunnels by appending to the `tunnels` attribute set. - - Required keys: - - remoteHost: The public hostname exposed via Cloudflare Tunnel. - - remoteTarget: The internal service to forward traffic to (e.g. localhost:22). - - localBindPort: The port to expose locally (e.g. 4401). - - logDir: Directory path for log files. + 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) ''; }