Skip to content

Instantly share code, notes, and snippets.

@cornradio
Created July 9, 2026 10:03
Show Gist options
  • Select an option

  • Save cornradio/c3d59c9ccf2ccbbdb2d1114cc85f589f to your computer and use it in GitHub Desktop.

Select an option

Save cornradio/c3d59c9ccf2ccbbdb2d1114cc85f589f to your computer and use it in GitHub Desktop.
cftunnel_manager.sh
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;36m'
NC='\033[0m'
# 检查是否为 Root 运行
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}错误: 请以 root 用户运行此脚本!${NC}"
exit 1
fi
# 检查当前 cloudflared 状态
get_status() {
if [ -f "/usr/local/bin/cloudflared" ]; then
if systemctl is-active --quiet cloudflared 2>/dev/null; then
echo -e "${GREEN}运行中 (Active)${NC}"
else
if [ -f "/etc/systemd/system/cloudflared.service" ]; then
echo -e "${YELLOW}已安装服务但未运行 (Inactive)${NC}"
else
echo -e "${RED}已安装主程序 (在 /usr/local/bin),但未注册系统服务${NC}"
fi
fi
else
echo -e "${RED}未安装${NC}"
fi
}
# 基础环境检查
check_env() {
if [ ! -f "/usr/local/bin/cloudflared" ]; then
if [ -f "./cloudflared" ]; then
mv ./cloudflared /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
echo -e "${GREEN}主程序已移动到系统目录 /usr/local/bin${NC}"
else
echo -e "${RED}未检测到 cloudflared 二进制程序!${NC}"
echo -e "请先将下载的 'cloudflared' 文件上传到当前目录 $(pwd) 下。"
read -n 1 -s -r -p "按任意键返回..."
return 1
fi
fi
# 检查登录状态
if [ ! -f "/root/.cloudflared/cert.pem" ]; then
echo -e "\n${YELLOW}正在请求 Cloudflare 授权登录...${NC}"
echo -e "请复制下方出现的 URL 到浏览器打开,登录并选择你要使用的域名进行授权:\n"
cloudflared tunnel login
if [ $? -ne 0 ]; then
echo -e "${RED}登录失败!${NC}"
read -n 1 -s -r -p "按任意键返回..."
return 1
fi
echo -e "${GREEN}授权成功!${NC}"
fi
return 0
}
# 重新生成系统服务
rebuild_service() {
local tunnel_id=$1
echo -e "\n${YELLOW}正在配置后台系统服务...${NC}"
cloudflared service uninstall 2>/dev/null
rm -f /etc/systemd/system/cloudflared.service
# 安装新服务
cloudflared service install 2>/dev/null
# 如果官方安装失败,手动强制写入
if [ ! -f "/etc/systemd/system/cloudflared.service" ]; then
cat <<EOF > /etc/systemd/system/cloudflared.service
[Unit]
Description=cloudflare tunnel
After=network.target
[Service]
TimeoutStartSec=0
Type=simple
User=root
ExecStart=/usr/local/bin/cloudflared --config /etc/cloudflared/config.yml --origintunneld tunnel run
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
fi
systemctl daemon-reload
systemctl enable cloudflared
systemctl restart cloudflared
if systemctl is-active --quiet cloudflared; then
echo -e "${GREEN}服务已成功在后台运行!${NC}"
else
echo -e "${RED}[警告] 服务启动失败,请选择菜单 8 查看运行日志。${NC}"
fi
}
# 1. 创建全新 Tunnel 并绑定
create_new_tunnel() {
clear
echo -e "${BLUE}=== 选项 1: 全新创建 Tunnel 并绑定 ===${NC}"
echo -e "${YELLOW}(在任何输入提示中输入 'q' 可退出返回主菜单)${NC}\n"
check_env || return
# 输入完整域名
read -p "请输入你要绑定的完整域名 (例如: nas.yourdomain.com): " full_domain
if [ -z "$full_domain" ] || [ "$full_domain" = "q" ] || [ "$full_domain" = "Q" ]; then return; fi
# 自动切出子域名作为 Tunnel 名字
tunnel_name=$(echo "$full_domain" | cut -d'.' -f1)
echo -e "系统将自动将新 Tunnel 命名为: ${GREEN}$tunnel_name${NC}"
read -p "请输入本地服务端口 (默认 http://localhost:8080): " local_service
if [ "$local_service" = "q" ] || [ "$local_service" = "Q" ]; then return; fi
[ -z "$local_service" ] && local_service="http://localhost:8080"
# 创建
echo "正在向 Cloudflare 申请创建 Tunnel..."
create_output=$(cloudflared tunnel create "$tunnel_name" 2>&1)
tunnel_id=$(echo "$create_output" | grep -oE "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" | head -n 1 | tr -d '\r' | tr -d '\n')
if [ -z "$tunnel_id" ]; then
echo -e "${RED}创建失败,可能名字重复。报错:${NC}"
echo "$create_output"
read -n 1 -s -r -p "按任意键返回..."
return
fi
# 写入全新配置文件
mkdir -p /etc/cloudflared
cat <<EOF > /etc/cloudflared/config.yml
tunnel: $tunnel_id
credentials-file: /root/.cloudflared/$tunnel_id.json
ingress:
- hostname: $full_domain
service: $local_service
- service: http_status:404
EOF
# 配置 DNS
echo "正在配置云端 DNS 解析..."
cloudflared tunnel route dns "$tunnel_name" "$full_domain"
# 重建服务
rebuild_service "$tunnel_id"
read -n 1 -s -r -p "配置完成!按任意键返回..."
}
# 2. 接管/管理已有的 Tunnel
manage_existing_tunnels() {
clear
echo -e "${BLUE}=== 选项 2: 管理/接管本地已有 Tunnel ===${NC}"
echo -e "${YELLOW}(在任何输入提示中输入 'q' 可退出返回主菜单)${NC}\n"
check_env || return
echo -e "${YELLOW}正在获取您账号下的 Tunnel 列表...${NC}"
tunnel_list=$(cloudflared tunnel list 2>&1)
# 检查是否获取成功
if echo "$tunnel_list" | grep -q "error"; then
echo -e "${RED}获取列表失败!${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo "--------------------------------------------------"
echo "$tunnel_list"
echo "--------------------------------------------------"
read -p "请输入你想接管/使用的 Tunnel ID 或 名字: " target_tunnel
if [ -z "$target_tunnel" ] || [ "$target_tunnel" = "q" ] || [ "$target_tunnel" = "Q" ]; then return; fi
tunnel_match=$(echo "$tunnel_list" | grep "$target_tunnel" | head -n 1)
tunnel_id=$(echo "$tunnel_match" | awk '{print $1}' | tr -d '\r' | tr -d '\n')
tunnel_name=$(echo "$tunnel_match" | awk '{print $2}' | tr -d '\r' | tr -d '\n')
if [ -z "$tunnel_id" ] || [[ ! "$tunnel_id" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then
echo -e "${RED}未找到对应的合法 Tunnel ID。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo -e "已选定 Tunnel: ${GREEN}$tunnel_name${NC} ($tunnel_id)"
echo "----------------------------------------------"
echo -e " 1) ${YELLOW}覆盖并重新配置该 Tunnel${NC} (清空所有,只留一个新域名)"
echo -e " 2) ${GREEN}追加新域名${NC} (多域名共用一个 Tunnel)"
echo -e " 3) ${RED}删除其中某个域名绑定${NC} (下线特定域名并清理 DNS)"
echo -e " q) ${NC}返回主菜单"
echo "----------------------------------------------"
read -p "请选择操作 [1-3, q]: " op_choice
if [ "$op_choice" = "q" ] || [ "$op_choice" = "Q" ]; then return; fi
config_file="/etc/cloudflared/config.yml"
# 选项 3:删除特定的域名绑定
if [ "$op_choice" = "3" ]; then
if [ ! -f "$config_file" ]; then
echo -e "${RED}未在本地找到配置文件 ($config_file),无法解析已绑定的域名。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo -e "\n当前已绑定的域名列表:"
domains=$(grep "hostname:" "$config_file" | awk '{print $3}')
if [ -z "$domains" ]; then
echo -e "${YELLOW}配置文件中没有找到任何绑定的域名。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo "----------------------------------------------"
echo "$domains" | cat -n
echo "----------------------------------------------"
read -p "请输入你想删除的域名序号或直接输入域名 (输入 q 退出): " del_input
if [ "$del_input" = "q" ] || [ "$del_input" = "Q" ]; then return; fi
if [[ "$del_input" =~ ^[0-9]+$ ]]; then
del_domain=$(echo "$domains" | sed -n "${del_input}p")
else
del_domain=$del_input
fi
if [ -z "$del_domain" ] || ! echo "$domains" | grep -q "$del_domain"; then
echo -e "${RED}未找到该域名,操作取消。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
sed -i "/hostname: $del_domain/{N;d;}" "$config_file"
echo -e "${GREEN}本地配置文件已移除域名: $del_domain${NC}"
echo "正在注销云端 CNAME DNS 解析..."
cloudflared tunnel route dns -d "$del_domain" 2>/dev/null
rebuild_service "$tunnel_id"
read -n 1 -s -r -p "域名 $del_domain 已彻底移除!按任意键返回..."
return
fi
# 选项 1 & 2 的逻辑
read -p "请输入绑定的完整域名 (例如: app.yourdomain.com): " full_domain
if [ "$full_domain" = "q" ] || [ "$full_domain" = "Q" ]; then return; fi
read -p "请输入对应的本地服务 (例如: http://localhost:8080): " local_service
if [ "$local_service" = "q" ] || [ "$local_service" = "Q" ]; then return; fi
if [ -z "$full_domain" ] || [ -z "$local_service" ]; then
echo -e "${RED}输入不能为空。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
mkdir -p /etc/cloudflared
if [ "$op_choice" = "1" ] || [ ! -f "$config_file" ]; then
# 覆写配置
cat <<EOF > "$config_file"
tunnel: $tunnel_id
credentials-file: /root/.cloudflared/$tunnel_id.json
ingress:
- hostname: $full_domain
service: $local_service
- service: http_status:404
EOF
else
# 追加配置
sed -i '/http_status:404/d' "$config_file"
sed -i '/- service: http_status:404/d' "$config_file"
cat <<EOF >> "$config_file"
- hostname: $full_domain
service: $local_service
- service: http_status:404
EOF
fi
echo "正在将域名 DNS CNAME 绑定到此 Tunnel..."
cloudflared tunnel route dns "$tunnel_name" "$full_domain"
rebuild_service "$tunnel_id"
read -n 1 -s -r -p "配置完成!按任意键返回..."
}
# 3. 删除/清理整个 Tunnel 实体 (流程调整:先选择 ID 后选操作)
cleanup_unused_tunnels() {
clear
echo -e "${RED}=== 选项 3: 删除/清理整个 Tunnel 实体 ===${NC}"
echo -e "${YELLOW}(在任何输入提示中输入 'q' 可退出返回主菜单)${NC}\n"
check_env || return
echo -e "${YELLOW}正在获取您账号下的所有 Tunnel 列表...${NC}"
tunnel_list=$(cloudflared tunnel list 2>&1)
if echo "$tunnel_list" | grep -q "error"; then
echo -e "${RED}获取列表失败,请检查网络或重新运行!${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo "--------------------------------------------------------------------------------"
echo "$tunnel_list"
echo "--------------------------------------------------------------------------------"
# 调整流程:先挑出目标 Tunnel ID
read -p "请输入你想操作/删除的 Tunnel 名字或 ID: " target_del
if [ -z "$target_del" ] || [ "$target_del" = "q" ] || [ "$target_del" = "Q" ]; then return; fi
# 在列表中寻找该 Tunnel
tunnel_match=$(echo "$tunnel_list" | grep "$target_del" | head -n 1)
del_id=$(echo "$tunnel_match" | awk '{print $1}' | tr -d '\r' | tr -d '\n')
del_name=$(echo "$tunnel_match" | awk '{print $2}' | tr -d '\r' | tr -d '\n')
del_connections=$(echo "$tunnel_match" | awk '{print $4}' | tr -d '\r' | tr -d '\n')
if [ -z "$del_id" ]; then
echo -e "${RED}未在列表中匹配到对应的 Tunnel。${NC}"
read -n 1 -s -r -p "按任意键返回..."
return
fi
echo -e "\n已选定目标 Tunnel: ${YELLOW}$del_name${NC} ($del_id),当前活动连接数: $del_connections"
echo "--------------------------------------------------------------------------------"
echo -e "【选择操作】"
echo -e " 1) ${YELLOW}智能分析删除${NC} (只有当前连接数为 0 时才允许安全删除)"
echo -e " 2) ${RED}手动强制抹去${NC} (无视连接状态,直接从云端强制干掉)"
echo -e " q) ${NC}放弃并返回主菜单"
echo "--------------------------------------------------------------------------------"
read -p "请选择操作 [1-2, q]: " clean_type
if [ "$clean_type" = "q" ] || [ "$clean_type" = "Q" ]; then return; fi
if [ "$clean_type" = "1" ]; then
# 智能安全删除
if [ "$del_connections" != "0" ]; then
echo -e "${RED}安全拦截: 该 Tunnel 此时仍有 $del_connections 个活跃连接,说明有设备正在使用它,无法安全删除!${NC}"
echo -e "提示:若要强行下线,请重新进入并选择 2) 手动强制抹去。"
read -n 1 -s -r -p "按任意键返回..."
return
fi
fi
# 执行删除确认
echo -e "${RED}\n⚠️ 警告: 您正在准备从云端彻底销毁整个 Tunnel: $del_name!${NC}"
echo -e "此操作不可逆,且会导致与其关联的所有外部域名彻底瘫痪。"
read -p "真的要彻底删除该 Tunnel 吗?(y/n): " confirm_del
if [ "$confirm_del" = "y" ] || [ "$confirm_del" = "Y" ]; then
echo "正在向云端下发销毁指令..."
cloudflared tunnel delete -f "$del_id"
if [ $? -eq 0 ]; then
echo -e "${GREEN}整个 Tunnel 实体已成功从云端抹除!${NC}"
else
echo -e "${RED}删除失败,可能网络超时或该实体在后台有锁。${NC}"
fi
else
echo -e "${YELLOW}操作已取消。${NC}"
fi
read -n 1 -s -r -p "按任意键返回..."
}
# 4. 彻底卸载
uninstall_cloudflared() {
clear
echo -e "${RED}=== 卸载 Cloudflared ===${NC}"
read -p "确定要彻底卸载并清理所有配置吗?(y/n): " confirm
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
systemctl stop cloudflared 2>/dev/null
systemctl disable cloudflared 2>/dev/null
/usr/local/bin/cloudflared service uninstall 2>/dev/null
rm -f /etc/systemd/system/cloudflared.service
systemctl daemon-reload
rm -rf /etc/cloudflared
rm -rf /root/.cloudflared
rm -f /usr/local/bin/cloudflared
echo -e "${GREEN}卸载及残留配置文件清理完成!${NC}"
fi
read -n 1 -s -r -p "按任意键返回..."
}
# 服务控制 (支持命令输出与原生 Status)
control_service() {
action=$1
if [ ! -f "/usr/local/bin/cloudflared" ]; then
echo -e "${RED}错误: 请先安装并配置服务!${NC}"
sleep 1.5
return
fi
clear
if [ "$action" = "status" ]; then
echo -e "${BLUE}=== 执行系统服务状态查询 ===${NC}"
echo -e "执行命令: ${YELLOW}systemctl status cloudflared${NC}\n"
echo "--------------------------------------------------------------------------------"
systemctl status cloudflared
echo "--------------------------------------------------------------------------------"
read -n 1 -s -r -p "按任意键返回主菜单..."
else
echo -e "${BLUE}=== 执行系统服务变更 ===${NC}"
echo -e "执行命令: ${YELLOW}systemctl $action cloudflared${NC}\n"
systemctl $action cloudflared 2>/dev/null
sleep 1
if systemctl is-active --quiet cloudflared; then
echo -e "结果: ${GREEN}服务正在运行中 (Active)${NC}"
else
echo -e "结果: ${YELLOW}服务目前已停止 (Inactive)${NC}"
fi
sleep 1.5
fi
}
# 主循环菜单
while true; do
clear
echo -e "${BLUE}==============================================${NC}"
echo -e "${BLUE} Cloudflare Tunnel 极简本地管理脚本 v3.5 ${NC}"
echo -e "${BLUE}==============================================${NC}"
echo -e "当前状态: $(get_status)"
echo "----------------------------------------------"
echo -e " 1. ${GREEN}全新创建 Tunnel 并绑定域名${NC}"
echo -e " 2. ${BLUE}管理已有的 Tunnel (追加、覆盖、或删除单个域名绑定)${NC}"
echo -e " 3. ${RED}删除整个 Tunnel 实体 (先选目标,再选清理模式)${NC}"
echo -e " 4. 彻底卸载并清理配置"
echo "----------------------------------------------"
echo -e " 5. 启动 Tunnel 服务 | 6. 停止 Tunnel 服务"
echo -e " 7. ${YELLOW}查看服务详细状态 (Status)${NC}"
echo -e " 8. 查看实时服务日志 | 0. 退出脚本"
echo "----------------------------------------------"
read -p "请输入选项 [0-8]: " choice
case $choice in
1) create_new_tunnel ;;
2) manage_existing_tunnels ;;
3) cleanup_unused_tunnels ;;
4) uninstall_cloudflared ;;
5) control_service "start" ;;
6) control_service "stop" ;;
7) control_service "status" ;;
8) clear; echo -e "执行命令: ${YELLOW}journalctl -u cloudflared -n 50 -f${NC}\n"; journalctl -u cloudflared -n 50 -f ;;
0|q|Q) exit 0 ;;
*) echo -e "${RED}无效选项!${NC}" && sleep 1 ;;
esac
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment