diff --git a/desktop/ftl.host.nix b/desktop/ftl.host.nix index adfbd4c..d36ae50 100644 --- a/desktop/ftl.host.nix +++ b/desktop/ftl.host.nix @@ -3,110 +3,76 @@ 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 + remoteHost = "warp.ftl.host"; # Cloudflare hostname + remoteTarget = "localhost:22"; # Where traffic routes to + localBindPort = 4401; # Local port exposed by cloudflared + logPath = "/var/log/cloudflared/warp.log"; }; - # 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 + + # Ensure /var/log/cloudflared exists systemd.tmpfiles.rules = [ - "d /var/lib/cloudflared 0755 cloudflared cloudflared -" + "d /var/log/cloudflared 0755 root root -" ]; - # Create a systemd service for each tunnel + # Systemd services per tunnel systemd.services = lib.mapAttrs (name: tunnel: { - description = "Cloudflare Tunnel for ${tunnel.hostname}"; + description = "Cloudflared Access TCP Tunnel for ${tunnel.remoteHost}"; 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 + # Use StateDirectory for persistent storage of logs serviceConfig = { + StateDirectory = "cloudflared"; + StateDirectoryMode = "0755"; + RuntimeDirectory = "cloudflared"; + RuntimeDirectoryMode = "0700"; + + # The key setting to ensure the service stays running Type = "simple"; - ExecStart = "${pkgs.cloudflared}/bin/cloudflared tunnel --config /var/lib/cloudflared/${name}.yml run"; + ExecStart = "${pkgs.cloudflared}/bin/cloudflared access tcp --hostname ${tunnel.remoteHost} --url ${tunnel.remoteTarget} --port ${toString tunnel.localBindPort} --logfile /var/lib/cloudflared/warp.log"; Restart = "always"; RestartSec = "5s"; + + # Use systemd's built-in user User = "cloudflared"; Group = "cloudflared"; + + # Standard outputs StandardOutput = "journal"; StandardError = "journal"; - - # Security hardening - ProtectSystem = "strict"; - ProtectHome = "read-only"; - PrivateTmp = true; }; + + # Ensure the user exists + users.users.cloudflared = { + isSystemUser = true; + group = "cloudflared"; + description = "Cloudflared service user"; + }; + + users.groups.cloudflared = {}; }) tunnels; - # Documentation metadata + # Activation step to ensure log directory exists + system.activationScripts.cloudflaredLogs = lib.stringAfter [ "users" ] '' + mkdir -p /var/log/cloudflared + chmod 755 /var/log/cloudflared + ''; + + # Optional 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) + 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). + - logPath: File path to capture logs. ''; }