tuic_docker/bin/update_port.sh
2026-01-23 17:55:54 +08:00

170 lines
6.0 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
# 查找随机空闲端口
# 用途:
# 在指定端口范围内随机选择一个当前未被监听TCP/UDP的端口
# 参数:
# $1: 起始端口(默认 10001
# $2: 结束端口(默认 65535
# $3: 最大随机尝试次数(默认 100
find_free_port() {
local start="${1:-10001}"
local max="${2:-65535}"
local attempts="${3:-100}"
# ------------------------------------------------------------
# 收集当前正在监听的端口TCP + UDP
# 优先使用 ss其次 netstat
# ------------------------------------------------------------
local used_ports raw
if command -v ss >/dev/null 2>&1; then
# ss 输出示例:
# LISTEN 0 128 0.0.0.0:22
raw=$(ss -lntu 2>/dev/null || true)
used_ports=$(printf "%s\n" "$raw" \
| awk '{print $5}' \
| sed -E 's/.*[:]//g')
elif command -v netstat >/dev/null 2>&1; then
# netstat 输出示例:
# tcp 0 0 0.0.0.0:22
raw=$(netstat -lntu 2>/dev/null || true)
used_ports=$(printf "%s\n" "$raw" \
| awk '{print $4}' \
| sed -E 's/.*[:]//g')
else
echo "Error: ss or netstat is required to check listening ports." >&2
return 2
fi
# ------------------------------------------------------------
# 将已占用端口存入关联数组,便于 O(1) 判断
# 需要 bash 4+
# ------------------------------------------------------------
declare -A used_map
local p
for p in $used_ports; do
# 过滤非数字字段(如 *、:::
if [[ $p =~ ^[0-9]+$ ]]; then
used_map["$p"]=1
fi
done
# ------------------------------------------------------------
# 随机尝试若干次
# 每次随机生成一个端口,只要未被监听就立即返回
# ------------------------------------------------------------
local port i
for ((i=0; i<attempts; i++)); do
# 在 [start, max] 范围内生成随机端口
port=$(( RANDOM % (max - start + 1) + start ))
# 如果端口不在已监听表中,则认为可用
if [[ -z "${used_map[$port]}" ]]; then
echo "$port"
return 0
fi
done
# ------------------------------------------------------------
# 多次随机尝试后仍未找到可用端口
# 通常意味着端口范围过小或已被大量占用
# ------------------------------------------------------------
echo "Error: no free port found after $attempts random attempts." >&2
return 1
}
# ==========================================
# 函数名: manage_port
# 功能: 开放端口和关闭端口
# 参数1: allow 或 deny (操作类型)
# 参数2: 端口号
# --- 使用示例 ---
# 开启端口 8888
# manage_port allow 8888
# 关闭端口 8888
# manage_port deny 8888
# ==========================================
manage_port() {
local ACTION=$1
local PORT=$2
if [[ -z "$PORT" ]]; then
echo "错误: 未提供端口号"
return 1
fi
# 统一转换为小写,增强鲁棒性
ACTION=$(echo "$ACTION" | tr '[:upper:]' '[:lower:]')
echo "--- 正在对端口 $PORT 执行 $ACTION 操作 ---"
# 1. 防火墙 (Firewall) 逻辑处理
if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld; then
# CentOS/RHEL/Fedora (firewalld)
if [ "$ACTION" == "allow" ]; then
sudo firewall-cmd --zone=public --add-port=${PORT}/tcp --permanent
sudo firewall-cmd --zone=public --add-port=${PORT}/udp --permanent
elif [ "$ACTION" == "deny" ]; then
sudo firewall-cmd --zone=public --remove-port=${PORT}/tcp --permanent
sudo firewall-cmd --zone=public --remove-port=${PORT}/udp --permanent
fi
sudo firewall-cmd --reload
echo "[OK] firewalld 规则已更新 ($ACTION)"
elif command -v ufw >/dev/null 2>&1 && systemctl is-active --quiet ufw; then
# Ubuntu/Debian (ufw)
if [ "$ACTION" == "allow" ]; then
sudo ufw allow ${PORT}/tcp
sudo ufw allow ${PORT}/udp
elif [ "$ACTION" == "deny" ]; then
sudo ufw delete allow ${PORT}/tcp
sudo ufw delete allow ${PORT}/udp
fi
echo "[OK] ufw 规则已更新 ($ACTION)"
else
# 兜底方案 (iptables)
# allow 使用 -I (Insert) 插入到规则首行deny 使用 -D (Delete)
local FLAG=$([ "$ACTION" == "allow" ] && echo "-I" || echo "-D")
sudo iptables $FLAG INPUT -p tcp --dport ${PORT} -j ACCEPT 2>/dev/null
sudo iptables $FLAG INPUT -p udp --dport ${PORT} -j ACCEPT 2>/dev/null
echo "[OK] iptables 规则已执行 ($ACTION)"
fi
# 2. SELinux 逻辑处理
if command -v getenforce >/dev/null 2>&1; then
local SELINUX_STATUS=$(getenforce)
if [ "$SELINUX_STATUS" == "Enforcing" ]; then
if command -v semanage >/dev/null 2>&1; then
if [ "$ACTION" == "allow" ]; then
# 尝试添加,若存在则尝试修改
sudo semanage port -a -t http_port_t -p tcp ${PORT} 2>/dev/null || \
sudo semanage port -m -t http_port_t -p tcp ${PORT}
elif [ "$ACTION" == "deny" ]; then
sudo semanage port -d -t http_port_t -p tcp ${PORT} 2>/dev/null
fi
echo "[OK] SELinux 端口权限已更新 ($ACTION)"
else
# 如果没装 semanage在 allow 时开启全局布尔值deny 时通常保持不变以防影响其他业务
if [ "$ACTION" == "allow" ]; then
echo "[!] 警告: 未找到 semanage尝试开启全局网络连接开关..."
sudo setsebool -P httpd_can_network_connect 1
fi
fi
fi
fi
}
update_port(){
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 脚本文件夹绝对路径
local config_dir="$script_dir/../config"
local port=$(find_free_port)
modify_json_file "$config_dir/config.json" ".inbounds[0].listen_port" "$port"
manage_port allow "$port"
echo "设置端口成功"
}