Cloudflare Argo 远程托管固定隧道脚本使用指南

Cloudflare Argo 远程托管固定隧道脚本使用指南

本文介绍的 Bash 脚本专为自动化管理 Cloudflare Argo (Cloudflare Tunnel) 设计。通过集成 Cloudflare Zero Trust REST API,实现了一键创建远程托管隧道、自动配置域名路由、自动绑定 DNS 记录并输出连接器 Token 的全流程自动化,省去了手动编写本地 config.yml 和管理凭证文件的步骤。

功能特性

  • 一键开通固定隧道:输入隧道名、域名和本地服务端口,自动在云端完成所有映射配置。
  • 零配置文件依赖:采用 Cloudflare 官方推荐的 Remote Managed 架构,无需本地 credentials.json 或 config.yml。
  • 自动化 DNS 解析:创建隧道时同步完成 CNAME 解析记录的添加。
  • 一键生成连接器 Token:直接输出 Token,复制即可在任意机型(Linux、Windows、Docker 等)上快速部署连接器。
  • 人性化交互:支持在任意输入步骤中输入 0 安全返回主菜单。

前置准备

1. 获取 Account ID 和 Zone ID

  1. 登录 Cloudflare 控制台。
  2. 点击你要绑定的目标域名。
  3. 进入该域名的概述页面,将页面向下拉,右侧边栏的 API 区域可以看到 Account ID 和 Zone ID。
  4. 复制备用。

2. 创建 API Token

脚本需要一个有权创建隧道和修改 DNS 的 API Token:

  1. 点击右上角头像,选择我的个人资料,然后在左侧菜单点击 API 令牌。
  2. 选择自定义令牌,开始使用。
  3. 设置令牌名称。
  4. 在权限区域,添加以下两组权限(必须准确设置):
    • 第一组:Account | Cloudflare Tunnel | Edit
    • 第二组:Zone | DNS | Edit
  5. 创建令牌,复制保存(注意只显示一次)。

3. 填写到脚本中

拿到这三个参数后,打开脚本文件,找到脚本顶部的配置文件区(约第 9~11 行):

CF_ACCOUNT_ID="你的Account_ID"
CF_API_TOKEN="你的API_Token"
CF_ZONE_ID="你的Zone_ID"

请确保保留外层的双引号,不要将等号两侧留有空格。保存并退出后,即可直接运行 bash argo_tunnel.sh 使用。

菜单功能详解

启动脚本后,你将看到如下交互菜单:

====================================================
         Cloudflare Argo 固定隧道管理               
====================================================
 1) 查看所有固定隧道
 2) 创建固定隧道 (包含域名配置与 Token 生成)
 3) 删除固定隧道
 0) 退出
----------------------------------------------------

1. 查看所有固定隧道

列出当前账户下所有未被删除的 Argo 隧道。输出信息包括 Tunnel ID、隧道名称、当前连线状态 (inactive / healthy)。

2. 创建固定隧道

选择此项后,脚本将引导你完成以下三步输入:

  1. 隧道名称:例如 my-web-tunnel。
  2. 完整域名:例如 app.yourdomain.com。
  3. 本地服务地址:默认值为 http://localhost:8001(按回车使用默认值,或自定义如 http://127.0.0.1:3000)。

自动化后台流转:

[1/4] 创建云端 Remote Managed Tunnel 
  └─► [2/4] 设置 Ingress 路由规则 (应用域名 -> 本地服务)
        └─► [3/4] 添加 DNS CNAME 记录 ($FULL_DOMAIN -> $TUNNEL_ID.cfargot.com)
              └─► [4/4] 提取 Connector Token

执行完毕后,控制台会高亮输出一长串连接器 Token。

3. 删除固定隧道

输入目标隧道的 Tunnel ID 即可瞬间清理云端的隧道实例。

如何使用生成的 Token 启动连接器?

方式 A:命令行直接运行 (Linux / macOS / Windows)

安装 cloudflared 后,执行:

cloudflared tunnel run --token <你的_TOKEN>

方式 B:Docker 一键部署(推荐)

无需在宿主机安装 cloudflared,直接启动容器:

docker run -d --name argo-connector --restart always \
  cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <你的_TOKEN>

常用技巧与注意事项

  1. 中途退出/取消:在输入隧道名称、域名或端口时,随时输入 0 并回车,脚本会中止当前操作并安全返回主菜单。
  2. 依赖包自动补全:脚本首次运行会自动检测系统环境,若缺少 curl 或 jq,会自动通过系统包管理器补齐。
  3. DNS 重复问题:如果输入的域名之前已经手动创建过 CNAME/A 记录,脚本会提示跳过或报错,不会覆盖原记录,确保域名安全性。

