-
-
Save cornradio/c3d59c9ccf2ccbbdb2d1114cc85f589f to your computer and use it in GitHub Desktop.
cftunnel_manager.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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