Files
shell/dev/cf-tunnel-mac
2025-12-31 06:02:09 +08:00

258 lines
8.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# https://github.com/sky22333/shell
# macOS 兼容版本 - 修复了 BSD grep 和 launchctl 兼容性问题
set -e
# === 颜色定义 ===
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m' # 清除颜色
# 检测操作系统与架构
OS_TYPE=$(uname -s)
ARCH=$(uname -m)
if [[ "$OS_TYPE" == "Darwin" ]]; then
# macOS 配置
if [[ "$ARCH" == "arm64" ]]; then
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/download/2025.11.1/cloudflared-darwin-arm64.tgz"
else
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/download/2025.11.1/cloudflared-darwin-amd64.tgz"
fi
CLOUDFLARED_BIN="/usr/local/bin/cloudflared"
SERVICE_PATH="$HOME/Library/LaunchAgents/com.cloudflare.cloudflared.plist"
LOG_PATH="$HOME/Library/Logs/cloudflared.log"
USE_LAUNCHCTL=true
else
# Linux 配置
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/download/2025.11.1/cloudflared-linux-arm64"
else
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/download/2025.11.1/cloudflared-linux-amd64"
fi
CLOUDFLARED_BIN="/usr/local/bin/cloudflared"
SERVICE_PATH="/etc/systemd/system/cloudflared-tunnel.service"
LOG_PATH="/var/log/cloudflared.log"
USE_LAUNCHCTL=false
fi
# 检查 cloudflared 是否已存在
if [[ -f "$CLOUDFLARED_BIN" ]]; then
echo -e "${GREEN}已存在文件,跳过下载。${NC}"
else
echo -e "${BLUE}正在下载 cloudflared...${NC}"
if [[ "$OS_TYPE" == "Darwin" ]]; then
# macOS: 下载 tgz 包并解压出可执行文件
TMP_TGZ=$(mktemp /tmp/cloudflared.XXXXXX.tgz)
if ! curl -L "$CLOUDFLARED_URL" -o "$TMP_TGZ"; then
echo -e "${RED}下载失败,请检查网络连接或 URL。${NC}"
exit 1
fi
tar -xzf "$TMP_TGZ" -C /tmp
# 解压后通常得到 cloudflared 可执行文件
if [[ -f "/tmp/cloudflared" ]]; then
sudo mv /tmp/cloudflared "$CLOUDFLARED_BIN"
else
# 有些发布包会包含子目录
FOUND=$(find /tmp -maxdepth 2 -type f -name cloudflared 2>/dev/null | head -n1)
if [[ -n "$FOUND" ]]; then
sudo mv "$FOUND" "$CLOUDFLARED_BIN"
else
echo -e "${RED}未能在压缩包中找到 cloudflared 可执行文件${NC}"
exit 1
fi
fi
rm -f "$TMP_TGZ"
else
# Linux: 直接下载二进制
if ! curl -L "$CLOUDFLARED_URL" -o "$CLOUDFLARED_BIN"; then
echo -e "${RED}下载失败,请检查网络连接或 URL。${NC}"
exit 1
fi
fi
chmod +x "$CLOUDFLARED_BIN"
fi
# 检查服务是否存在(根据系统选择 launchctl 或 systemd
SERVICE_EXISTS=false
if [[ "$USE_LAUNCHCTL" == true ]]; then
if launchctl list | grep -q 'com.cloudflare.cloudflared'; then
SERVICE_EXISTS=true
echo -e "${YELLOW}已检测到 cloudflared launchctl 服务${NC}"
read -p "是否要卸载旧服务?(y/n): " UNINSTALL
if [[ "$UNINSTALL" == "y" || "$UNINSTALL" == "Y" ]]; then
echo -e "${BLUE}正在卸载旧服务...${NC}"
launchctl unload "$SERVICE_PATH" 2>/dev/null || true
rm -f "$SERVICE_PATH"
rm -f "$LOG_PATH"
SERVICE_EXISTS=false
echo -e "${GREEN}服务卸载完成${NC}"
else
echo -e "${YELLOW}将保留旧服务配置,仅修改穿透地址${NC}"
fi
fi
else
if sudo systemctl list-units --full --all | grep -q 'cloudflared-tunnel.service'; then
SERVICE_EXISTS=true
echo -e "${YELLOW}已检测到 cloudflared-tunnel systemd 服务${NC}"
read -p "是否要卸载旧服务?(y/n): " UNINSTALL
if [[ "$UNINSTALL" == "y" || "$UNINSTALL" == "Y" ]]; then
echo -e "${BLUE}正在卸载旧服务...${NC}"
sudo systemctl stop cloudflared-tunnel || true
sudo systemctl disable cloudflared-tunnel || true
sudo rm -f "$SERVICE_PATH"
sudo rm -f "$LOG_PATH"
sudo systemctl daemon-reload
SERVICE_EXISTS=false
echo -e "${GREEN}服务卸载完成${NC}"
else
echo -e "${YELLOW}将保留旧服务配置,仅修改穿透地址${NC}"
fi
fi
fi
# 用户选择运行模式
echo ""
echo -e "${YELLOW}请选择运行模式:${NC}"
echo "1) 临时运行(前台运行并显示临时访问域名)"
echo "2) 后台运行(自动配置后台服务并显示访问域名)"
read -p "请输入 1 或 2: " MODE
# 输入内网地址
read -p "请输入要穿透的本地地址(例如 127.0.0.1:8080: " LOCAL_ADDR
if [[ "$MODE" == "1" ]]; then
echo -e "${BLUE}正在前台运行 cloudflared...${NC}"
LOGFILE=$(mktemp)
# macOS 无 stdbuf用 script 捕获输出(同时兼容 Linux
if command -v stdbuf >/dev/null 2>&1; then
stdbuf -oL "$CLOUDFLARED_BIN" tunnel --url "$LOCAL_ADDR" 2>&1 | tee "$LOGFILE" &
else
"$CLOUDFLARED_BIN" tunnel --url "$LOCAL_ADDR" 2>&1 | tee "$LOGFILE" &
fi
PID=$!
echo -e "${YELLOW}等待 cloudflared 输出访问域名...${NC}"
for i in {1..60}; do
# BSD grep 无 -P用 -E 实现扩展正则
DOMAIN=$(grep -Eo 'https://[A-Za-z0-9-]+\.trycloudflare\.com' "$LOGFILE" | head -n1)
if [[ -n "$DOMAIN" ]]; then
echo ""
echo -e "${GREEN}成功获取公网临时访问域名:$DOMAIN${NC}"
echo ""
wait $PID
exit 0
fi
sleep 1
done
echo -e "${RED}超时未能获取临时域名,日志保存在:$LOGFILE${NC}"
kill $PID 2>/dev/null || true
exit 1
elif [[ "$MODE" == "2" ]]; then
if [[ "$USE_LAUNCHCTL" == true ]]; then
echo -e "${BLUE}正在配置 launchctl 服务...${NC}"
mkdir -p "$(dirname "$SERVICE_PATH")" "$(dirname "$LOG_PATH")"
cat > "$SERVICE_PATH" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.cloudflare.cloudflared</string>
<key>ProgramArguments</key>
<array>
<string>$CLOUDFLARED_BIN</string>
<string>tunnel</string>
<string>--url</string>
<string>$LOCAL_ADDR</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>$LOG_PATH</string>
<key>StandardErrorPath</key>
<string>$LOG_PATH</string>
</dict>
</plist>
EOF
launchctl unload "$SERVICE_PATH" 2>/dev/null || true
launchctl load "$SERVICE_PATH"
launchctl start com.cloudflare.cloudflared || true
else
echo -e "${BLUE}正在配置 systemd 服务...${NC}"
if [[ "$SERVICE_EXISTS" == false ]]; then
sudo bash -c "cat > $SERVICE_PATH" <<EOF
[Unit]
Description=Cloudflared Tunnel Service
After=network.target
[Service]
ExecStart=$CLOUDFLARED_BIN tunnel --url $LOCAL_ADDR
Restart=always
StandardOutput=append:$LOG_PATH
StandardError=append:$LOG_PATH
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared-tunnel
else
echo -e "${YELLOW}更新 systemd 服务配置中的穿透地址...${NC}"
sudo truncate -s 0 "$LOG_PATH" || sudo bash -c "> $LOG_PATH"
sudo sed -i "s|ExecStart=.*|ExecStart=$CLOUDFLARED_BIN tunnel --url $LOCAL_ADDR|" "$SERVICE_PATH"
sudo systemctl daemon-reload
sudo systemctl restart cloudflared-tunnel
fi
fi
echo -e "${GREEN}服务已启动,日志保存在 $LOG_PATH${NC}"
echo -e "${YELLOW}等待 cloudflared 输出访问域名...${NC}"
# 等待日志文件创建
for i in {1..10}; do
if [[ -f "$LOG_PATH" ]]; then
break
fi
sleep 1
done
# 如果日志文件仍不存在,尝试创建
if [[ ! -f "$LOG_PATH" ]]; then
mkdir -p "$(dirname "$LOG_PATH")"
touch "$LOG_PATH" 2>/dev/null || true
fi
for i in {1..60}; do
if [[ -f "$LOG_PATH" ]]; then
DOMAIN=$(grep -Eo 'https://[A-Za-z0-9-]+\.trycloudflare\.com' "$LOG_PATH" 2>/dev/null | head -n1)
if [[ -n "$DOMAIN" ]]; then
echo ""
echo -e "${GREEN}成功获取公网访问域名:$DOMAIN${NC}"
echo ""
exit 0
fi
fi
sleep 1
done
echo -e "${RED}超时未能获取公网访问域名,请稍后手动查看:$LOG_PATH${NC}"
if [[ "$USE_LAUNCHCTL" == true ]]; then
echo -e "${YELLOW}您也可以使用以下命令查看服务状态:${NC}"
echo "launchctl list | grep cloudflared"
echo "tail -f $LOG_PATH"
fi
exit 1
else
echo -e "${RED}无效输入,请输入 1 或 2${NC}"
exit 1
fi