|
1 | 1 | #!/usr/bin/env bash |
2 | | -# Quick Cloudflare Tunnel for Codeman |
3 | | -# Usage: ./scripts/tunnel.sh [start|stop|status|url] |
| 2 | +# Cloudflare Tunnel manager for Codeman |
| 3 | +# Usage: ./scripts/tunnel.sh [quick|named] [start|stop|status|url] |
| 4 | +# |
| 5 | +# Modes: |
| 6 | +# quick — Quick tunnel with random trycloudflare.com URL (default) |
| 7 | +# named — Named tunnel on a fixed hostname (requires setup, see below) |
| 8 | +# |
| 9 | +# Environment variables: |
| 10 | +# CLOUDFLARED_TUNNEL_NAME — tunnel name (default: codeman) |
| 11 | +# CLOUDFLARED_TUNNEL_ID — tunnel UUID (from: cloudflared tunnel list) |
| 12 | +# CODEMAN_TUNNEL_HOSTNAME — public hostname (e.g. codeman.example.com) |
| 13 | +# |
| 14 | +# First-time named tunnel setup: |
| 15 | +# cloudflared tunnel login |
| 16 | +# cloudflared tunnel create <tunnel-name> |
| 17 | +# cloudflared tunnel route dns <tunnel-name> <hostname> |
| 18 | +# ./scripts/tunnel.sh named setup # writes ~/.cloudflared/<tunnel-name>.yml |
4 | 19 | set -euo pipefail |
5 | 20 |
|
6 | | -SERVICE="codeman-tunnel" |
7 | | - |
8 | | -case "${1:-start}" in |
9 | | - start) |
10 | | - if ! systemctl --user is-active "$SERVICE" &>/dev/null; then |
11 | | - # Install service if not already |
12 | | - if ! systemctl --user cat "$SERVICE" &>/dev/null 2>&1; then |
13 | | - cp "$(dirname "$0")/codeman-tunnel.service" "$HOME/.config/systemd/user/" |
14 | | - systemctl --user daemon-reload |
15 | | - fi |
16 | | - systemctl --user start "$SERVICE" |
17 | | - echo "Tunnel starting... waiting for URL" |
18 | | - sleep 6 |
19 | | - fi |
20 | | - # Extract the tunnel URL from journal |
21 | | - URL=$(grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$HOME/.codeman/tunnel.log" 2>/dev/null | tail -1) |
22 | | - if [ -n "$URL" ]; then |
23 | | - echo "$URL" |
24 | | - else |
25 | | - echo "URL not ready yet, try: $0 url" |
26 | | - fi |
27 | | - ;; |
28 | | - stop) |
29 | | - systemctl --user stop "$SERVICE" |
30 | | - echo "Tunnel stopped" |
31 | | - ;; |
32 | | - status) |
33 | | - systemctl --user status "$SERVICE" --no-pager 2>&1 | head -10 |
34 | | - echo "" |
35 | | - echo "URL:" |
36 | | - grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$HOME/.codeman/tunnel.log" 2>/dev/null | tail -1 |
| 21 | +QUICK_SERVICE="codeman-tunnel" |
| 22 | +NAMED_SERVICE="codeman-tunnel-named" |
| 23 | +TUNNEL_NAME="${CLOUDFLARED_TUNNEL_NAME:-codeman}" |
| 24 | +TUNNEL_HOSTNAME="${CODEMAN_TUNNEL_HOSTNAME:-codeman.example.com}" |
| 25 | +CODEMAN_PORT="3000" |
| 26 | +LOG_FILE="$HOME/.codeman/tunnel.log" |
| 27 | + |
| 28 | +# ── helpers ────────────────────────────────────────────────────────────────── |
| 29 | + |
| 30 | +_require_cloudflared() { |
| 31 | + if ! command -v cloudflared &>/dev/null; then |
| 32 | + echo "Error: cloudflared not found. Install with: yay -S cloudflared" >&2 |
| 33 | + exit 1 |
| 34 | + fi |
| 35 | +} |
| 36 | + |
| 37 | +_cloudflared_bin() { |
| 38 | + command -v cloudflared |
| 39 | +} |
| 40 | + |
| 41 | +_install_service() { |
| 42 | + local svc_file="$1" |
| 43 | + local svc_name="$2" |
| 44 | + if ! systemctl --user cat "$svc_name" &>/dev/null 2>&1; then |
| 45 | + cp "$(dirname "$0")/$svc_file" "$HOME/.config/systemd/user/" |
| 46 | + systemctl --user daemon-reload |
| 47 | + echo "Service $svc_name installed." |
| 48 | + fi |
| 49 | +} |
| 50 | + |
| 51 | +_install_named_service() { |
| 52 | + if ! systemctl --user cat "$NAMED_SERVICE" &>/dev/null 2>&1; then |
| 53 | + # Generate service file with the configured tunnel name |
| 54 | + sed "s/codeman\.yml/$TUNNEL_NAME.yml/g; s/run codeman/run $TUNNEL_NAME/g" \ |
| 55 | + "$(dirname "$0")/codeman-tunnel-named.service" \ |
| 56 | + > "$HOME/.config/systemd/user/codeman-tunnel-named.service" |
| 57 | + systemctl --user daemon-reload |
| 58 | + echo "Service $NAMED_SERVICE installed (tunnel: $TUNNEL_NAME)." |
| 59 | + fi |
| 60 | +} |
| 61 | + |
| 62 | +# ── named tunnel setup ─────────────────────────────────────────────────────── |
| 63 | + |
| 64 | +_named_setup() { |
| 65 | + _require_cloudflared |
| 66 | + |
| 67 | + local creds_dir="$HOME/.cloudflared" |
| 68 | + local config_file="$creds_dir/$TUNNEL_NAME.yml" |
| 69 | + # Replace with your tunnel ID (from: cloudflared tunnel list) |
| 70 | + local tunnel_id="${CLOUDFLARED_TUNNEL_ID:-YOUR_TUNNEL_ID_HERE}" |
| 71 | + local creds_file="$creds_dir/$tunnel_id.json" |
| 72 | + |
| 73 | + if [ ! -f "$creds_file" ]; then |
| 74 | + echo "Credentials not found: $creds_file" |
| 75 | + echo "Run: cloudflared tunnel create $TUNNEL_NAME" |
| 76 | + exit 1 |
| 77 | + fi |
| 78 | + |
| 79 | + cat > "$config_file" <<EOF |
| 80 | +tunnel: $tunnel_id |
| 81 | +credentials-file: $creds_file |
| 82 | +
|
| 83 | +ingress: |
| 84 | + - hostname: $TUNNEL_HOSTNAME |
| 85 | + service: http://localhost:$CODEMAN_PORT |
| 86 | + - service: http_status:404 |
| 87 | +EOF |
| 88 | + |
| 89 | + echo "Config written to $config_file" |
| 90 | + echo "Tunnel ID: $tunnel_id" |
| 91 | + echo "Hostname: $TUNNEL_HOSTNAME" |
| 92 | + echo "" |
| 93 | + echo "Next steps:" |
| 94 | + echo " 1. Add Cloudflare Access policy for $TUNNEL_HOSTNAME (Zero Trust dashboard)" |
| 95 | + echo " 2. ./scripts/tunnel.sh named start" |
| 96 | +} |
| 97 | + |
| 98 | +# ── quick mode ─────────────────────────────────────────────────────────────── |
| 99 | + |
| 100 | +_quick_start() { |
| 101 | + if ! systemctl --user is-active "$QUICK_SERVICE" &>/dev/null; then |
| 102 | + _install_service "codeman-tunnel.service" "$QUICK_SERVICE" |
| 103 | + systemctl --user start "$QUICK_SERVICE" |
| 104 | + echo "Quick tunnel starting... waiting for URL" |
| 105 | + sleep 6 |
| 106 | + fi |
| 107 | + local url |
| 108 | + url=$(grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$LOG_FILE" 2>/dev/null | tail -1) |
| 109 | + if [ -n "$url" ]; then |
| 110 | + echo "$url" |
| 111 | + else |
| 112 | + echo "URL not ready yet, try: $0 quick url" |
| 113 | + fi |
| 114 | +} |
| 115 | + |
| 116 | +_quick_stop() { |
| 117 | + systemctl --user stop "$QUICK_SERVICE" |
| 118 | + echo "Quick tunnel stopped" |
| 119 | +} |
| 120 | + |
| 121 | +_quick_status() { |
| 122 | + systemctl --user status "$QUICK_SERVICE" --no-pager 2>&1 | head -10 |
| 123 | + echo "" |
| 124 | + echo "URL:" |
| 125 | + grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$LOG_FILE" 2>/dev/null | tail -1 |
| 126 | +} |
| 127 | + |
| 128 | +_quick_url() { |
| 129 | + grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$LOG_FILE" 2>/dev/null | tail -1 |
| 130 | +} |
| 131 | + |
| 132 | +# ── named mode ─────────────────────────────────────────────────────────────── |
| 133 | + |
| 134 | +_named_start() { |
| 135 | + _require_cloudflared |
| 136 | + if [ ! -f "$HOME/.cloudflared/$TUNNEL_NAME.yml" ]; then |
| 137 | + echo "Config not found. Run: $0 named setup" |
| 138 | + exit 1 |
| 139 | + fi |
| 140 | + if ! systemctl --user is-active "$NAMED_SERVICE" &>/dev/null; then |
| 141 | + _install_named_service |
| 142 | + systemctl --user start "$NAMED_SERVICE" |
| 143 | + echo "Named tunnel starting..." |
| 144 | + sleep 3 |
| 145 | + fi |
| 146 | + echo "https://$TUNNEL_HOSTNAME" |
| 147 | +} |
| 148 | + |
| 149 | +_named_stop() { |
| 150 | + systemctl --user stop "$NAMED_SERVICE" |
| 151 | + echo "Named tunnel stopped" |
| 152 | +} |
| 153 | + |
| 154 | +_named_status() { |
| 155 | + systemctl --user status "$NAMED_SERVICE" --no-pager 2>&1 | head -10 |
| 156 | + echo "" |
| 157 | + echo "URL: https://$TUNNEL_HOSTNAME" |
| 158 | +} |
| 159 | + |
| 160 | +_named_enable() { |
| 161 | + _install_named_service |
| 162 | + systemctl --user enable "$NAMED_SERVICE" |
| 163 | + echo "Named tunnel enabled at boot." |
| 164 | +} |
| 165 | + |
| 166 | +_named_disable() { |
| 167 | + systemctl --user disable "$NAMED_SERVICE" |
| 168 | + echo "Named tunnel disabled." |
| 169 | +} |
| 170 | + |
| 171 | +# ── dispatch ───────────────────────────────────────────────────────────────── |
| 172 | + |
| 173 | +MODE="${1:-quick}" |
| 174 | +CMD="${2:-start}" |
| 175 | + |
| 176 | +case "$MODE" in |
| 177 | + quick) |
| 178 | + case "$CMD" in |
| 179 | + start) _quick_start ;; |
| 180 | + stop) _quick_stop ;; |
| 181 | + status) _quick_status ;; |
| 182 | + url) _quick_url ;; |
| 183 | + *) echo "Usage: $0 quick [start|stop|status|url]"; exit 1 ;; |
| 184 | + esac |
37 | 185 | ;; |
38 | | - url) |
39 | | - grep -oP 'https://[a-z0-9-]+\.trycloudflare\.com' "$HOME/.codeman/tunnel.log" 2>/dev/null | tail -1 |
| 186 | + named) |
| 187 | + case "$CMD" in |
| 188 | + start) _named_start ;; |
| 189 | + stop) _named_stop ;; |
| 190 | + status) _named_status ;; |
| 191 | + url) echo "https://$TUNNEL_HOSTNAME" ;; |
| 192 | + setup) _named_setup ;; |
| 193 | + enable) _named_enable ;; |
| 194 | + disable) _named_disable ;; |
| 195 | + *) echo "Usage: $0 named [start|stop|status|url|setup|enable|disable]"; exit 1 ;; |
| 196 | + esac |
40 | 197 | ;; |
| 198 | + # backward compat: no mode prefix → quick tunnel |
| 199 | + start) _quick_start ;; |
| 200 | + stop) _quick_stop ;; |
| 201 | + status) _quick_status ;; |
| 202 | + url) _quick_url ;; |
41 | 203 | *) |
42 | | - echo "Usage: $0 [start|stop|status|url]" |
| 204 | + echo "Usage: $0 [quick|named] [start|stop|status|url]" |
| 205 | + echo " $0 named setup # first-time named tunnel configuration" |
| 206 | + echo " $0 named enable # start at boot" |
43 | 207 | exit 1 |
44 | 208 | ;; |
45 | 209 | esac |
0 commit comments