Simplify cloudflared tunnel configuration

This commit is contained in:
Julian Sutter 2025-07-18 22:18:55 -07:00
parent 8c5b711681
commit bddfe0b656

View file

@ -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 <name>
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/<name>.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 <tunnel-id> <hostname>
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.
'';
}