#!/bin/bash # 跑 Cloudflare 隧道,并自动维护公网地址文件。 # # 为什么需要这个包装: # 1. public_url.txt 只有安装脚本写过一次。服务一重启地址就变了, # 文件还是旧的 —— 客户端一直在打失效地址。 # 2. 这条 4G 线路对 api.trycloudflare.com 的 TCP 握手经常超时, # cloudflared 内部会一直重试但可能卡住。所以要**超时就退出**, # 让 systemd(Restart=always)把它重新拉起来 —— 相当于自动重试。 # # 参数选择(有实测依据): # --edge-ip-version 4 这条线路的 IPv6 有地址但实际不通, # Cloudflare 边缘优先 IPv6 会一直超时。 # 协议不强制 cloudflared 自检结果是 QUIC 通、 # HTTP/2 到 region2 被挡,建议 quic。 set -u RELAY_DIR=/root/lanremote-relay URL_FILE="$RELAY_DIR/public_url.txt" LOG="$RELAY_DIR/tunnel.log" # 多久拿不到地址就放弃,交给 systemd 重启 GIVE_UP_SECONDS=90 cd "$RELAY_DIR" || exit 1 : > "$LOG" rm -f "$URL_FILE" # 后台跑 cloudflared,日志写文件 cloudflared tunnel --no-autoupdate --edge-ip-version 4 \ --url http://127.0.0.1:8080 >> "$LOG" 2>&1 & CF_PID=$! trap 'kill $CF_PID 2>/dev/null; wait $CF_PID 2>/dev/null; exit 0' TERM INT STARTED=$(date +%s) LAST="" GOT_URL=0 while kill -0 "$CF_PID" 2>/dev/null; do URL=$(grep -oE 'https://[a-z0-9]+(-[a-z0-9]+)+\.trycloudflare\.com' \ "$LOG" 2>/dev/null | tail -1) if [ -n "$URL" ] && [ "$URL" != "$LAST" ]; then LAST="$URL" GOT_URL=1 echo "$URL" > "$URL_FILE" logger -t lanremote-tunnel "公网地址: $URL" fi # 一直拿不到地址 -> 退出,让 systemd 重来 if [ "$GOT_URL" = "0" ]; then NOW=$(date +%s) if [ $((NOW - STARTED)) -gt "$GIVE_UP_SECONDS" ]; then logger -t lanremote-tunnel \ "超过 ${GIVE_UP_SECONDS} 秒没拿到地址,重启一次(这条线路经常这样)" echo "[lanremote] 超时,交给 systemd 重启" >> "$LOG" kill "$CF_PID" 2>/dev/null exit 1 fi fi sleep 2 done wait "$CF_PID" exit $?