脚本代码

#!/usr/bin/env bash

# ==============================================================================
# Cloudflare Argo 远程托管固定隧道管理脚本
# 功能:创建隧道(含路由与DNS绑定并输出Token)、查看列表、删除隧道
# ==============================================================================

# 配置文件区(请替换为你的真实凭证)
CF_ACCOUNT_ID="********************"
CF_API_TOKEN="********************"
CF_ZONE_ID="********************"

# 彩色文本定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

pause() {
    echo ""
    read -r -p "按回车键返回主菜单..."
}

# 依赖检查与安装
check_env() {
    if [[ "$CF_ACCOUNT_ID" == *"你的"* || "$CF_API_TOKEN" == *"你的"* || "$CF_ZONE_ID" == *"你的"* ]]; then
        echo -e "${RED}[X] 请先在脚本顶部配置好你的 CF_ACCOUNT_ID、CF_API_TOKEN 和 CF_ZONE_ID!${NC}"
        exit 1
    fi

    if ! command -v jq &> /dev/null || ! command -v curl &> /dev/null; then
        echo -e "${BLUE}--> 正在自动安装基础依赖 (curl, jq)...${NC}"
        if command -v pacman &> /dev/null; then
            sudo pacman -Sy --noconfirm jq curl
        elif command -v apt &> /dev/null; then
            sudo apt update && sudo apt install -y jq curl
        elif command -v yum &> /dev/null; then
            sudo yum install -y jq curl
        fi
    fi
}

# 1. 查看所有 Argo 隧道
list_tunnels() {
    echo -e "\n${CYAN}====== 当前 Argo 固定隧道列表 ======${NC}"
    RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel?is_deleted=false" \
        -H "Authorization: Bearer ${CF_API_TOKEN}")

    SUCCESS=$(echo "$RESPONSE" | jq -r '.success // false')
    if [ "$SUCCESS" != "true" ]; then
        echo -e "${RED}[X] 获取列表失败:${NC}"
        echo "$RESPONSE" | jq '.errors'
        pause
        return
    fi

    TOTAL=$(echo "$RESPONSE" | jq '.result | length')
    if [ "$TOTAL" -eq 0 ]; then
        echo -e "${YELLOW}当前没有活跃的隧道。${NC}"
    else
        echo "$RESPONSE" | jq -r '.result[] | "ID: \(.id) | 名称: \(.name) | 状态: \(.status)"'
    fi
    pause
}

# 2. 一键创建 Argo 固定隧道 (配置路由 + DNS + 获取 Token)
create_argo_tunnel() {
    echo -e "\n${CYAN}====== 创建 Argo 固定隧道 ======${NC}"
    
    read -r -p "1. 请输入隧道名称 (输入 0 返回): " TUNNEL_NAME
    if [[ "$TUNNEL_NAME" == "0" || -z "$TUNNEL_NAME" ]]; then return; fi

    read -r -p "2. 请输入绑定的完整域名 (例如 app.yourdomain.com, 输入 0 返回): " FULL_DOMAIN
    if [[ "$FULL_DOMAIN" == "0" || -z "$FULL_DOMAIN" ]]; then return; fi

    read -r -p "3. 请输入本地服务地址 (默认 http://localhost:8001, 输入 0 返回): " LOCAL_SERVICE
    if [ "$LOCAL_SERVICE" == "0" ]; then return; fi
    LOCAL_SERVICE=${LOCAL_SERVICE:-"http://localhost:8001"}

    echo -e "\n${BLUE}--> [1/4] 正在云端创建远程托管隧道 [$TUNNEL_NAME]...${NC}"
    CREATE_RESP=$(curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel" \
        -H "Authorization: Bearer ${CF_API_TOKEN}" \
        -H "Content-Type: application/json" \
        --data "{\"name\":\"${TUNNEL_NAME}\",\"config_src\":\"cloudflare\"}")

    if [ "$(echo "$CREATE_RESP" | jq -r '.success // false')" != "true" ]; then
        echo -e "${RED}[X] 隧道创建失败:${NC}"
        echo "$CREATE_RESP" | jq '.errors'
        pause
        return
    fi

    TUNNEL_ID=$(echo "$CREATE_RESP" | jq -r '.result.id')
    echo -e "${GREEN}[✔] 隧道创建成功!Tunnel ID: ${TUNNEL_ID}${NC}"

    # 配置应用路由规则 (Ingress Rules)
    echo -e "${BLUE}--> [2/4] 正在配置云端应用程序路由 ($FULL_DOMAIN -> $LOCAL_SERVICE)...${NC}"
    ROUTE_DATA=$(jq -n \
        --arg hostname "$FULL_DOMAIN" \
        --arg service "$LOCAL_SERVICE" \
        '{
            config: {
                ingress: [
                    {hostname: $hostname, service: $service},
                    {service: "http_status:404"}
                ]
            }
        }')

    ROUTE_RESP=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \
        -H "Authorization: Bearer ${CF_API_TOKEN}" \
        -H "Content-Type: application/json" \
        --data "$ROUTE_DATA")

    if [ "$(echo "$ROUTE_RESP" | jq -r '.success // false')" != "true" ]; then
        echo -e "${RED}[X] 云端路由设置失败:${NC}"
        echo "$ROUTE_RESP" | jq '.errors'
    else
        echo -e "${GREEN}[✔] 云端路由规则配置完毕!${NC}"
    fi

    # 绑定 DNS 解析
    echo -e "${BLUE}--> [3/4] 正在添加 DNS CNAME 记录...${NC}"
    DNS_RESP=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records" \
        -H "Authorization: Bearer ${CF_API_TOKEN}" \
        -H "Content-Type: application/json" \
        --data "{\"type\":\"CNAME\",\"name\":\"${FULL_DOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargot.com\",\"proxied\":true}")

    if [ "$(echo "$DNS_RESP" | jq -r '.success // false')" != "true" ]; then
        echo -e "${YELLOW}[!] DNS 绑定未成功 (可能域名已存在解析),请手动确认。${NC}"
    else
        echo -e "${GREEN}[✔] DNS CNAME 绑定成功!${NC}"
    fi

    # 获取连接器 Token
    echo -e "${BLUE}--> [4/4] 正在生成连接器 Token...${NC}"
    TOKEN_RESP=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/token" \
        -H "Authorization: Bearer ${CF_API_TOKEN}")

    TOKEN=$(echo "$TOKEN_RESP" | jq -r '.result // empty')

    if [ -n "$TOKEN" ]; then
        echo -e "\n${GREEN}=================== 创建完成 ===================${NC}"
        echo -e "${BLUE}域名:${NC} ${FULL_DOMAIN} -> ${LOCAL_SERVICE}"
        echo -e "${BLUE}Tunnel ID:${NC} ${TUNNEL_ID}"
        echo -e "${BLUE}连接器 Token:${NC}"
        echo -e "${YELLOW}${TOKEN}${NC}"
        echo -e "${GREEN}================================================${NC}"
        echo -e "${CYAN}你在任意客户端只需运行以下命令即可上线该连接器:${NC}"
        echo -e "cloudflared tunnel run --token ${TOKEN}"
    else
        echo -e "${RED}[X] 获取 Token 失败。${NC}"
    fi
    pause
}

# 3. 删除隧道
delete_argo_tunnel() {
    echo -e "\n${CYAN}====== 删除 Argo 隧道 ======${NC}"
    read -r -p "请输入要删除的 Tunnel ID (输入 0 返回): " TUNNEL_ID
    if [[ "$TUNNEL_ID" == "0" || -z "$TUNNEL_ID" ]]; then return; fi

    echo -e "${BLUE}--> 正在删除云端隧道...${NC}"
    RESPONSE=$(curl -s -X DELETE "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}" \
        -H "Authorization: Bearer ${CF_API_TOKEN}")

    SUCCESS=$(echo "$RESPONSE" | jq -r '.success // false')
    if [ "$SUCCESS" == "true" ]; then
        echo -e "${GREEN}[✔] 隧道删除成功!${NC}"
    else
        echo -e "${RED}[X] 删除失败:${NC}"
        echo "$RESPONSE" | jq '.errors'
    fi
    pause
}

# 主循环
main_menu() {
    check_env

    while true; do
        clear
        echo -e "${CYAN}====================================================${NC}"
        echo -e "${CYAN}         Cloudflare Argo 固定隧道管理               ${NC}"
        echo -e "${CYAN}====================================================${NC}"
        echo -e " 1) 查看所有固定隧道"
        echo -e " 2) 创建固定隧道 (包含域名配置与 Token 生成)"
        echo -e " 3) 删除固定隧道"
        echo -e " 0) 退出"
        echo -e "${CYAN}----------------------------------------------------${NC}"
        read -r -p "请选择操作 [0-3]: " CHOICE

        case "$CHOICE" in
            1) list_tunnels ;;
            2) create_argo_tunnel ;;
            3) delete_argo_tunnel ;;
            0) echo -e "${GREEN}退出程序。${NC}"; exit 0 ;;
            *) echo -e "${RED}无效选项!${NC}"; pause ;;
        esac
    done
}

main_menu
本文由 BOSH 的博客助手 HerMes 整理 🚀

原文链接:[本地上传]