update
This commit is contained in:
31
proxy/README.md
Normal file
31
proxy/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
#### 卸载xray
|
||||
```
|
||||
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ remove --purge
|
||||
```
|
||||
|
||||
- 一键安装L2TP
|
||||
```
|
||||
bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/l2tp.sh)
|
||||
```
|
||||
|
||||
- 站群多IP源进源出节点脚本,支持sk5和vless+tcp协议
|
||||
```
|
||||
bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/zhanqun.sh)
|
||||
```
|
||||
|
||||
- 快速批量搭建二级代理脚本,vmess入站sk5出站
|
||||
```
|
||||
bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/vmess-sk5.sh)
|
||||
```
|
||||
|
||||
|
||||
- 站群多IP源进源出节点脚本sk5协议
|
||||
```
|
||||
bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/duosocks.sh)
|
||||
```
|
||||
|
||||
|
||||
- 站群多IP源进源出节点脚本,vmess+ws协议
|
||||
```
|
||||
bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/duovmess.sh)
|
||||
```
|
||||
556
proxy/duosk5-ss2022.sh
Normal file
556
proxy/duosk5-ss2022.sh
Normal file
@@ -0,0 +1,556 @@
|
||||
#!/bin/bash
|
||||
# 批量搭建ss2022入站到sk5出站代理
|
||||
# 读取sk5文件实现批量导入出站
|
||||
# 作者sky22333
|
||||
|
||||
red='\e[31m'
|
||||
yellow='\e[33m'
|
||||
green='\e[32m'
|
||||
none='\e[0m'
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
default_config='
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 9999,
|
||||
"protocol": "shadowsocks",
|
||||
"settings": {
|
||||
"method": "2022-blake3-aes-256-gcm",
|
||||
"password": "75ENbpfSCyzUdZnLRjVGexaQxVPdCLw5T4RXbTGRQ/Q=",
|
||||
"network": "tcp,udp"
|
||||
},
|
||||
"tag": "inbound0"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.2",
|
||||
"port": 2222,
|
||||
"users": [
|
||||
{
|
||||
"user": "admin123",
|
||||
"pass": "admin333"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": "outbound0"
|
||||
}
|
||||
],
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["inbound0"],
|
||||
"outboundTag": "outbound0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
check_and_install_curl() {
|
||||
if ! type curl &>/dev/null; then
|
||||
echo -e "${yellow}正在安装curl...${none}"
|
||||
apt update && apt install -yq curl
|
||||
fi
|
||||
}
|
||||
|
||||
check_and_install_jq() {
|
||||
if ! type jq &>/dev/null; then
|
||||
echo -e "${yellow}正在安装jq...${none}"
|
||||
apt update && apt install -yq jq
|
||||
fi
|
||||
}
|
||||
|
||||
check_and_install_openssl() {
|
||||
if ! type openssl &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 openssl...${none}"
|
||||
apt update && apt install -yq openssl
|
||||
fi
|
||||
}
|
||||
|
||||
check_and_install_xray() {
|
||||
if ! type xray &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 xray...${none}"
|
||||
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.13
|
||||
fi
|
||||
}
|
||||
|
||||
check_existing_inbound_config() {
|
||||
if grep -q '"tag":' "$config_file"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
create_default_config() {
|
||||
if ! check_existing_inbound_config; then
|
||||
echo "$default_config" > "$config_file"
|
||||
echo -e "${green}已创建默认配置文件。${none}"
|
||||
else
|
||||
echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}"
|
||||
fi
|
||||
}
|
||||
|
||||
get_local_ip() {
|
||||
local ip=$(curl -s http://ipinfo.io/ip)
|
||||
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$ip"
|
||||
else
|
||||
echo "无法自动获取公网IP地址,请手动输入。"
|
||||
read -p "请输入您的公网IP地址: " manual_ip
|
||||
if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$manual_ip"
|
||||
else
|
||||
echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_ss_filename() {
|
||||
local timestamp=$(date +"%Y%m%d-%H点%M分%S秒")
|
||||
echo "/home/${timestamp}-ss.txt"
|
||||
}
|
||||
|
||||
generate_ss_link() {
|
||||
local server=$1
|
||||
local port=$2
|
||||
local method=$3
|
||||
local password=$4
|
||||
local ps=$5
|
||||
|
||||
local password_urlencoded=$(echo -n "$password" | xxd -p | tr -d '\n' | sed 's/\(..\)/%\1/g')
|
||||
|
||||
local base64_part=$(echo -n "${method}:${password}" | base64 -w 0)
|
||||
echo "ss://${base64_part}@${server}:${port}#${ps}"
|
||||
}
|
||||
|
||||
save_multiple_ss_links() {
|
||||
local local_ip=$1
|
||||
shift
|
||||
local ss_file=$(get_ss_filename)
|
||||
|
||||
> "$ss_file"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local port=$1
|
||||
local password=$2
|
||||
local method=$3
|
||||
local index=$4
|
||||
shift 4
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP"
|
||||
fi
|
||||
|
||||
local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip")
|
||||
|
||||
echo "$ss_link" >> "$ss_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将操作的所有节点保存至 $ss_file${none}"
|
||||
}
|
||||
|
||||
save_multiple_ss_links_with_ps() {
|
||||
local local_ip=$1
|
||||
shift
|
||||
local ss_file=$(get_ss_filename)
|
||||
|
||||
> "$ss_file"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local port=$1
|
||||
local password=$2
|
||||
local method=$3
|
||||
local index=$4
|
||||
local sk5_ip=$5
|
||||
shift 5
|
||||
|
||||
local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip")
|
||||
|
||||
echo "$ss_link" >> "$ss_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将操作的所有节点保存至 $ss_file${none}"
|
||||
}
|
||||
|
||||
save_all_ss_links() {
|
||||
local local_ip=$(get_local_ip)
|
||||
local ss_file=$(get_ss_filename)
|
||||
|
||||
> "$ss_file"
|
||||
|
||||
local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file")
|
||||
local length=$(jq '. | length' <<< "$config")
|
||||
|
||||
for ((i = 0; i < length; i++)); do
|
||||
local port=$(jq -r ".[$i].port" <<< "$config")
|
||||
local method=$(jq -r ".[$i].settings.method" <<< "$config")
|
||||
local password=$(jq -r ".[$i].settings.password" <<< "$config")
|
||||
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP"
|
||||
fi
|
||||
|
||||
# 生成SS链接
|
||||
local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip")
|
||||
|
||||
# 写入文件
|
||||
echo "$ss_link" >> "$ss_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将全部Shadowsocks节点保存至 $ss_file${none}"
|
||||
}
|
||||
|
||||
show_inbound_configs() {
|
||||
local local_ip=$(get_local_ip)
|
||||
|
||||
local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file")
|
||||
local outbounds=$(jq '.outbounds' "$config_file")
|
||||
echo -e "${green}入站节点配置:${none}"
|
||||
|
||||
local length=$(jq '. | length' <<< "$config")
|
||||
for ((i = 0; i < length; i++)); do
|
||||
local port=$(jq -r ".[$i].port" <<< "$config")
|
||||
local method=$(jq -r ".[$i].settings.method" <<< "$config")
|
||||
local password=$(jq -r ".[$i].settings.password" <<< "$config")
|
||||
|
||||
local node_address="$local_ip"
|
||||
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP"
|
||||
fi
|
||||
|
||||
local ss_link=$(generate_ss_link "$node_address" "$port" "$method" "$password" "$sk5_ip")
|
||||
|
||||
echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Shadowsocks 链接: ${ss_link}"
|
||||
|
||||
# 构造出站配置的标签
|
||||
local outbound_tag="outbound$port"
|
||||
|
||||
# 根据构造的标签查找对应的出站配置
|
||||
local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds")
|
||||
|
||||
if [[ ! -z $outbound_config ]]; then
|
||||
echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")"
|
||||
else
|
||||
echo -e "${red}未找到对应的出站配置。${none}"
|
||||
fi
|
||||
done
|
||||
|
||||
save_all_ss_links
|
||||
}
|
||||
|
||||
add_new_nodes() {
|
||||
local sk5_file="/home/sk5.txt"
|
||||
|
||||
# 检查sk5.txt文件是否存在
|
||||
if [ ! -f "$sk5_file" ]; then
|
||||
echo -e "${red}错误!${none} $sk5_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取sk5.txt文件中的代理信息
|
||||
local sk5_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
sk5_proxies+=("$line")
|
||||
done < "$sk5_file"
|
||||
|
||||
local proxy_count=${#sk5_proxies[@]}
|
||||
if [ $proxy_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $sk5_file 中找到有效的代理配置。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $sk5_file 读取到 $proxy_count 个代理配置。${none}"
|
||||
read -p "是否要导入全部配置?(y/n): " confirm
|
||||
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
read -p "请输入要导入的代理数量 (最大 $proxy_count): " num_to_import
|
||||
if ! [[ $num_to_import =~ ^[0-9]+$ ]] || [ $num_to_import -le 0 ] || [ $num_to_import -gt $proxy_count ]; then
|
||||
echo -e "${red}错误!${none} 输入数量无效。"
|
||||
return
|
||||
fi
|
||||
else
|
||||
num_to_import=$proxy_count
|
||||
fi
|
||||
|
||||
local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file")
|
||||
local start_port=$((max_port+1))
|
||||
local local_ip=$(get_local_ip)
|
||||
local nodes_to_save=()
|
||||
|
||||
for ((i=0; i<num_to_import; i++)); do
|
||||
local new_port=$((start_port+i))
|
||||
local new_tag="inbound$new_port"
|
||||
local new_outbound_tag="outbound$new_port"
|
||||
# 为Shadowsocks 2022生成密钥
|
||||
local new_password=$(openssl rand -base64 32)
|
||||
local method="2022-blake3-aes-256-gcm" # 使用默认的Shadowsocks 2022加密方法
|
||||
|
||||
# 解析代理信息 (格式: IP:端口:用户名:密码)
|
||||
IFS=':' read -r outbound_addr outbound_port outbound_user outbound_pass <<< "${sk5_proxies[$i]}"
|
||||
|
||||
if [[ -z "$outbound_addr" || -z "$outbound_port" || -z "$outbound_user" || -z "$outbound_pass" ]]; then
|
||||
echo -e "${red}警告:${none} 代理 #$((i+1)) 格式无效,终止脚本运行: ${sk5_proxies[$i]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${yellow}配置入站端口 $new_port 连接到代理 $outbound_addr:$outbound_port${none}"
|
||||
|
||||
# 添加Shadowsocks入站配置
|
||||
jq --argjson port "$new_port" --arg password "$new_password" --arg method "$method" --arg tag "$new_tag" '
|
||||
.inbounds += [{
|
||||
listen: "0.0.0.0",
|
||||
port: $port,
|
||||
protocol: "shadowsocks",
|
||||
settings: {
|
||||
method: $method,
|
||||
password: $password,
|
||||
network: "tcp,udp"
|
||||
},
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加出站配置
|
||||
jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" '
|
||||
.outbounds += [{
|
||||
protocol: "socks",
|
||||
settings: { servers: [{ address: $addr, port: $port | tonumber, users: [{ user: $user, pass: $pass }] }] },
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加路由规则
|
||||
jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" '
|
||||
.routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }]
|
||||
' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 保存节点信息以便后续生成SS链接
|
||||
nodes_to_save+=("$new_port" "$new_password" "$method" "$((i+1))")
|
||||
done
|
||||
|
||||
# 保存所有新添加的节点到一个文件
|
||||
save_multiple_ss_links "$local_ip" "${nodes_to_save[@]}"
|
||||
|
||||
echo -e "${green}已成功添加 $num_to_import 个节点。${none}"
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 根据xiugai.txt文件修改SOCKS5出站代理
|
||||
modify_socks5_outbound() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
# 检查xiugai.txt文件是否存在
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo -e "${red}错误!${none} $modify_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取xiugai.txt文件中的代理信息
|
||||
local modify_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
modify_proxies+=("$line")
|
||||
done < "$modify_file"
|
||||
|
||||
# 检查是否读取到代理
|
||||
local proxy_count=${#modify_proxies[@]}
|
||||
if [ $proxy_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的代理配置。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $modify_file 读取到 $proxy_count 个代理配置。${none}"
|
||||
local local_ip=$(get_local_ip)
|
||||
local nodes_to_save=()
|
||||
|
||||
# 处理每个要修改的代理
|
||||
for proxy in "${modify_proxies[@]}"; do
|
||||
IFS=':' read -r old_ip new_port new_user new_pass <<< "$proxy"
|
||||
|
||||
if [[ -z "$old_ip" || -z "$new_port" || -z "$new_user" || -z "$new_pass" ]]; then
|
||||
echo -e "${red}警告:${none} 代理格式无效,终止脚本运行: $proxy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 查找匹配的出站节点
|
||||
local outbound_config=$(jq --arg ip "$old_ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, address: .settings.servers[0].address, port: .settings.servers[0].port, user: .settings.servers[0].users[0].user, pass: .settings.servers[0].users[0].pass}' "$config_file")
|
||||
|
||||
if [[ -z "$outbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到IP地址为 $old_ip 的SOCKS5出站节点,终止脚本运行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local tag=$(echo "$outbound_config" | jq -r '.tag')
|
||||
local old_port=$(echo "$outbound_config" | jq -r '.port')
|
||||
local old_user=$(echo "$outbound_config" | jq -r '.user')
|
||||
local old_pass=$(echo "$outbound_config" | jq -r '.pass')
|
||||
|
||||
echo -e "${yellow}找到匹配的出站节点:${none} 标签=$tag, 旧IP=$old_ip, 旧端口=$old_port, 旧用户名=$old_user, 旧密码=$old_pass"
|
||||
echo -e "${green}将更新为:${none} 新IP=$old_ip, 新端口=$new_port, 新用户名=$new_user, 新密码=$new_pass"
|
||||
|
||||
# 更新SOCKS5出站配置
|
||||
local temp_file=$(mktemp)
|
||||
jq --arg tag "$tag" \
|
||||
--arg ip "$old_ip" \
|
||||
--arg port "$new_port" \
|
||||
--arg user "$new_user" \
|
||||
--arg pass "$new_pass" \
|
||||
'(.outbounds[] | select(.tag == $tag) | .settings.servers[0].address) = $ip |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].port) = ($port | tonumber) |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].user) = $user |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].pass) = $pass' \
|
||||
"$config_file" > "$temp_file"
|
||||
|
||||
if [ $? -eq 0 ] && [ -s "$temp_file" ]; then
|
||||
mv "$temp_file" "$config_file"
|
||||
echo -e "${green}成功修改SOCKS5出站节点配置!${none}"
|
||||
|
||||
# 查找对应的入站配置并保存节点信息
|
||||
local inbound_port=${tag//outbound/}
|
||||
local inbound_config=$(jq --arg port "$inbound_port" '.inbounds[] | select(.port == ($port | tonumber))' "$config_file")
|
||||
if [[ -n "$inbound_config" ]]; then
|
||||
local method=$(echo "$inbound_config" | jq -r '.settings.method')
|
||||
local password=$(echo "$inbound_config" | jq -r '.settings.password')
|
||||
local index=$(jq --arg port "$inbound_port" '.inbounds | map(select(.port != 9999)) | map(.port == ($port | tonumber)) | index(true)' "$config_file")
|
||||
|
||||
# 包含实际的SOCKS5 IP地址作为PS字段
|
||||
nodes_to_save+=("$inbound_port" "$password" "$method" "$((index+1))" "$old_ip")
|
||||
fi
|
||||
else
|
||||
echo -e "${red}更新配置失败!${none}"
|
||||
rm -f "$temp_file"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#nodes_to_save[@]} -gt 0 ]]; then
|
||||
save_multiple_ss_links_with_ps "$local_ip" "${nodes_to_save[@]}"
|
||||
fi
|
||||
|
||||
sudo chmod 755 /usr/local/etc/xray/config.json
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 根据xiugai.txt文件删除节点
|
||||
delete_nodes_by_ip() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
# 检查xiugai.txt文件是否存在
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo -e "${red}错误!${none} $modify_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取xiugai.txt文件中的代理信息
|
||||
local modify_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
# 只提取IP部分
|
||||
IFS=':' read -r ip _ <<< "$line"
|
||||
modify_proxies+=("$ip")
|
||||
done < "$modify_file"
|
||||
|
||||
# 检查是否读取到IP
|
||||
local ip_count=${#modify_proxies[@]}
|
||||
if [ $ip_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的IP地址。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $modify_file 读取到 $ip_count 个IP地址。${none}"
|
||||
|
||||
# 处理每个要删除的IP
|
||||
for ip in "${modify_proxies[@]}"; do
|
||||
# 查找匹配的出站节点
|
||||
local outbound_config=$(jq --arg ip "$ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, port: .settings.servers[0].port}' "$config_file")
|
||||
|
||||
if [[ -z "$outbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到IP地址为 $ip 的SOCKS5出站节点,终止脚本运行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local outbound_tag=$(echo "$outbound_config" | jq -r '.tag')
|
||||
|
||||
# 从outbound_tag中提取端口号(假设格式为"outbound端口号")
|
||||
local port=${outbound_tag#outbound}
|
||||
|
||||
echo -e "${yellow}找到匹配的节点:${none} 出站标签=$outbound_tag, IP=$ip, 端口=$port"
|
||||
|
||||
# 查找对应的入站配置
|
||||
local inbound_config=$(jq --arg port "$port" '.inbounds[] | select(.port == ($port | tonumber))' "$config_file")
|
||||
|
||||
if [[ -z "$inbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到对应端口 $port 的入站配置,继续删除出站配置"
|
||||
else
|
||||
local inbound_tag=$(echo "$inbound_config" | jq -r '.tag')
|
||||
echo -e "${yellow}找到对应的入站配置:${none} 标签=$inbound_tag"
|
||||
|
||||
# 删除入站配置
|
||||
jq --arg port "$port" 'del(.inbounds[] | select(.port == ($port | tonumber)))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 删除路由规则(使用实际的inbound_tag而不是构造的标签)
|
||||
jq --arg inTag "$inbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
fi
|
||||
|
||||
# 删除出站配置
|
||||
jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
echo -e "${green}已成功删除IP地址为 $ip 的节点。${none}"
|
||||
done
|
||||
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
while true; do
|
||||
echo -e "\n${green}快速批量搭建二级代理脚本-管理菜单:${none}"
|
||||
echo "1. 查看所有节点"
|
||||
echo "2. 新增Shadowsocks入站sk5出站(从/home/sk5.txt文件导入)"
|
||||
echo "3. 删除节点(根据/home/xiugai.txt文件匹配)"
|
||||
echo "4. 修改SOCKS5出站节点(根据/home/xiugai.txt文件匹配)"
|
||||
echo "5. 退出"
|
||||
read -p "请输入选项: " choice
|
||||
|
||||
case $choice in
|
||||
1) show_inbound_configs ;;
|
||||
2) add_new_nodes ;;
|
||||
3) delete_nodes_by_ip ;;
|
||||
4) modify_socks5_outbound ;;
|
||||
5) break ;;
|
||||
*) echo -e "${red}无效的选项,请重新选择。${none}" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
check_and_install_curl
|
||||
check_and_install_jq
|
||||
check_and_install_openssl
|
||||
check_and_install_xray
|
||||
create_default_config
|
||||
get_local_ip
|
||||
main_menu
|
||||
578
proxy/duosk5-vmess.sh
Normal file
578
proxy/duosk5-vmess.sh
Normal file
@@ -0,0 +1,578 @@
|
||||
#!/bin/bash
|
||||
# 批量搭建vmess+ws入站到sk5出站代理
|
||||
# 读取sk5文件实现批量导入出站
|
||||
# 作者sky22333
|
||||
|
||||
red='\e[31m'
|
||||
yellow='\e[33m'
|
||||
green='\e[32m'
|
||||
none='\e[0m'
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
default_config='
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 9999,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "wss"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "none",
|
||||
"wsSettings": {
|
||||
"path": "/wss"
|
||||
}
|
||||
},
|
||||
"tag": "inbound0"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.2",
|
||||
"port": 2222,
|
||||
"users": [
|
||||
{
|
||||
"user": "admin123",
|
||||
"pass": "admin333"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": "outbound0"
|
||||
}
|
||||
],
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["inbound0"],
|
||||
"outboundTag": "outbound0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
# 检查并安装curl
|
||||
check_and_install_curl() {
|
||||
if ! type curl &>/dev/null; then
|
||||
echo -e "${yellow}正在安装curl...${none}"
|
||||
apt update && apt install -yq curl
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装jq
|
||||
check_and_install_jq() {
|
||||
if ! type jq &>/dev/null; then
|
||||
echo -e "${yellow}正在安装jq...${none}"
|
||||
apt update && apt install -yq jq
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装uuid-runtime
|
||||
check_and_install_uuid_runtime() {
|
||||
if ! type uuidgen &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 uuid-runtime...${none}"
|
||||
apt update && apt install -yq uuid-runtime
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装xray
|
||||
check_and_install_xray() {
|
||||
if ! type xray &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 xray...正在启用BBR...${none}"
|
||||
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已存在入站配置
|
||||
check_existing_inbound_config() {
|
||||
if grep -q '"tag":' "$config_file"; then
|
||||
return 0 # 已存在入站配置
|
||||
else
|
||||
return 1 # 不存在入站配置
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建默认配置文件
|
||||
create_default_config() {
|
||||
if ! check_existing_inbound_config; then
|
||||
echo "$default_config" > "$config_file"
|
||||
echo -e "${green}已创建默认配置文件。${none}"
|
||||
else
|
||||
echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取本机公网 IP
|
||||
get_local_ip() {
|
||||
local ip=$(curl -s http://ipinfo.io/ip)
|
||||
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$ip"
|
||||
else
|
||||
echo "无法自动获取公网IP地址,请手动输入。"
|
||||
read -p "请输入您的公网IP地址: " manual_ip
|
||||
if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$manual_ip"
|
||||
else
|
||||
echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成带时间戳的vmess文件名(精确到秒)
|
||||
get_vmess_filename() {
|
||||
local timestamp=$(date +"%Y%m%d-%H点%M分%S秒")
|
||||
echo "/home/${timestamp}-vmess.txt"
|
||||
}
|
||||
|
||||
# 保存多个VMess节点到文件
|
||||
save_multiple_vmess_links() {
|
||||
local local_ip=$1
|
||||
shift
|
||||
local vmess_file=$(get_vmess_filename)
|
||||
|
||||
# 清空或创建文件
|
||||
> "$vmess_file"
|
||||
|
||||
# 处理所有传入的节点信息
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local port=$1
|
||||
local id=$2
|
||||
local path=$3
|
||||
local index=$4
|
||||
shift 4
|
||||
# 获取对应的出站 SOCKS5 的 IP 地址
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值
|
||||
fi
|
||||
|
||||
# 生成VMess链接
|
||||
local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)"
|
||||
|
||||
# 写入文件
|
||||
echo "$vmess_link" >> "$vmess_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将操作的所有节点保存至 $vmess_file${none}"
|
||||
}
|
||||
|
||||
# 保存多个VMess节点到文件(带自定义PS字段)
|
||||
save_multiple_vmess_links_with_ps() {
|
||||
local local_ip=$1
|
||||
shift
|
||||
local vmess_file=$(get_vmess_filename)
|
||||
|
||||
# 清空或创建文件
|
||||
> "$vmess_file"
|
||||
|
||||
# 处理所有传入的节点信息
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local port=$1
|
||||
local id=$2
|
||||
local path=$3
|
||||
local index=$4
|
||||
local sk5_ip=$5 # 额外参数用于PS字段
|
||||
shift 5
|
||||
|
||||
# 生成VMess链接,使用自定义PS字段
|
||||
local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)"
|
||||
|
||||
# 写入文件
|
||||
echo "$vmess_link" >> "$vmess_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将操作的所有节点保存至 $vmess_file${none}"
|
||||
}
|
||||
|
||||
# 保存所有VMess节点到文件
|
||||
save_all_vmess_links() {
|
||||
local local_ip=$(get_local_ip) # 获取本机IP
|
||||
local vmess_file=$(get_vmess_filename)
|
||||
|
||||
# 清空或创建文件
|
||||
> "$vmess_file"
|
||||
|
||||
local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file")
|
||||
local length=$(jq '. | length' <<< "$config")
|
||||
|
||||
for ((i = 0; i < length; i++)); do
|
||||
local port=$(jq -r ".[$i].port" <<< "$config")
|
||||
local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config")
|
||||
local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config")
|
||||
|
||||
# 获取对应的出站 SOCKS5 的 IP 地址
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值
|
||||
fi
|
||||
# 生成VMess链接
|
||||
local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)"
|
||||
|
||||
# 写入文件
|
||||
echo "$vmess_link" >> "$vmess_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已将全部VMess节点保存至 $vmess_file${none}"
|
||||
}
|
||||
|
||||
# 显示所有入站配置和 Vmess 链接以及对应的出站配置
|
||||
show_inbound_configs() {
|
||||
local local_ip=$(get_local_ip) # 获取本机IP
|
||||
|
||||
local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file")
|
||||
local outbounds=$(jq '.outbounds' "$config_file")
|
||||
echo -e "${green}入站节点配置:${none}"
|
||||
|
||||
local length=$(jq '. | length' <<< "$config")
|
||||
for ((i = 0; i < length; i++)); do
|
||||
local port=$(jq -r ".[$i].port" <<< "$config")
|
||||
local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config")
|
||||
local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config")
|
||||
|
||||
# 将节点地址设置为本机IP
|
||||
local node_address="$local_ip"
|
||||
|
||||
# 获取sk5代理IP
|
||||
local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file")
|
||||
if [[ -z "$sk5_ip" ]]; then
|
||||
sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值
|
||||
fi
|
||||
local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$node_address\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)"
|
||||
|
||||
echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Vmess 链接: ${vmess_link}"
|
||||
|
||||
# 构造出站配置的标签
|
||||
local outbound_tag="outbound$port"
|
||||
|
||||
# 根据构造的标签查找对应的出站配置
|
||||
local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds")
|
||||
|
||||
if [[ ! -z $outbound_config ]]; then
|
||||
echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")"
|
||||
else
|
||||
echo -e "${red}未找到对应的出站配置。${none}"
|
||||
fi
|
||||
done
|
||||
|
||||
# 保存所有VMess链接到文件
|
||||
save_all_vmess_links
|
||||
}
|
||||
|
||||
# 添加新节点
|
||||
add_new_nodes() {
|
||||
local sk5_file="/home/sk5.txt"
|
||||
|
||||
# 检查sk5.txt文件是否存在
|
||||
if [ ! -f "$sk5_file" ]; then
|
||||
echo -e "${red}错误!${none} $sk5_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取sk5.txt文件中的代理信息
|
||||
local sk5_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
sk5_proxies+=("$line")
|
||||
done < "$sk5_file"
|
||||
|
||||
# 检查是否读取到代理
|
||||
local proxy_count=${#sk5_proxies[@]}
|
||||
if [ $proxy_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $sk5_file 中找到有效的代理配置。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $sk5_file 读取到 $proxy_count 个代理配置。${none}"
|
||||
read -p "是否要导入全部配置?(y/n): " confirm
|
||||
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
read -p "请输入要导入的代理数量 (最大 $proxy_count): " num_to_import
|
||||
if ! [[ $num_to_import =~ ^[0-9]+$ ]] || [ $num_to_import -le 0 ] || [ $num_to_import -gt $proxy_count ]; then
|
||||
echo -e "${red}错误!${none} 输入数量无效。"
|
||||
return
|
||||
fi
|
||||
else
|
||||
num_to_import=$proxy_count
|
||||
fi
|
||||
|
||||
local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file")
|
||||
local start_port=$((max_port+1))
|
||||
local local_ip=$(get_local_ip)
|
||||
local nodes_to_save=()
|
||||
|
||||
for ((i=0; i<num_to_import; i++)); do
|
||||
local new_port=$((start_port+i))
|
||||
local new_tag="inbound$new_port"
|
||||
local new_outbound_tag="outbound$new_port"
|
||||
local new_id=$(uuidgen)
|
||||
|
||||
# 解析代理信息 (格式: IP:端口:用户名:密码)
|
||||
IFS=':' read -r outbound_addr outbound_port outbound_user outbound_pass <<< "${sk5_proxies[$i]}"
|
||||
|
||||
if [[ -z "$outbound_addr" || -z "$outbound_port" || -z "$outbound_user" || -z "$outbound_pass" ]]; then
|
||||
echo -e "${red}警告:${none} 代理 #$((i+1)) 格式无效,终止脚本运行: ${sk5_proxies[$i]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${yellow}配置入站端口 $new_port 连接到代理 $outbound_addr:$outbound_port${none}"
|
||||
|
||||
# 添加入站配置(入站地址设置为 "0.0.0.0")
|
||||
jq --argjson port "$new_port" --arg id "$new_id" --arg tag "$new_tag" '
|
||||
.inbounds += [{
|
||||
listen: "0.0.0.0",
|
||||
port: $port,
|
||||
protocol: "vmess",
|
||||
settings: { clients: [{ id: $id }] },
|
||||
streamSettings: { network: "ws", security: "none", wsSettings: { path: "/websocket" } },
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加出站配置
|
||||
jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" '
|
||||
.outbounds += [{
|
||||
protocol: "socks",
|
||||
settings: { servers: [{ address: $addr, port: $port | tonumber, users: [{ user: $user, pass: $pass }] }] },
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加路由规则
|
||||
jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" '
|
||||
.routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }]
|
||||
' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 保存节点信息以便后续生成VMess链接
|
||||
nodes_to_save+=("$new_port" "$new_id" "/websocket" "$((i+1))")
|
||||
done
|
||||
|
||||
# 保存所有新添加的节点到一个文件
|
||||
save_multiple_vmess_links "$local_ip" "${nodes_to_save[@]}"
|
||||
|
||||
echo -e "${green}已成功添加 $num_to_import 个节点。${none}"
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 根据xiugai.txt文件修改SOCKS5出站代理
|
||||
modify_socks5_outbound() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
# 检查xiugai.txt文件是否存在
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo -e "${red}错误!${none} $modify_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取xiugai.txt文件中的代理信息
|
||||
local modify_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
modify_proxies+=("$line")
|
||||
done < "$modify_file"
|
||||
|
||||
# 检查是否读取到代理
|
||||
local proxy_count=${#modify_proxies[@]}
|
||||
if [ $proxy_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的代理配置。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $modify_file 读取到 $proxy_count 个代理配置。${none}"
|
||||
local local_ip=$(get_local_ip)
|
||||
local nodes_to_save=()
|
||||
|
||||
# 处理每个要修改的代理
|
||||
for proxy in "${modify_proxies[@]}"; do
|
||||
IFS=':' read -r old_ip new_port new_user new_pass <<< "$proxy"
|
||||
|
||||
if [[ -z "$old_ip" || -z "$new_port" || -z "$new_user" || -z "$new_pass" ]]; then
|
||||
echo -e "${red}警告:${none} 代理格式无效,终止脚本运行: $proxy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 查找匹配的出站节点
|
||||
local outbound_config=$(jq --arg ip "$old_ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, address: .settings.servers[0].address, port: .settings.servers[0].port, user: .settings.servers[0].users[0].user, pass: .settings.servers[0].users[0].pass}' "$config_file")
|
||||
|
||||
if [[ -z "$outbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到IP地址为 $old_ip 的SOCKS5出站节点,终止脚本运行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local tag=$(echo "$outbound_config" | jq -r '.tag')
|
||||
local old_port=$(echo "$outbound_config" | jq -r '.port')
|
||||
local old_user=$(echo "$outbound_config" | jq -r '.user')
|
||||
local old_pass=$(echo "$outbound_config" | jq -r '.pass')
|
||||
|
||||
echo -e "${yellow}找到匹配的出站节点:${none} 标签=$tag, 旧IP=$old_ip, 旧端口=$old_port, 旧用户名=$old_user, 旧密码=$old_pass"
|
||||
echo -e "${green}将更新为:${none} 新IP=$old_ip, 新端口=$new_port, 新用户名=$new_user, 新密码=$new_pass"
|
||||
|
||||
# 更新SOCKS5出站配置
|
||||
local temp_file=$(mktemp)
|
||||
jq --arg tag "$tag" \
|
||||
--arg ip "$old_ip" \
|
||||
--argjson port "$new_port" \
|
||||
--arg user "$new_user" \
|
||||
--arg pass "$new_pass" \
|
||||
'(.outbounds[] | select(.tag == $tag) | .settings.servers[0].address) = $ip |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].port) = $port |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].user) = $user |
|
||||
(.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].pass) = $pass' \
|
||||
"$config_file" > "$temp_file"
|
||||
|
||||
if [ $? -eq 0 ] && [ -s "$temp_file" ]; then
|
||||
mv "$temp_file" "$config_file"
|
||||
echo -e "${green}成功修改SOCKS5出站节点配置!${none}"
|
||||
|
||||
# 查找对应的入站配置并保存节点信息
|
||||
local inbound_port=${tag//outbound/}
|
||||
local inbound_config=$(jq --argjson port "$inbound_port" '.inbounds[] | select(.port == $port)' "$config_file")
|
||||
if [[ -n "$inbound_config" ]]; then
|
||||
local id=$(echo "$inbound_config" | jq -r '.settings.clients[0].id')
|
||||
local path=$(echo "$inbound_config" | jq -r '.streamSettings.wsSettings.path')
|
||||
local index=$(jq '.inbounds | map(select(.port != 9999)) | map(.port == '$inbound_port') | index(true)' "$config_file")
|
||||
|
||||
# 修复: 包含实际的SOCKS5 IP地址作为PS字段
|
||||
nodes_to_save+=("$inbound_port" "$id" "$path" "$((index+1))" "$old_ip")
|
||||
fi
|
||||
else
|
||||
echo -e "${red}更新配置失败!${none}"
|
||||
rm -f "$temp_file"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
# 保存所有修改的节点到一个文件,使用新函数传递PS字段
|
||||
if [[ ${#nodes_to_save[@]} -gt 0 ]]; then
|
||||
save_multiple_vmess_links_with_ps "$local_ip" "${nodes_to_save[@]}"
|
||||
fi
|
||||
|
||||
# 重启Xray服务使配置生效
|
||||
sudo chmod 755 /usr/local/etc/xray/config.json
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 根据xiugai.txt文件删除节点
|
||||
delete_nodes_by_ip() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
# 检查xiugai.txt文件是否存在
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo -e "${red}错误!${none} $modify_file 文件不存在。"
|
||||
return
|
||||
fi
|
||||
|
||||
# 读取xiugai.txt文件中的代理信息
|
||||
local modify_proxies=()
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# 忽略空行
|
||||
if [[ -z "$line" ]]; then
|
||||
continue
|
||||
fi
|
||||
# 只提取IP部分
|
||||
IFS=':' read -r ip _ <<< "$line"
|
||||
modify_proxies+=("$ip")
|
||||
done < "$modify_file"
|
||||
|
||||
# 检查是否读取到IP
|
||||
local ip_count=${#modify_proxies[@]}
|
||||
if [ $ip_count -eq 0 ]; then
|
||||
echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的IP地址。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${green}从 $modify_file 读取到 $ip_count 个IP地址。${none}"
|
||||
|
||||
# 处理每个要删除的IP
|
||||
for ip in "${modify_proxies[@]}"; do
|
||||
# 查找匹配的出站节点
|
||||
local outbound_config=$(jq --arg ip "$ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, port: .settings.servers[0].port}' "$config_file")
|
||||
|
||||
if [[ -z "$outbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到IP地址为 $ip 的SOCKS5出站节点,终止脚本运行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local outbound_tag=$(echo "$outbound_config" | jq -r '.tag')
|
||||
|
||||
# 从outbound_tag中提取端口号(假设格式为"outbound端口号")
|
||||
local port=${outbound_tag#outbound}
|
||||
|
||||
echo -e "${yellow}找到匹配的节点:${none} 出站标签=$outbound_tag, IP=$ip, 端口=$port"
|
||||
|
||||
# 查找对应的入站配置
|
||||
local inbound_config=$(jq --argjson port "$port" '.inbounds[] | select(.port == $port)' "$config_file")
|
||||
|
||||
if [[ -z "$inbound_config" ]]; then
|
||||
echo -e "${red}警告:${none} 未找到对应端口 $port 的入站配置,继续删除出站配置"
|
||||
else
|
||||
local inbound_tag=$(echo "$inbound_config" | jq -r '.tag')
|
||||
echo -e "${yellow}找到对应的入站配置:${none} 标签=$inbound_tag"
|
||||
|
||||
# 删除入站配置
|
||||
jq --argjson port "$port" 'del(.inbounds[] | select(.port == $port))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 删除路由规则(使用实际的inbound_tag而不是构造的标签)
|
||||
jq --arg inTag "$inbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
fi
|
||||
|
||||
# 删除出站配置
|
||||
jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
echo -e "${green}已成功删除IP地址为 $ip 的节点。${none}"
|
||||
done
|
||||
|
||||
sudo systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 主菜单
|
||||
main_menu() {
|
||||
while true; do
|
||||
echo -e "\n${green}快速批量搭建二级代理脚本-管理菜单:${none}"
|
||||
echo "1. 查看所有节点"
|
||||
echo "2. 新增vmess入站sk5出站(从/home/sk5.txt文件导入)"
|
||||
echo "3. 删除节点(根据/home/xiugai.txt文件匹配)"
|
||||
echo "4. 修改SOCKS5出站节点(根据/home/xiugai.txt文件匹配)"
|
||||
echo "5. 退出"
|
||||
read -p "请输入选项: " choice
|
||||
|
||||
case $choice in
|
||||
1) show_inbound_configs ;;
|
||||
2) add_new_nodes ;;
|
||||
3) delete_nodes_by_ip ;;
|
||||
4) modify_socks5_outbound ;;
|
||||
5) break ;;
|
||||
*) echo -e "${red}无效的选项,请重新选择。${none}" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 调用主菜单函数
|
||||
check_and_install_curl
|
||||
check_and_install_jq
|
||||
check_and_install_uuid_runtime
|
||||
check_and_install_xray
|
||||
create_default_config
|
||||
get_local_ip
|
||||
main_menu
|
||||
469
proxy/duosk5.go
Normal file
469
proxy/duosk5.go
Normal file
@@ -0,0 +1,469 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// 颜色常量(ANSI转义码)
|
||||
ColorReset = "\033[0m"
|
||||
ColorRed = "\033[31m"
|
||||
ColorGreen = "\033[32m"
|
||||
ColorYellow = "\033[33m"
|
||||
ColorCyan = "\033[36m"
|
||||
|
||||
// 构建:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sk5 main.go
|
||||
// 脚本过期时间以及其他变量
|
||||
EXPIRE_DATE = "2025-06-08 02:01:01"
|
||||
CONFIG_FILE = "/usr/local/etc/xray/config.json"
|
||||
SOCKS_FILE = "/home/socks.txt"
|
||||
XRAY_INSTALL_URL = "https://github.com/XTLS/Xray-install/raw/main/install-release.sh"
|
||||
XRAY_VERSION = "v1.8.4"
|
||||
START_PORT = 10001
|
||||
)
|
||||
|
||||
// 彩色打印函数
|
||||
func colorPrint(colorCode, format string, a ...interface{}) {
|
||||
fmt.Printf(colorCode+format+ColorReset+"\n", a...)
|
||||
}
|
||||
|
||||
// XrayConfig represents the Xray configuration structure
|
||||
type XrayConfig struct {
|
||||
Inbounds []Inbound `json:"inbounds"`
|
||||
Outbounds []Outbound `json:"outbounds"`
|
||||
Routing Routing `json:"routing"`
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
Port int `json:"port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Settings InboundSettings `json:"settings"`
|
||||
StreamSettings StreamSettings `json:"streamSettings"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type InboundSettings struct {
|
||||
Auth string `json:"auth"`
|
||||
Accounts []Account `json:"accounts"`
|
||||
UDP bool `json:"udp"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
User string `json:"user"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
|
||||
type StreamSettings struct {
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Settings interface{} `json:"settings"`
|
||||
SendThrough string `json:"sendThrough"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type Routing struct {
|
||||
Rules []Rule `json:"rules"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Type string `json:"type"`
|
||||
InboundTag []string `json:"inboundTag"`
|
||||
OutboundTag string `json:"outboundTag"`
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
IP string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// generateRandomString generates a random string of specified length
|
||||
func generateRandomString(length int) string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
b := make([]byte, length)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := range b {
|
||||
b[i] = charset[b[i]%byte(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// checkExpiration checks if the script has expired
|
||||
func checkExpiration() error {
|
||||
colorPrint(ColorCyan, "开始运行...")
|
||||
|
||||
// Get timestamp from cloudflare
|
||||
resp, err := http.Get("https://www.cloudflare.com/cdn-cgi/trace")
|
||||
if err != nil {
|
||||
return fmt.Errorf("网络错误")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取响应失败")
|
||||
}
|
||||
|
||||
// Extract timestamp
|
||||
re := regexp.MustCompile(`ts=(\d+)`)
|
||||
matches := re.FindStringSubmatch(string(body))
|
||||
if len(matches) < 2 {
|
||||
return fmt.Errorf("无法解析时间")
|
||||
}
|
||||
|
||||
timestamp, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("时间转换失败")
|
||||
}
|
||||
|
||||
// Convert to Beijing time
|
||||
currentTime := time.Unix(timestamp, 0).In(time.FixedZone("CST", 8*3600))
|
||||
expireTime, _ := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600))
|
||||
|
||||
if currentTime.After(expireTime) {
|
||||
return fmt.Errorf("当前脚本已过期,请联系作者")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// commandExists checks if a command exists in PATH
|
||||
func commandExists(cmd string) bool {
|
||||
_, err := exec.LookPath(cmd)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// installJQ installs jq if not present
|
||||
func installJQ() error {
|
||||
if commandExists("jq") {
|
||||
colorPrint(ColorGreen, "jq 已安装")
|
||||
return nil
|
||||
}
|
||||
|
||||
colorPrint(ColorYellow, "jq 未安装,正在安装 jq...")
|
||||
|
||||
// Detect OS
|
||||
if _, err := os.Stat("/etc/debian_version"); err == nil {
|
||||
// Debian/Ubuntu
|
||||
cmd := exec.Command("bash", "-c", "apt update && apt install -yq jq")
|
||||
return cmd.Run()
|
||||
} else if _, err := os.Stat("/etc/redhat-release"); err == nil {
|
||||
// RHEL/CentOS
|
||||
cmd := exec.Command("yum", "install", "-y", "epel-release", "jq")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
return fmt.Errorf("无法确定系统发行版,请手动安装 jq")
|
||||
}
|
||||
|
||||
// installXray installs Xray if not present
|
||||
func installXray() error {
|
||||
if commandExists("xray") {
|
||||
colorPrint(ColorGreen, "Xray 已安装")
|
||||
return nil
|
||||
}
|
||||
|
||||
colorPrint(ColorYellow, "Xray 未安装,正在安装 Xray...")
|
||||
|
||||
cmd := exec.Command("bash", "-c", fmt.Sprintf("curl -L %s | bash -s install --version %s", XRAY_INSTALL_URL, XRAY_VERSION))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Xray 安装失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 安装完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPublicIPv4 gets all public IPv4 addresses
|
||||
func getPublicIPv4() ([]string, error) {
|
||||
var publicIPs []string
|
||||
|
||||
// Get all network interfaces
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
ip := ipNet.IP.String()
|
||||
// Check if it's a public IP
|
||||
if isPublicIP(ip) {
|
||||
publicIPs = append(publicIPs, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return publicIPs, nil
|
||||
}
|
||||
|
||||
// isPublicIP checks if an IP is public
|
||||
func isPublicIP(ip string) bool {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for private IP ranges
|
||||
privateRanges := []string{
|
||||
"127.0.0.0/8", // loopback
|
||||
"10.0.0.0/8", // private
|
||||
"172.16.0.0/12", // private
|
||||
"192.168.0.0/16", // private
|
||||
"169.254.0.0/16", // link-local
|
||||
}
|
||||
|
||||
for _, cidr := range privateRanges {
|
||||
_, network, _ := net.ParseCIDR(cidr)
|
||||
if network.Contains(parsedIP) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ensureSocksFileExists creates socks.txt if it doesn't exist
|
||||
func ensureSocksFileExists() error {
|
||||
if _, err := os.Stat(SOCKS_FILE); os.IsNotExist(err) {
|
||||
colorPrint(ColorYellow, "socks.txt 文件不存在,正在创建...")
|
||||
file, err := os.Create(SOCKS_FILE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveNodeInfo saves node information to file and prints it
|
||||
func saveNodeInfo(node NodeInfo) error {
|
||||
// Print node info with colors
|
||||
fmt.Printf(" IP: %s%s%s 端口: %s%d%s 用户名: %s%s%s 密码: %s%s%s\n",
|
||||
ColorGreen, node.IP, ColorReset,
|
||||
ColorGreen, node.Port, ColorReset,
|
||||
ColorGreen, node.Username, ColorReset,
|
||||
ColorGreen, node.Password, ColorReset)
|
||||
|
||||
// Save to file
|
||||
file, err := os.OpenFile(SOCKS_FILE, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = fmt.Fprintf(file, "%s %d %s %s\n", node.IP, node.Port, node.Username, node.Password)
|
||||
return err
|
||||
}
|
||||
|
||||
// configureXray configures Xray with multiple IPs
|
||||
func configureXray() error {
|
||||
publicIPs, err := getPublicIPv4()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取公网IP失败: %v", err)
|
||||
}
|
||||
|
||||
if len(publicIPs) == 0 {
|
||||
return fmt.Errorf("未找到额外IP地址")
|
||||
}
|
||||
|
||||
colorPrint(ColorCyan, "找到的公网 IPv4 地址: %v", publicIPs)
|
||||
|
||||
// Create initial config
|
||||
config := XrayConfig{
|
||||
Inbounds: []Inbound{},
|
||||
Outbounds: []Outbound{},
|
||||
Routing: Routing{
|
||||
Rules: []Rule{},
|
||||
},
|
||||
}
|
||||
|
||||
// Configure each IP
|
||||
port := START_PORT
|
||||
for _, ip := range publicIPs {
|
||||
colorPrint(ColorCyan, "正在配置 IP: %s 端口: %d", ip, port)
|
||||
|
||||
username := generateRandomString(8)
|
||||
password := generateRandomString(8)
|
||||
|
||||
// Create inbound
|
||||
inbound := Inbound{
|
||||
Port: port,
|
||||
Protocol: "socks",
|
||||
Settings: InboundSettings{
|
||||
Auth: "password",
|
||||
Accounts: []Account{
|
||||
{User: username, Pass: password},
|
||||
},
|
||||
UDP: true,
|
||||
IP: "0.0.0.0",
|
||||
},
|
||||
StreamSettings: StreamSettings{
|
||||
Network: "tcp",
|
||||
},
|
||||
Tag: fmt.Sprintf("in-%d", port),
|
||||
}
|
||||
|
||||
// Create outbound
|
||||
outbound := Outbound{
|
||||
Protocol: "freedom",
|
||||
Settings: map[string]interface{}{},
|
||||
SendThrough: ip,
|
||||
Tag: fmt.Sprintf("out-%d", port),
|
||||
}
|
||||
|
||||
// Create routing rule
|
||||
rule := Rule{
|
||||
Type: "field",
|
||||
InboundTag: []string{fmt.Sprintf("in-%d", port)},
|
||||
OutboundTag: fmt.Sprintf("out-%d", port),
|
||||
}
|
||||
|
||||
config.Inbounds = append(config.Inbounds, inbound)
|
||||
config.Outbounds = append(config.Outbounds, outbound)
|
||||
config.Routing.Rules = append(config.Routing.Rules, rule)
|
||||
|
||||
// Save node info
|
||||
node := NodeInfo{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if err := saveNodeInfo(node); err != nil {
|
||||
return fmt.Errorf("保存节点信息失败: %v", err)
|
||||
}
|
||||
|
||||
port++
|
||||
}
|
||||
|
||||
// Write config file
|
||||
configData, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(CONFIG_FILE, configData, 0644); err != nil {
|
||||
return fmt.Errorf("写入配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 配置完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// restartXray restarts the Xray service
|
||||
func restartXray() error {
|
||||
colorPrint(ColorCyan, "正在重启 Xray 服务...")
|
||||
|
||||
// Restart service
|
||||
cmd := exec.Command("systemctl", "restart", "xray")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Xray 服务重启失败: %v", err)
|
||||
}
|
||||
|
||||
// Enable service
|
||||
cmd = exec.Command("systemctl", "enable", "xray")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("启用 Xray 服务失败: %v", err)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "Xray 服务已重启")
|
||||
return nil
|
||||
}
|
||||
|
||||
// readUserInput reads user input for confirmation
|
||||
func readUserInput(prompt string) string {
|
||||
fmt.Print(prompt)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ := reader.ReadString('\n')
|
||||
return strings.TrimSpace(input)
|
||||
}
|
||||
|
||||
func main() {
|
||||
colorPrint(ColorCyan, "站群多IP源进源出sk5协议一键脚本")
|
||||
colorPrint(ColorCyan, "当前为测试版,可以联系作者获取源码")
|
||||
expireTime, err := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600))
|
||||
if err == nil {
|
||||
expireStr := fmt.Sprintf("%d年%d月%d日%d点%d分%d秒",
|
||||
expireTime.Year(),
|
||||
expireTime.Month(),
|
||||
expireTime.Day(),
|
||||
expireTime.Hour(),
|
||||
expireTime.Minute(),
|
||||
expireTime.Second())
|
||||
colorPrint(ColorCyan, "脚本过期时间: %s", expireStr)
|
||||
} else {
|
||||
colorPrint(ColorYellow, "脚本过期时间解析失败")
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Check expiration
|
||||
if err := checkExpiration(); err != nil {
|
||||
colorPrint(ColorRed, "错误: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure socks file exists
|
||||
if err := ensureSocksFileExists(); err != nil {
|
||||
colorPrint(ColorRed, "创建socks文件失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Install jq
|
||||
if err := installJQ(); err != nil {
|
||||
colorPrint(ColorRed, "安装jq失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Install Xray
|
||||
if err := installXray(); err != nil {
|
||||
colorPrint(ColorRed, "安装Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Configure Xray
|
||||
if err := configureXray(); err != nil {
|
||||
colorPrint(ColorRed, "配置Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Restart Xray
|
||||
if err := restartXray(); err != nil {
|
||||
colorPrint(ColorRed, "重启Xray失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
colorPrint(ColorGreen, "部署完成,所有节点信息已保存到 %s", SOCKS_FILE)
|
||||
}
|
||||
237
proxy/duosocks.sh
Normal file
237
proxy/duosocks.sh
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/bin/bash
|
||||
# 站群多IP源进源出节点脚本sk5协议
|
||||
|
||||
# 生成随机8位数的用户名和密码
|
||||
generate_random_string() {
|
||||
local length=8
|
||||
tr -dc A-Za-z0-9 </dev/urandom | head -c $length
|
||||
}
|
||||
|
||||
install_jq() {
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "jq 未安装,正在安装 jq..."
|
||||
if [[ -f /etc/debian_version ]]; then
|
||||
apt update && apt install -yq jq
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
yum install -y epel-release jq
|
||||
else
|
||||
echo "无法确定系统发行版,请手动安装 jq。"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "jq 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
install_xray() {
|
||||
if ! command -v xray &> /dev/null; then
|
||||
echo "Xray 未安装,正在安装 Xray..."
|
||||
if ! bash <(curl -sSL https://gh-proxy.com/https://github.com/sky22333/shell/raw/main/proxy/xray.sh); then
|
||||
echo "Xray 安装失败,请检查网络连接或安装脚本。"
|
||||
exit 1
|
||||
fi
|
||||
echo "Xray 安装完成。"
|
||||
else
|
||||
echo "Xray 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
get_public_ipv4() {
|
||||
ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}'
|
||||
}
|
||||
|
||||
# 确保 socks.txt 文件存在,如果不存在则创建
|
||||
ensure_socks_file_exists() {
|
||||
if [ ! -f /home/socks.txt ]; then
|
||||
echo "socks.txt 文件不存在,正在创建..."
|
||||
touch /home/socks.txt
|
||||
fi
|
||||
}
|
||||
|
||||
print_node_info() {
|
||||
local ip=$1
|
||||
local port=$2
|
||||
local username=$3
|
||||
local password=$4
|
||||
local outfile=${5:-/home/socks.txt}
|
||||
echo -e " IP: \033[32m$ip\033[0m 端口: \033[32m$port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m"
|
||||
echo "$ip $port $username $password" >> "$outfile"
|
||||
}
|
||||
|
||||
configure_xray() {
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到额外IP地址,退出..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "找到的公网 IPv4 地址: ${public_ips[@]}"
|
||||
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"routing": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 配置 inbounds 和 outbounds
|
||||
port=10001
|
||||
for ip in "${public_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip 端口: $port"
|
||||
|
||||
# 此处用户名和密码可以改为固定值
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
|
||||
jq --argjson port "$port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "password",
|
||||
"accounts": [{
|
||||
"user": $username,
|
||||
"pass": $password
|
||||
}],
|
||||
"udp": true,
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
print_node_info "$ip" "$port" "$username" "$password"
|
||||
|
||||
port=$((port + 1))
|
||||
done
|
||||
|
||||
echo "Xray 配置完成。"
|
||||
}
|
||||
|
||||
restart_xray() {
|
||||
echo "正在重启 Xray 服务..."
|
||||
if ! systemctl restart xray; then
|
||||
echo "Xray 服务重启失败,请检查配置文件。"
|
||||
exit 1
|
||||
fi
|
||||
systemctl enable xray
|
||||
echo "Xray 服务已重启。"
|
||||
}
|
||||
|
||||
add_mode=false
|
||||
if [[ "$1" == "-add" ]]; then
|
||||
add_mode=true
|
||||
fi
|
||||
|
||||
main() {
|
||||
ensure_socks_file_exists
|
||||
install_jq
|
||||
install_xray
|
||||
if $add_mode; then
|
||||
add_xray_nodes
|
||||
else
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
if [[ -f $config_file ]]; then
|
||||
if jq -e '.inbounds[]? | select(.port==10001)' "$config_file" >/dev/null; then
|
||||
echo "检测到已有节点配置,无需重复生成,如需添加节点请添加 -add命令"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
configure_xray
|
||||
restart_xray
|
||||
echo "部署完成,所有节点信息已保存到 /home/socks.txt"
|
||||
fi
|
||||
}
|
||||
|
||||
add_xray_nodes() {
|
||||
public_ips=($(get_public_ipv4))
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
if [[ ! -f $config_file ]]; then
|
||||
echo "Xray 配置文件不存在,无法追加。"
|
||||
exit 1
|
||||
fi
|
||||
# 获取已存在的IP
|
||||
existing_ips=($(jq -r '.outbounds[].sendThrough' "$config_file" | grep -v null))
|
||||
# 过滤出未添加的新IP
|
||||
new_ips=()
|
||||
for ip in "${public_ips[@]}"; do
|
||||
found=false
|
||||
for eip in "${existing_ips[@]}"; do
|
||||
if [[ "$ip" == "$eip" ]]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if ! $found; then
|
||||
new_ips+=("$ip")
|
||||
fi
|
||||
done
|
||||
if [[ ${#new_ips[@]} -eq 0 ]]; then
|
||||
echo "没有新IP需要追加。"
|
||||
return
|
||||
fi
|
||||
# 生成北京时间文件名
|
||||
beijing_time=$(TZ=Asia/Shanghai date +"%Y%m%d_%H%M%S")
|
||||
newfile="/home/socks_add_${beijing_time}.txt"
|
||||
touch "$newfile"
|
||||
# 找到当前最大端口
|
||||
last_port=$(jq -r '.inbounds[].port' "$config_file" | sort -n | tail -1)
|
||||
if [[ -z "$last_port" || "$last_port" == "null" ]]; then
|
||||
port=10001
|
||||
else
|
||||
port=$((last_port + 1))
|
||||
fi
|
||||
for ip in "${new_ips[@]}"; do
|
||||
echo "追加 IP: $ip 端口: $port"
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
jq --argjson port "$port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "password",
|
||||
"accounts": [{
|
||||
"user": $username,
|
||||
"pass": $password
|
||||
}],
|
||||
"udp": true,
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
print_node_info "$ip" "$port" "$username" "$password" "$newfile"
|
||||
port=$((port + 1))
|
||||
done
|
||||
echo "Xray 追加完成,新增节点信息已保存到 $newfile"
|
||||
restart_xray
|
||||
}
|
||||
|
||||
main
|
||||
141
proxy/duovmess.sh
Normal file
141
proxy/duovmess.sh
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
# 站群多IP源进源出节点脚本vmess+ws协议
|
||||
# 作者sky22333
|
||||
|
||||
install_jq() {
|
||||
# 检查 jq 和 uuidgen 是否已安装
|
||||
if ! command -v jq &> /dev/null || ! command -v uuidgen &> /dev/null; then
|
||||
echo "未找到 jq 或 uuidgen,正在安装依赖..."
|
||||
if [[ -f /etc/debian_version ]]; then
|
||||
apt update && apt install -yq jq uuid-runtime
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
yum install -y jq util-linux
|
||||
else
|
||||
echo "无法确定系统发行版,请手动安装 jq 和 uuid-runtime。"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "jq 和 uuidgen 都已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
install_xray() {
|
||||
if ! command -v xray &> /dev/null; then
|
||||
echo "Xray 未安装,正在安装 Xray..."
|
||||
if ! bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4; then
|
||||
echo "Xray 安装失败,请检查网络连接或安装脚本。"
|
||||
exit 1
|
||||
fi
|
||||
echo "Xray 安装完成。"
|
||||
else
|
||||
echo "Xray 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
get_public_ipv4() {
|
||||
ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}'
|
||||
}
|
||||
|
||||
# 确保 vmess.txt 文件存在,如果不存在则创建
|
||||
ensure_vmess_file() {
|
||||
if [ ! -f /home/vmess.txt ]; then
|
||||
echo "vmess.txt 文件不存在,正在创建..."
|
||||
touch /home/vmess.txt
|
||||
fi
|
||||
}
|
||||
|
||||
print_node_links() {
|
||||
local port=$1
|
||||
local id=$2
|
||||
local outbound_ip=$3
|
||||
local link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$outbound_ip\",\"add\":\"$outbound_ip\",\"port\":\"$port\",\"id\":\"$id\",\"aid\":\"0\",\"net\":\"ws\",\"type\":\"none\",\"host\":\"\",\"path\":\"/ws\",\"tls\":\"none\"}" | base64 | tr -d '\n')"
|
||||
echo -e "端口: $port, 节点链接: \033[32m$link\033[0m"
|
||||
|
||||
# 将 vmess 链接保存到 /home/vmess.txt 文件中,每行一个链接
|
||||
echo "$link" >> /home/vmess.txt
|
||||
}
|
||||
|
||||
configure_xray() {
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到任何公网 IPv4 地址,退出..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "找到的公网 IPv4 地址: ${public_ips[@]}"
|
||||
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"routing": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 配置 inbounds 和 outbounds
|
||||
port=10001
|
||||
for ip in "${public_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip 端口: $port"
|
||||
|
||||
id=$(uuidgen)
|
||||
|
||||
jq --argjson port "$port" --arg ip "$ip" --arg id "$id" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [{
|
||||
"id": $id,
|
||||
"alterId": 0
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"wsSettings": {
|
||||
"path": "/ws"
|
||||
}
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
print_node_links "$port" "$id" "$ip"
|
||||
|
||||
port=$((port + 1))
|
||||
done
|
||||
|
||||
echo "Xray 配置完成。"
|
||||
}
|
||||
|
||||
restart_xray() {
|
||||
echo "正在重启 Xray 服务..."
|
||||
if ! systemctl restart xray; then
|
||||
echo "Xray 服务重启失败,请检查配置文件。"
|
||||
exit 1
|
||||
fi
|
||||
systemctl enable xray
|
||||
echo "Xray 服务已重启。"
|
||||
}
|
||||
|
||||
main() {
|
||||
ensure_vmess_file
|
||||
install_jq
|
||||
install_xray
|
||||
configure_xray
|
||||
restart_xray
|
||||
echo "部署完成,所有节点信息已保存在 /home/vmess.txt"
|
||||
}
|
||||
|
||||
main
|
||||
564
proxy/l2tp.sh
Normal file
564
proxy/l2tp.sh
Normal file
@@ -0,0 +1,564 @@
|
||||
#!/usr/bin/env bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
#=======================================================================#
|
||||
# 系统支持: Debian 10 + / Ubuntu 18.04 + #
|
||||
# 描述: L2TP VPN 自动安装脚本 #
|
||||
# 基于Teddysun版本修改 #
|
||||
#=======================================================================#
|
||||
cur_dir=`pwd`
|
||||
|
||||
rootness(){
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "错误: 此脚本必须以root身份运行!" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
tunavailable(){
|
||||
if [[ ! -e /dev/net/tun ]]; then
|
||||
echo "错误: TUN/TAP 不可用!" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
disable_selinux(){
|
||||
if [ -s /etc/selinux/config ] && grep 'SELINUX=enforcing' /etc/selinux/config; then
|
||||
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
|
||||
setenforce 0
|
||||
fi
|
||||
}
|
||||
|
||||
get_opsy(){
|
||||
[ -f /etc/os-release ] && awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release && return
|
||||
[ -f /etc/lsb-release ] && awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release && return
|
||||
}
|
||||
|
||||
get_os_info(){
|
||||
IP=$( ip addr | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | egrep -v "^192\.168|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-2]\.|^10\.|^127\.|^255\.|^0\." | head -n 1 )
|
||||
[ -z ${IP} ] && IP=$( wget -qO- -t1 -T2 ipinfo.io/ip )
|
||||
if [ -z ${IP} ]; then
|
||||
IP=$( wget -qO- -t1 -T2 ifconfig.me )
|
||||
fi
|
||||
|
||||
local cname=$( awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' )
|
||||
local cores=$( awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo )
|
||||
local freq=$( awk -F: '/cpu MHz/ {freq=$2} END {print freq}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' )
|
||||
local tram=$( free -m | awk '/Mem/ {print $2}' )
|
||||
local swap=$( free -m | awk '/Swap/ {print $2}' )
|
||||
local up=$( awk '{a=$1/86400;b=($1%86400)/3600;c=($1%3600)/60;d=$1%60} {printf("%d天 %d:%d:%d\n",a,b,c,d)}' /proc/uptime )
|
||||
local load=$( w | head -1 | awk -F'load average:' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//' )
|
||||
local opsy=$( get_opsy )
|
||||
local arch=$( uname -m )
|
||||
local lbit=$( getconf LONG_BIT )
|
||||
local host=$( hostname )
|
||||
local kern=$( uname -r )
|
||||
|
||||
echo "########## 系统信息 ##########"
|
||||
echo
|
||||
echo "CPU型号 : ${cname}"
|
||||
echo "CPU核心数 : ${cores}"
|
||||
echo "CPU频率 : ${freq} MHz"
|
||||
echo "总内存大小 : ${tram} MB"
|
||||
echo "总交换分区大小 : ${swap} MB"
|
||||
echo "系统运行时间 : ${up}"
|
||||
echo "平均负载 : ${load}"
|
||||
echo "操作系统 : ${opsy}"
|
||||
echo "系统架构 : ${arch} (${lbit} Bit)"
|
||||
echo "内核版本 : ${kern}"
|
||||
echo "主机名 : ${host}"
|
||||
echo "IPv4地址 : ${IP}"
|
||||
echo
|
||||
echo "##################################"
|
||||
}
|
||||
|
||||
check_sys(){
|
||||
local checkType=$1
|
||||
local value=$2
|
||||
|
||||
local release=''
|
||||
local systemPackage=''
|
||||
|
||||
if cat /etc/issue | grep -Eqi "debian"; then
|
||||
release="debian"
|
||||
systemPackage="apt"
|
||||
elif cat /etc/issue | grep -Eqi "ubuntu"; then
|
||||
release="ubuntu"
|
||||
systemPackage="apt"
|
||||
elif cat /proc/version | grep -Eqi "debian"; then
|
||||
release="debian"
|
||||
systemPackage="apt"
|
||||
elif cat /proc/version | grep -Eqi "ubuntu"; then
|
||||
release="ubuntu"
|
||||
systemPackage="apt"
|
||||
else
|
||||
echo "错误: 不支持的系统,请使用Debian或Ubuntu系统!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${checkType} == "sysRelease" ]]; then
|
||||
if [ "$value" == "$release" ];then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
elif [[ ${checkType} == "packageManager" ]]; then
|
||||
if [ "$value" == "$systemPackage" ];then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
rand(){
|
||||
index=0
|
||||
str=""
|
||||
for i in {a..z}; do arr[index]=${i}; index=`expr ${index} + 1`; done
|
||||
for i in {A..Z}; do arr[index]=${i}; index=`expr ${index} + 1`; done
|
||||
for i in {0..9}; do arr[index]=${i}; index=`expr ${index} + 1`; done
|
||||
for i in {1..10}; do str="$str${arr[$RANDOM%$index]}"; done
|
||||
echo ${str}
|
||||
}
|
||||
|
||||
is_64bit(){
|
||||
if [ `getconf WORD_BIT` = '32' ] && [ `getconf LONG_BIT` = '64' ] ; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
versionget(){
|
||||
if [ -f /etc/os-release ];then
|
||||
grep -oE "[0-9.]+" /etc/os-release | head -1
|
||||
else
|
||||
grep -oE "[0-9.]+" /etc/issue
|
||||
fi
|
||||
}
|
||||
|
||||
debianversion(){
|
||||
if check_sys sysRelease debian;then
|
||||
local version=$( get_opsy )
|
||||
local code=${1}
|
||||
local main_ver=$( echo ${version} | sed 's/[^0-9]//g')
|
||||
if [ "${main_ver}" == "${code}" ];then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
version_check(){
|
||||
if check_sys packageManager apt; then
|
||||
if debianversion 5; then
|
||||
echo "错误: Debian 5 不支持,请重新安装OS并重试。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_char(){
|
||||
SAVEDSTTY=`stty -g`
|
||||
stty -echo
|
||||
stty cbreak
|
||||
dd if=/dev/tty bs=1 count=1 2> /dev/null
|
||||
stty -raw
|
||||
stty echo
|
||||
stty $SAVEDSTTY
|
||||
}
|
||||
|
||||
preinstall_l2tp(){
|
||||
|
||||
echo
|
||||
if [ -d "/proc/vz" ]; then
|
||||
echo -e "\033[41;37m 警告: \033[0m 您的VPS基于OpenVZ,内核可能不支持IPSec。"
|
||||
echo "是否继续安装? (y/n)"
|
||||
read -p "(默认: n)" agree
|
||||
[ -z ${agree} ] && agree="n"
|
||||
if [ "${agree}" == "n" ]; then
|
||||
echo
|
||||
echo "L2TP安装已取消。"
|
||||
echo
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
echo "请输入IP范围:"
|
||||
read -p "(默认范围: 192.168.18):" iprange
|
||||
[ -z ${iprange} ] && iprange="192.168.18"
|
||||
|
||||
echo "请输入PSK密钥:"
|
||||
read -p "(默认PSK: admin123@l2tp):" mypsk
|
||||
[ -z ${mypsk} ] && mypsk="admin123@l2tp"
|
||||
|
||||
echo "请输入用户名:"
|
||||
read -p "(默认用户名: admin123):" username
|
||||
[ -z ${username} ] && username="admin123"
|
||||
|
||||
password=`rand`
|
||||
echo "请输入 ${username} 的密码:"
|
||||
read -p "(默认密码: ${password}):" tmppassword
|
||||
[ ! -z ${tmppassword} ] && password=${tmppassword}
|
||||
|
||||
echo
|
||||
echo "服务器IP: ${IP}"
|
||||
echo "服务器本地IP: ${iprange}.1"
|
||||
echo "客户端远程IP范围: ${iprange}.2-${iprange}.254"
|
||||
echo "PSK密钥: ${mypsk}"
|
||||
echo
|
||||
echo "按任意键开始安装...或按Ctrl+C取消。"
|
||||
char=`get_char`
|
||||
|
||||
}
|
||||
|
||||
# 安装依赖
|
||||
install_l2tp(){
|
||||
mknod /dev/random c 1 9
|
||||
apt -y update
|
||||
apt -yq install curl wget ppp xl2tpd libreswan
|
||||
config_install
|
||||
}
|
||||
|
||||
config_install(){
|
||||
|
||||
cat > /etc/ipsec.conf<<EOF
|
||||
version 2.0
|
||||
|
||||
config setup
|
||||
protostack=netkey
|
||||
nhelpers=0
|
||||
uniqueids=no
|
||||
interfaces=%defaultroute
|
||||
virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:!${iprange}.0/24
|
||||
|
||||
conn l2tp-psk
|
||||
rightsubnet=vhost:%priv
|
||||
also=l2tp-psk-nonat
|
||||
|
||||
conn l2tp-psk-nonat
|
||||
authby=secret
|
||||
pfs=no
|
||||
auto=add
|
||||
keyingtries=3
|
||||
rekey=no
|
||||
ikelifetime=8h
|
||||
keylife=1h
|
||||
type=transport
|
||||
left=%defaultroute
|
||||
leftid=${IP}
|
||||
leftprotoport=17/1701
|
||||
right=%any
|
||||
rightprotoport=17/%any
|
||||
dpddelay=40
|
||||
dpdtimeout=130
|
||||
dpdaction=clear
|
||||
sha2-truncbug=yes
|
||||
EOF
|
||||
|
||||
cat > /etc/ipsec.secrets<<EOF
|
||||
%any %any : PSK "${mypsk}"
|
||||
EOF
|
||||
|
||||
cat > /etc/xl2tpd/xl2tpd.conf<<EOF
|
||||
[global]
|
||||
port = 1701
|
||||
|
||||
[lns default]
|
||||
ip range = ${iprange}.2-${iprange}.254
|
||||
local ip = ${iprange}.1
|
||||
require chap = yes
|
||||
refuse pap = yes
|
||||
require authentication = yes
|
||||
name = l2tpd
|
||||
ppp debug = yes
|
||||
pppoptfile = /etc/ppp/options.xl2tpd
|
||||
length bit = yes
|
||||
EOF
|
||||
|
||||
cat > /etc/ppp/options.xl2tpd<<EOF
|
||||
ipcp-accept-local
|
||||
ipcp-accept-remote
|
||||
require-mschap-v2
|
||||
ms-dns 8.8.8.8
|
||||
ms-dns 8.8.4.4
|
||||
noccp
|
||||
auth
|
||||
hide-password
|
||||
idle 1800
|
||||
mtu 1410
|
||||
mru 1410
|
||||
nodefaultroute
|
||||
debug
|
||||
proxyarp
|
||||
connect-delay 5000
|
||||
EOF
|
||||
|
||||
rm -f /etc/ppp/chap-secrets
|
||||
cat > /etc/ppp/chap-secrets<<EOF
|
||||
# Secrets for authentication using CHAP
|
||||
# client server secret IP addresses
|
||||
${username} l2tpd ${password} *
|
||||
EOF
|
||||
|
||||
cp -pf /etc/sysctl.conf /etc/sysctl.conf.bak
|
||||
|
||||
sed -i 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
|
||||
|
||||
for each in `ls /proc/sys/net/ipv4/conf/`; do
|
||||
echo "net.ipv4.conf.${each}.accept_source_route=0" >> /etc/sysctl.conf
|
||||
echo "net.ipv4.conf.${each}.accept_redirects=0" >> /etc/sysctl.conf
|
||||
echo "net.ipv4.conf.${each}.send_redirects=0" >> /etc/sysctl.conf
|
||||
echo "net.ipv4.conf.${each}.rp_filter=0" >> /etc/sysctl.conf
|
||||
done
|
||||
sysctl -p
|
||||
|
||||
[ -f /etc/iptables.rules ] && cp -pf /etc/iptables.rules /etc/iptables.rules.old.`date +%Y%m%d`
|
||||
|
||||
# 确保IP变量已正确获取
|
||||
if [ -z "${IP}" ]; then
|
||||
IP=$(wget -qO- ipinfo.io/ip)
|
||||
fi
|
||||
|
||||
cat > /etc/iptables.rules <<EOF
|
||||
# Added by L2TP VPN script
|
||||
*filter
|
||||
:INPUT ACCEPT [0:0]
|
||||
:FORWARD ACCEPT [0:0]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
-A INPUT -p icmp -j ACCEPT
|
||||
-A INPUT -i lo -j ACCEPT
|
||||
-A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
-A INPUT -p udp -m multiport --dports 500,4500,1701 -j ACCEPT
|
||||
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
-A FORWARD -s ${iprange}.0/24 -j ACCEPT
|
||||
COMMIT
|
||||
*nat
|
||||
:PREROUTING ACCEPT [0:0]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
:POSTROUTING ACCEPT [0:0]
|
||||
-A POSTROUTING -s ${iprange}.0/24 -j SNAT --to-source ${IP}
|
||||
COMMIT
|
||||
EOF
|
||||
|
||||
# 创建rc.local文件(如果不存在)
|
||||
if [ ! -f /etc/rc.local ]; then
|
||||
cat > /etc/rc.local <<EOF
|
||||
#!/bin/sh -e
|
||||
#
|
||||
# rc.local
|
||||
#
|
||||
# This script is executed at the end of each multiuser runlevel.
|
||||
# Make sure that the script will "exit 0" on success or any other
|
||||
# value on error.
|
||||
#
|
||||
# In order to enable or disable this script just change the execution
|
||||
# bits.
|
||||
#
|
||||
# By default this script does nothing.
|
||||
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
/usr/sbin/service ipsec start
|
||||
/usr/sbin/service xl2tpd start
|
||||
/sbin/iptables-restore < /etc/iptables.rules
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x /etc/rc.local
|
||||
else
|
||||
# 如果已存在rc.local,则追加内容
|
||||
sed -i '/^exit 0/d' /etc/rc.local
|
||||
cat >> /etc/rc.local <<EOF
|
||||
|
||||
# Added by L2TP VPN script
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
/usr/sbin/service ipsec start
|
||||
/usr/sbin/service xl2tpd start
|
||||
/sbin/iptables-restore < /etc/iptables.rules
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat > /etc/network/if-up.d/iptables <<EOF
|
||||
#!/bin/sh
|
||||
/sbin/iptables-restore < /etc/iptables.rules
|
||||
EOF
|
||||
chmod +x /etc/network/if-up.d/iptables
|
||||
|
||||
if [ ! -f /etc/ipsec.d/cert9.db ]; then
|
||||
echo > /var/tmp/libreswan-nss-pwd
|
||||
certutil -N -f /var/tmp/libreswan-nss-pwd -d /etc/ipsec.d
|
||||
rm -f /var/tmp/libreswan-nss-pwd
|
||||
fi
|
||||
|
||||
update-rc.d -f xl2tpd defaults
|
||||
|
||||
# 启用并启动服务
|
||||
systemctl enable ipsec
|
||||
systemctl enable xl2tpd
|
||||
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
/sbin/iptables-restore < /etc/iptables.rules
|
||||
systemctl restart ipsec
|
||||
systemctl restart xl2tpd
|
||||
}
|
||||
|
||||
finally(){
|
||||
cp -f ${cur_dir}/l2tp.sh /usr/bin/l2tp 2>/dev/null || true
|
||||
echo "请稍候..."
|
||||
sleep 3
|
||||
ipsec verify
|
||||
echo
|
||||
echo "###############################################################"
|
||||
echo "# L2TP 安装脚本 #"
|
||||
echo "###############################################################"
|
||||
echo
|
||||
echo "默认用户名和密码如下:"
|
||||
echo
|
||||
echo "服务器IP: ${IP}"
|
||||
echo "PSK密钥 : ${mypsk}"
|
||||
echo "用户名 : ${username}"
|
||||
echo "密码 : ${password}"
|
||||
echo
|
||||
echo "如果您想修改用户设置,请使用以下命令:"
|
||||
echo "-a (添加用户)"
|
||||
echo "-d (删除用户)"
|
||||
echo "-l (列出所有用户)"
|
||||
echo "-m (修改用户密码)"
|
||||
echo
|
||||
}
|
||||
|
||||
|
||||
l2tp(){
|
||||
clear
|
||||
echo
|
||||
echo "###############################################################"
|
||||
echo "# L2TP 安装脚本 #"
|
||||
echo "###############################################################"
|
||||
echo
|
||||
rootness
|
||||
tunavailable
|
||||
disable_selinux
|
||||
version_check
|
||||
get_os_info
|
||||
preinstall_l2tp
|
||||
install_l2tp
|
||||
finally
|
||||
}
|
||||
|
||||
list_users(){
|
||||
if [ ! -f /etc/ppp/chap-secrets ];then
|
||||
echo "错误: /etc/ppp/chap-secrets 文件未找到."
|
||||
exit 1
|
||||
fi
|
||||
local line="+-------------------------------------------+\n"
|
||||
local string=%20s
|
||||
printf "${line}|${string} |${string} |\n${line}" 用户名 密码
|
||||
grep -v "^#" /etc/ppp/chap-secrets | awk '{printf "|'${string}' |'${string}' |\n", $1,$3}'
|
||||
printf ${line}
|
||||
}
|
||||
|
||||
add_user(){
|
||||
while :
|
||||
do
|
||||
read -p "请输入用户名:" user
|
||||
if [ -z ${user} ]; then
|
||||
echo "用户名不能为空"
|
||||
else
|
||||
grep -w "${user}" /etc/ppp/chap-secrets > /dev/null 2>&1
|
||||
if [ $? -eq 0 ];then
|
||||
echo "用户名 (${user}) 已存在。请重新输入用户名。"
|
||||
else
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
pass=`rand`
|
||||
echo "请输入 ${user} 的密码:"
|
||||
read -p "(默认密码: ${pass}):" tmppass
|
||||
[ ! -z ${tmppass} ] && pass=${tmppass}
|
||||
echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets
|
||||
echo "用户 (${user}) 添加完成。"
|
||||
}
|
||||
|
||||
del_user(){
|
||||
while :
|
||||
do
|
||||
read -p "请输入要删除的用户名:" user
|
||||
if [ -z ${user} ]; then
|
||||
echo "用户名不能为空"
|
||||
else
|
||||
grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1
|
||||
if [ $? -eq 0 ];then
|
||||
break
|
||||
else
|
||||
echo "用户名 (${user}) 不存在。请重新输入用户名。"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets
|
||||
echo "用户 (${user}) 删除完成。"
|
||||
}
|
||||
|
||||
mod_user(){
|
||||
while :
|
||||
do
|
||||
read -p "请输入要修改密码的用户名:" user
|
||||
if [ -z ${user} ]; then
|
||||
echo "用户名不能为空"
|
||||
else
|
||||
grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1
|
||||
if [ $? -eq 0 ];then
|
||||
break
|
||||
else
|
||||
echo "用户名 (${user}) 不存在。请重新输入用户名。"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
pass=`rand`
|
||||
echo "请输入 ${user} 的新密码:"
|
||||
read -p "(默认密码: ${pass}):" tmppass
|
||||
[ ! -z ${tmppass} ] && pass=${tmppass}
|
||||
sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets
|
||||
echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets
|
||||
echo "用户 ${user} 的密码已更改。"
|
||||
}
|
||||
|
||||
# 主程序
|
||||
action=$1
|
||||
if [ -z ${action} ] && [ "`basename $0`" != "l2tp" ]; then
|
||||
action=install
|
||||
fi
|
||||
|
||||
case ${action} in
|
||||
install)
|
||||
l2tp 2>&1 | tee ${cur_dir}/l2tp.log
|
||||
;;
|
||||
-l|--list)
|
||||
list_users
|
||||
;;
|
||||
-a|--add)
|
||||
add_user
|
||||
;;
|
||||
-d|--del)
|
||||
del_user
|
||||
;;
|
||||
-m|--mod)
|
||||
mod_user
|
||||
;;
|
||||
-h|--help)
|
||||
echo "用法: -l,--list 列出所有用户"
|
||||
echo " -a,--add 添加用户"
|
||||
echo " -d,--del 删除用户"
|
||||
echo " -m,--mod 修改用户密码"
|
||||
echo " -h,--help 打印此帮助信息"
|
||||
;;
|
||||
*)
|
||||
echo "用法: [-l,--查看用户|-a,--添加用户|-d,--删除用户|-m,--修改密码|-h,--帮助信息]" && exit
|
||||
;;
|
||||
esac
|
||||
569
proxy/singbox-zhanqun.sh
Normal file
569
proxy/singbox-zhanqun.sh
Normal file
@@ -0,0 +1,569 @@
|
||||
#!/bin/bash
|
||||
# sing-box站群多IP源进源出节点脚本 支持sk5和vless+tcp协议
|
||||
|
||||
# 生成随机8位数的用户名和密码
|
||||
generate_random_string() {
|
||||
local length=8
|
||||
tr -dc A-Za-z0-9 </dev/urandom | head -c $length
|
||||
}
|
||||
|
||||
# 生成随机UUID
|
||||
generate_uuid() {
|
||||
cat /proc/sys/kernel/random/uuid
|
||||
}
|
||||
|
||||
# 获取当前北京时间,精确到秒
|
||||
get_beijing_time() {
|
||||
TZ=Asia/Shanghai date +"%Y年%m月%d日%H点%M分%S秒"
|
||||
}
|
||||
|
||||
# 全局变量,保存当前操作使用的输出文件名
|
||||
OUTPUT_FILE=""
|
||||
|
||||
# 初始化输出文件名
|
||||
init_output_file() {
|
||||
OUTPUT_FILE="/home/$(get_beijing_time).txt"
|
||||
# 确保文件存在并清空内容
|
||||
touch "$OUTPUT_FILE"
|
||||
> "$OUTPUT_FILE"
|
||||
echo "将使用输出文件: $OUTPUT_FILE"
|
||||
}
|
||||
|
||||
install_jq() {
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "jq 未安装,正在安装 jq..."
|
||||
if [[ -f /etc/debian_version ]]; then
|
||||
apt update && apt install -yq jq
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
yum install -y epel-release jq
|
||||
else
|
||||
echo "无法确定系统发行版,请手动安装 jq。"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "jq 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
install_xray() {
|
||||
if ! command -v sing-box &> /dev/null; then
|
||||
echo "sing-box 未安装,正在安装 sing-box..."
|
||||
VERSION="1.11.5"
|
||||
curl -Lo sing-box.deb "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box_${VERSION}_linux_amd64.deb"
|
||||
if ! dpkg -i sing-box.deb; then
|
||||
echo "sing-box 安装失败,请检查dpkg输出。"
|
||||
rm -f sing-box.deb
|
||||
exit 1
|
||||
fi
|
||||
rm -f sing-box.deb
|
||||
echo "sing-box 安装完成。"
|
||||
else
|
||||
echo "sing-box 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已有节点配置
|
||||
check_existing_nodes() {
|
||||
local config_file="/etc/sing-box/config.json"
|
||||
|
||||
# 如果配置文件不存在,则没有节点配置
|
||||
if [ ! -f "$config_file" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 route.rules 数组数量是否大于等于2
|
||||
local rules_count=$(jq '.route.rules | length' "$config_file" 2>/dev/null)
|
||||
# 如果jq命令失败或rules为空或小于2,则认为没有足够节点配置
|
||||
if [ -z "$rules_count" ] || [ "$rules_count" -lt 2 ]; then
|
||||
return 1
|
||||
fi
|
||||
# 有2个及以上路由规则,视为已有节点配置
|
||||
return 0
|
||||
}
|
||||
|
||||
get_public_ipv4() {
|
||||
ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}'
|
||||
}
|
||||
|
||||
# 获取已配置的IP列表
|
||||
get_configured_ips() {
|
||||
local config_file="/etc/sing-box/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
jq -r '.outbounds[] | .inet4_bind_address' "$config_file" | sort | uniq
|
||||
}
|
||||
|
||||
print_node_info() {
|
||||
local ip=$1
|
||||
local socks_port=$2
|
||||
local vless_port=$3
|
||||
local username=$4
|
||||
local password=$5
|
||||
local uuid=$6
|
||||
|
||||
echo -e " IP: \033[32m$ip\033[0m"
|
||||
echo -e " Socks5 端口: \033[32m$socks_port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m"
|
||||
echo -e " VLESS 端口: \033[32m$vless_port\033[0m UUID: \033[32m$uuid\033[0m"
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
# 保存节点信息到文件
|
||||
echo "$ip:$socks_port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
echo "节点信息已保存到 $OUTPUT_FILE"
|
||||
}
|
||||
|
||||
# 导出所有节点配置
|
||||
export_all_nodes() {
|
||||
local config_file="/etc/sing-box/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "Xray配置文件不存在,无法导出节点信息。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
echo "正在导出所有节点配置到 $OUTPUT_FILE..."
|
||||
|
||||
# 获取所有Socks5节点
|
||||
local socks_nodes=$(jq -r '.inbounds[] | select(.type == "socks") | {port: .listen_port, tag: .tag}' "$config_file")
|
||||
if [ -z "$socks_nodes" ]; then
|
||||
echo "未找到任何节点配置。"
|
||||
return 1
|
||||
fi
|
||||
# 遍历所有Socks5节点,查找对应的信息
|
||||
for row in $(jq -r '.inbounds[] | select(.type == "socks") | @base64' "$config_file"); do
|
||||
inbound=$(echo $row | base64 --decode)
|
||||
local port=$(echo "$inbound" | jq -r '.listen_port')
|
||||
local tag=$(echo "$inbound" | jq -r '.tag')
|
||||
# 查找对应的outbound以获取IP
|
||||
local outbound_tag="out-$port"
|
||||
local ip=$(jq -r --arg tag "$outbound_tag" '.outbounds[] | select(.tag == $tag) | .inet4_bind_address' "$config_file")
|
||||
# 获取Socks5的用户名和密码
|
||||
local username=$(echo "$inbound" | jq -r '.users[0].username')
|
||||
local password=$(echo "$inbound" | jq -r '.users[0].password')
|
||||
# 查找相应的VLESS节点
|
||||
local vless_port=$((port + 1))
|
||||
local vless_tag="in-$vless_port"
|
||||
# 获取VLESS的UUID
|
||||
local uuid=$(jq -r --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .users[0].uuid' "$config_file")
|
||||
if [ -n "$uuid" ]; then
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
# 输出节点信息
|
||||
echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
echo -e "已导出节点: \033[32m$ip\033[0m Socks5端口:\033[32m$port\033[0m VLESS端口:\033[32m$vless_port\033[0m"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "所有节点导出完成,信息已保存到 $OUTPUT_FILE"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 查找配置中未使用的端口号
|
||||
find_next_unused_port() {
|
||||
local config_file="/etc/sing-box/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "10001" # 如果配置文件不存在,从10001开始
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取所有已使用的端口
|
||||
local used_ports=$(jq -r '.inbounds[].port' "$config_file" | sort -n)
|
||||
|
||||
if [ -z "$used_ports" ]; then
|
||||
echo "10001" # 如果没有已使用的端口,从10001开始
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取最大的端口号并加1
|
||||
local max_port=$(echo "$used_ports" | tail -1)
|
||||
local next_port=$((max_port + 1))
|
||||
|
||||
# 确保端口号是奇数(用于socks5)
|
||||
if [ $((next_port % 2)) -eq 0 ]; then
|
||||
next_port=$((next_port + 1))
|
||||
fi
|
||||
|
||||
echo "$next_port"
|
||||
}
|
||||
|
||||
# 添加新节点(只添加未配置的IP)
|
||||
add_new_nodes() {
|
||||
# 获取当前系统的所有公网IP
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到公网IP地址,退出..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取已经配置的IP列表
|
||||
configured_ips=($(get_configured_ips))
|
||||
|
||||
# 初始化新IP列表
|
||||
new_ips=()
|
||||
|
||||
# 比对IP,找出未配置的IP
|
||||
for ip in "${public_ips[@]}"; do
|
||||
is_configured=false
|
||||
for configured_ip in "${configured_ips[@]}"; do
|
||||
if [[ "$ip" == "$configured_ip" ]]; then
|
||||
is_configured=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $is_configured; then
|
||||
new_ips+=("$ip")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查是否有新的IP需要配置
|
||||
if [[ ${#new_ips[@]} -eq 0 ]]; then
|
||||
echo "所有IP都已配置,无需添加新节点。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "发现 ${#new_ips[@]} 个未配置的IP: ${new_ips[@]}"
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
# 获取配置文件路径
|
||||
config_file="/etc/sing-box/config.json"
|
||||
|
||||
# 如果配置文件不存在,创建基础配置
|
||||
if [ ! -f "$config_file" ]; then
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"route": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# 获取下一个可用的端口
|
||||
socks_port=$(find_next_unused_port)
|
||||
|
||||
echo "将从端口 $socks_port 开始配置新节点"
|
||||
|
||||
# 为每个新IP配置节点
|
||||
for ip in "${new_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip"
|
||||
|
||||
# Socks5配置 (奇数端口)
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
|
||||
# VLESS配置 (偶数端口)
|
||||
vless_port=$((socks_port + 1))
|
||||
uuid=$(generate_uuid)
|
||||
|
||||
# 添加Socks5配置
|
||||
jq --argjson port "$socks_port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"type": "socks",
|
||||
"tag": ("in-\($port)"),
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": $port,
|
||||
"users": [{
|
||||
"username": $username,
|
||||
"password": $password
|
||||
}]
|
||||
}] | .outbounds += [{
|
||||
"type": "direct",
|
||||
"tag": ("out-\($port)"),
|
||||
"inet4_bind_address": $ip
|
||||
}] | .route.rules += [{
|
||||
"inbound": ["in-\($port)"],
|
||||
"outbound": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
# 添加VLESS配置
|
||||
jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{
|
||||
"type": "vless",
|
||||
"tag": ("in-\($port)"),
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": $port,
|
||||
"users": [{
|
||||
"uuid": $uuid
|
||||
}]
|
||||
}] | .outbounds += [{
|
||||
"type": "direct",
|
||||
"tag": ("out-\($port)"),
|
||||
"inet4_bind_address": $ip
|
||||
}] | .route.rules += [{
|
||||
"inbound": ["in-\($port)"],
|
||||
"outbound": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 输出节点信息
|
||||
print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid"
|
||||
|
||||
# 增加端口号,为下一个IP准备
|
||||
socks_port=$((vless_port + 1))
|
||||
done
|
||||
|
||||
echo "新节点配置完成,共添加了 ${#new_ips[@]} 个节点"
|
||||
return 0
|
||||
}
|
||||
|
||||
configure_xray() {
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到额外IP地址,退出..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "找到的公网 IPv4 地址: ${public_ips[@]}"
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
config_file="/etc/sing-box/config.json"
|
||||
|
||||
# 创建基础配置文件
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"route": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 初始端口
|
||||
socks_port=10001
|
||||
|
||||
# 配置 inbounds 和 outbounds
|
||||
for ip in "${public_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip"
|
||||
# Socks5配置 (奇数端口)
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
# VLESS配置 (偶数端口)
|
||||
vless_port=$((socks_port + 1))
|
||||
uuid=$(generate_uuid)
|
||||
# 添加Socks5配置
|
||||
jq --argjson port "$socks_port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"type": "socks",
|
||||
"tag": ("in-\($port)"),
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": $port,
|
||||
"users": [{
|
||||
"username": $username,
|
||||
"password": $password
|
||||
}]
|
||||
}] | .outbounds += [{
|
||||
"type": "direct",
|
||||
"tag": ("out-\($port)"),
|
||||
"inet4_bind_address": $ip
|
||||
}] | .route.rules += [{
|
||||
"inbound": ["in-\($port)"],
|
||||
"outbound": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
# 添加VLESS配置
|
||||
jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{
|
||||
"type": "vless",
|
||||
"tag": ("in-\($port)"),
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": $port,
|
||||
"users": [{
|
||||
"uuid": $uuid
|
||||
}]
|
||||
}] | .outbounds += [{
|
||||
"type": "direct",
|
||||
"tag": ("out-\($port)"),
|
||||
"inet4_bind_address": $ip
|
||||
}] | .route.rules += [{
|
||||
"inbound": ["in-\($port)"],
|
||||
"outbound": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
# 输出节点信息
|
||||
print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid"
|
||||
# 增加端口号,为下一个IP准备
|
||||
socks_port=$((vless_port + 1))
|
||||
done
|
||||
|
||||
echo "sing-box 配置完成。"
|
||||
}
|
||||
|
||||
modify_by_ip() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo "修改文件 $modify_file 不存在,跳过修改操作。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "检测到修改文件,开始根据IP修改节点..."
|
||||
|
||||
# 读取当前配置
|
||||
local config_file="/etc/sing-box/config.json"
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "Xray配置文件不存在,请先配置Xray。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
local modify_success=false
|
||||
|
||||
# 逐行读取修改文件中的IP
|
||||
while IFS= read -r ip || [[ -n "$ip" ]]; do
|
||||
# 跳过空行和注释行
|
||||
[[ -z "$ip" || "$ip" =~ ^# ]] && continue
|
||||
|
||||
echo "正在处理IP: $ip"
|
||||
|
||||
# 查找此IP对应的出站配置
|
||||
local ip_exists=$(jq --arg ip "$ip" '.outbounds[] | select(.inet4_bind_address == $ip) | .tag' "$config_file")
|
||||
|
||||
if [[ -z "$ip_exists" ]]; then
|
||||
echo "错误: IP $ip 在当前配置中未找到,停止脚本执行。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 找到对应的入站端口和标签
|
||||
local outbound_tags=$(jq -r --arg ip "$ip" '.outbounds[] | select(.inet4_bind_address == $ip) | .tag' "$config_file")
|
||||
|
||||
for outbound_tag in $outbound_tags; do
|
||||
local port=$(echo $outbound_tag | cut -d'-' -f2)
|
||||
local inbound_tag="in-$port"
|
||||
# 检查协议类型
|
||||
local type=$(jq -r --arg tag "$inbound_tag" '.inbounds[] | select(.tag == $tag) | .type' "$config_file")
|
||||
if [[ "$type" == "socks" ]]; then
|
||||
# 更新socks协议的用户名和密码
|
||||
local username=$(generate_random_string)
|
||||
local password=$(generate_random_string)
|
||||
jq --arg tag "$inbound_tag" --arg username "$username" --arg password "$password" '
|
||||
.inbounds[] |= if .tag == $tag then
|
||||
.users[0].username = $username |
|
||||
.users[0].password = $password
|
||||
else . end' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
# 找到对应的vless端口
|
||||
local vless_port=$((port + 1))
|
||||
local vless_tag="in-$vless_port"
|
||||
# 确认vless端口存在
|
||||
local vless_exists=$(jq --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .tag' "$config_file")
|
||||
# 如果存在,更新vless协议的UUID
|
||||
if [[ -n "$vless_exists" ]]; then
|
||||
local uuid=$(generate_uuid)
|
||||
jq --arg tag "$vless_tag" --arg uuid "$uuid" '
|
||||
.inbounds[] |= if .tag == $tag then
|
||||
.users[0].uuid = $uuid
|
||||
else . end' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
# 保存修改后的节点信息
|
||||
echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
echo "已修改 IP: $ip 的Socks5(端口:$port)和VLESS(端口:$vless_port)配置"
|
||||
modify_success=true
|
||||
else
|
||||
echo "警告: 未找到IP $ip 对应的VLESS配置"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done < "$modify_file"
|
||||
|
||||
if $modify_success; then
|
||||
echo "节点修改完成,信息已保存到 $OUTPUT_FILE"
|
||||
else
|
||||
echo "未进行任何修改"
|
||||
fi
|
||||
}
|
||||
|
||||
restart_xray() {
|
||||
echo "正在重启 sing-box 服务..."
|
||||
if ! systemctl restart sing-box; then
|
||||
echo "sing-box 服务重启失败,请检查配置文件。"
|
||||
exit 1
|
||||
fi
|
||||
systemctl enable sing-box
|
||||
echo "sing-box 服务已重启。"
|
||||
}
|
||||
|
||||
# 显示交互式菜单
|
||||
show_menu() {
|
||||
echo -e "\n\033[36m==== 站群多IP节点管理菜单 ====\033[0m"
|
||||
echo -e "\033[33m1. 部署节点(首次部署)\033[0m"
|
||||
echo -e "\033[33m2. 修改节点\033[0m"
|
||||
echo -e "\033[33m3. 导出所有节点\033[0m"
|
||||
echo -e "\033[33m4. 新增节点(自动添加未配置的IP)\033[0m"
|
||||
echo -e "\033[33m0. 退出\033[0m"
|
||||
echo -e "\033[36m==========================\033[0m"
|
||||
|
||||
read -p "请输入选项 [0-4]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
if check_existing_nodes; then
|
||||
echo -e "\033[31m警告: 检测到已有节点配置!\033[0m"
|
||||
echo -e "\033[31m选择此选项将会清空所有现有节点并重新部署所有IP的节点\033[0m"
|
||||
echo -e "\033[31m如果您只想添加新的IP节点,请使用选项4\033[0m"
|
||||
read -p "是否确认清空所有节点并重新部署? (y/n): " confirm
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
echo "已取消操作"
|
||||
show_menu
|
||||
return
|
||||
fi
|
||||
fi
|
||||
configure_xray
|
||||
restart_xray
|
||||
echo "节点部署完成"
|
||||
;;
|
||||
2)
|
||||
echo "请确保 /home/xiugai.txt 文件中包含需要修改的IP地址列表,每行一个IP"
|
||||
read -p "是否继续修改? (y/n): " confirm
|
||||
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
|
||||
modify_by_ip
|
||||
restart_xray
|
||||
echo "节点修改完成"
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
export_all_nodes
|
||||
;;
|
||||
4)
|
||||
add_new_nodes
|
||||
if [ $? -eq 0 ]; then
|
||||
restart_xray
|
||||
echo "新节点添加完成"
|
||||
fi
|
||||
;;
|
||||
0)
|
||||
echo "退出程序"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "无效选项,请重新选择"
|
||||
show_menu
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 检查是否已有节点配置
|
||||
if check_existing_nodes; then
|
||||
echo "检测到已有节点配置,跳过依赖安装..."
|
||||
show_menu
|
||||
else
|
||||
echo "未检测到节点配置,开始安装必要依赖..."
|
||||
install_jq
|
||||
install_xray
|
||||
show_menu
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
270
proxy/vmess-sk5.sh
Normal file
270
proxy/vmess-sk5.sh
Normal file
@@ -0,0 +1,270 @@
|
||||
#!/bin/bash
|
||||
|
||||
red='\e[31m'
|
||||
yellow='\e[33m'
|
||||
green='\e[32m'
|
||||
none='\e[0m'
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
default_config='
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 9999,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "sky22333"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "none",
|
||||
"wsSettings": {
|
||||
"path": "/sky22333"
|
||||
}
|
||||
},
|
||||
"tag": "inbound0"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.2",
|
||||
"port": 2222,
|
||||
"users": [
|
||||
{
|
||||
"user": "admin123",
|
||||
"pass": "admin333"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": "outbound0"
|
||||
}
|
||||
],
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["inbound0"],
|
||||
"outboundTag": "outbound0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
# 检查并安装curl
|
||||
check_and_install_curl() {
|
||||
if ! type curl &>/dev/null; then
|
||||
echo -e "${yellow}正在安装curl...${none}"
|
||||
apt update && apt install -yq curl
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装jq
|
||||
check_and_install_jq() {
|
||||
if ! type jq &>/dev/null; then
|
||||
echo -e "${yellow}正在安装jq...${none}"
|
||||
apt update && apt install -yq jq
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装uuid-runtime
|
||||
check_and_install_uuid_runtime() {
|
||||
if ! type uuidgen &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 uuid-runtime...${none}"
|
||||
apt update && apt install -yq uuid-runtime
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查并安装xray
|
||||
check_and_install_xray() {
|
||||
if ! type xray &>/dev/null; then
|
||||
echo -e "${yellow}正在安装 xray...${none}"
|
||||
bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已存在入站配置
|
||||
check_existing_inbound_config() {
|
||||
if grep -q '"tag":' "$config_file"; then
|
||||
return 0 # 已存在入站配置
|
||||
else
|
||||
return 1 # 不存在入站配置
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建默认配置文件
|
||||
create_default_config() {
|
||||
if ! check_existing_inbound_config; then
|
||||
echo "$default_config" > "$config_file"
|
||||
echo -e "${green}已创建默认配置文件。${none}"
|
||||
else
|
||||
echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取本机公网 IP
|
||||
get_local_ip() {
|
||||
local ip=$(curl -s http://ipinfo.io/ip)
|
||||
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$ip"
|
||||
else
|
||||
echo "无法自动获取公网IP地址,请手动输入。"
|
||||
read -p "请输入您的公网IP地址: " manual_ip
|
||||
if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "$manual_ip"
|
||||
else
|
||||
echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示所有入站配置和 Vmess 链接以及对应的出站配置(出战只显示地址、端口、用户名和密码)
|
||||
show_inbound_configs() {
|
||||
local local_ip=$(get_local_ip) # 获取本机IP
|
||||
|
||||
local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file")
|
||||
local outbounds=$(jq '.outbounds' "$config_file")
|
||||
echo -e "${green}入站节点配置:${none}"
|
||||
|
||||
local length=$(jq '. | length' <<< "$config")
|
||||
for ((i = 0; i < length; i++)); do
|
||||
local port=$(jq -r ".[$i].port" <<< "$config")
|
||||
local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config")
|
||||
local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config")
|
||||
|
||||
# 将节点地址设置为本机IP
|
||||
local node_address="$local_ip"
|
||||
|
||||
local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"节点$(($i + 1))\",\"add\":\"$node_address\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)"
|
||||
|
||||
echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Vmess 链接: ${vmess_link}"
|
||||
|
||||
# 构造出站配置的标签
|
||||
local outbound_tag="outbound$port"
|
||||
|
||||
# 根据构造的标签查找对应的出站配置
|
||||
local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds")
|
||||
|
||||
if [[ ! -z $outbound_config ]]; then
|
||||
echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")"
|
||||
else
|
||||
echo -e "${red}未找到对应的出站配置。${none}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 添加新节点
|
||||
add_new_nodes() {
|
||||
read -p "请输入要添加的节点数量: " num_nodes
|
||||
if ! [[ $num_nodes =~ ^[0-9]+$ ]]; then
|
||||
echo -e "${red}错误!${none} 请输入有效的数量。\n"
|
||||
return
|
||||
fi
|
||||
|
||||
local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file")
|
||||
local start_port=$((max_port+1))
|
||||
|
||||
for ((i=0; i<num_nodes; i++)); do
|
||||
local new_port=$((start_port+i))
|
||||
local new_tag="inbound$new_port"
|
||||
local new_outbound_tag="outbound$new_port"
|
||||
local new_id=$(uuidgen)
|
||||
|
||||
# 用户输入出站代理信息
|
||||
echo "配置第 $((i+1)) 个sk5出站 (入站端口是$new_port)"
|
||||
read -p "请输入socks5出站地址, 端口, 用户名, 密码 (按顺序以空格分隔): " outbound_addr outbound_port outbound_user outbound_pass
|
||||
|
||||
# 添加入站配置(入站地址设置为 "0.0.0.0")
|
||||
jq --argjson port "$new_port" --arg id "$new_id" --arg tag "$new_tag" '
|
||||
.inbounds += [{
|
||||
listen: "0.0.0.0",
|
||||
port: $port,
|
||||
protocol: "vmess",
|
||||
settings: { clients: [{ id: $id }] },
|
||||
streamSettings: { network: "ws", security: "none", wsSettings: { path: "/websocket" } },
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加出站配置
|
||||
jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" '
|
||||
.outbounds += [{
|
||||
protocol: "socks",
|
||||
settings: { servers: [{ address: $addr, port: $port, users: [{ user: $user, pass: $pass }] }] },
|
||||
tag: $tag
|
||||
}]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 添加路由规则
|
||||
jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" '
|
||||
.routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }]
|
||||
' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
done
|
||||
|
||||
echo -e "${green}已成功添加 $num_nodes 个节点。${none}"
|
||||
systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 删除特定端口号的节点
|
||||
delete_node_by_port() {
|
||||
read -p "请输入要删除的vmess节点端口号: " port_to_delete
|
||||
if ! [[ $port_to_delete =~ ^[0-9]+$ ]]; then
|
||||
echo -e "${red}错误!${none} 请输入有效的端口号。\n"
|
||||
return
|
||||
fi
|
||||
|
||||
local inbound_tag="inbound$port_to_delete"
|
||||
local outbound_tag="outbound$port_to_delete"
|
||||
|
||||
# 删除入站配置
|
||||
jq --argjson port "$port_to_delete" 'del(.inbounds[] | select(.port == $port))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 删除出站配置
|
||||
jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
# 删除路由规则
|
||||
jq --arg inTag "$inbound_tag" --arg outTag "$outbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag and .outboundTag == $outTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
|
||||
|
||||
echo -e "${green}已成功删除端口号为 $port_to_delete 的节点。${none}"
|
||||
systemctl restart xray
|
||||
echo -e "${green}Xray 服务已重新启动。${none}"
|
||||
}
|
||||
|
||||
# 主菜单
|
||||
main_menu() {
|
||||
while true; do
|
||||
echo -e "\n${green}sky22333-快速批量搭建二级代理脚本-管理菜单:${none}"
|
||||
echo "1. 查看所有节点"
|
||||
echo "2. 新增vmess入站sk5出站"
|
||||
echo "3. 删除节点"
|
||||
echo "4. 退出"
|
||||
read -p "请输入选项: " choice
|
||||
|
||||
case $choice in
|
||||
1) show_inbound_configs ;;
|
||||
2) add_new_nodes ;;
|
||||
3) delete_node_by_port ;;
|
||||
4) break ;;
|
||||
*) echo -e "${red}无效的选项,请重新选择。${none}" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 调用主菜单函数
|
||||
check_and_install_curl
|
||||
check_and_install_jq
|
||||
check_and_install_uuid_runtime
|
||||
check_and_install_xray
|
||||
create_default_config
|
||||
get_local_ip
|
||||
main_menu
|
||||
86
proxy/vmess.sh
Normal file
86
proxy/vmess.sh
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义变量
|
||||
SERVER_IP="192.168.12.23" # 目标服务器的IP地址
|
||||
SERVER_PASSWORD="password" # 目标服务器的登录密码
|
||||
NODE_NAME="美国独享" # 用于标识节点的名称
|
||||
TARGET_DIR="/home/xray.txt"
|
||||
|
||||
green='\e[32m'
|
||||
none='\e[0m'
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
# 检查并安装依赖项
|
||||
install_dependencies() {
|
||||
if ! type jq &>/dev/null || ! type uuidgen &>/dev/null || ! type sshpass &>/dev/null; then
|
||||
echo -e "${green}正在安装 jq, uuid-runtime 和 sshpass...${none}"
|
||||
apt update && apt install -yq jq uuid-runtime sshpass
|
||||
fi
|
||||
|
||||
if ! type xray &>/dev/null; then
|
||||
echo -e "${green}正在安装 xray...${none}"
|
||||
bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成配置和传输逻辑
|
||||
configure_and_transfer() {
|
||||
PORT=$(shuf -i 10000-65535 -n 1)
|
||||
UUID=$(uuidgen)
|
||||
RANDOM_PATH=$(cat /dev/urandom | tr -dc 'a-z' | head -c 6)
|
||||
|
||||
cat > "$config_file" << EOF
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": $PORT,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "$UUID"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"wsSettings": {
|
||||
"path": "/$RANDOM_PATH"
|
||||
}
|
||||
},
|
||||
"listen": "0.0.0.0"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"routing": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["inbound0"],
|
||||
"outboundTag": "direct"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
local ip=$(curl -s http://ipinfo.io/ip)
|
||||
local config="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$NODE_NAME\",\"add\":\"$ip\",\"port\":$PORT,\"id\":\"$UUID\",\"aid\":\"0\",\"net\":\"ws\",\"path\":\"/$RANDOM_PATH\",\"type\":\"none\",\"host\":\"\",\"tls\":\"\"}" | base64 -w 0)"
|
||||
echo -e "${green}Vmess-ws节点链接:${none}"
|
||||
echo $config
|
||||
|
||||
echo $config > /tmp/xray_config.txt
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no root@$SERVER_IP "cat >> $TARGET_DIR" < /tmp/xray_config.txt
|
||||
}
|
||||
|
||||
# 主执行逻辑
|
||||
install_dependencies
|
||||
configure_and_transfer
|
||||
systemctl restart xray
|
||||
systemctl enable xray
|
||||
echo -e "${green}Xray 服务已启动。${none}"
|
||||
182
proxy/xray.sh
Normal file
182
proxy/xray.sh
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Xray 安装脚本(极简版)
|
||||
# 固定版本 v1.8.4
|
||||
# 支持通过 -p 参数设置 GitHub 加速前缀(如 https://gh-proxy.com/)
|
||||
# 仅适用于 Linux 系统,需 root 权限
|
||||
|
||||
XRAY_VERSION="v1.8.4"
|
||||
XRAY_BIN_URL="github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}/Xray-linux-64.zip"
|
||||
INSTALL_PATH="/usr/local/bin/xray"
|
||||
SERVICE_PATH="/etc/systemd/system/xray.service"
|
||||
CONFIG_PATH="/usr/local/etc/xray/config.json"
|
||||
|
||||
# github文件加速前缀
|
||||
GH_PROXY="https://gh-proxy.com"
|
||||
|
||||
show_help() {
|
||||
echo "用法: $0 [-p <gh-proxy前缀>] [-u|--uninstall]"
|
||||
echo " -p (可选)GitHub 文件加速前缀,如 https://gh-proxy.com"
|
||||
echo " -u, --uninstall 卸载 Xray 及所有相关文件和服务"
|
||||
echo "此脚本会自动下载安装 Xray ${XRAY_VERSION},并注册 systemd 服务。"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 检查 root 权限
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "请以 root 用户运行此脚本。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 解析参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-p)
|
||||
shift
|
||||
GH_PROXY="$1"
|
||||
;;
|
||||
-u|--uninstall)
|
||||
echo "正在卸载 Xray ..."
|
||||
systemctl stop xray 2>/dev/null
|
||||
systemctl disable xray 2>/dev/null
|
||||
rm -f /usr/local/bin/xray
|
||||
rm -rf /usr/local/etc/xray
|
||||
rm -f /etc/systemd/system/xray.service
|
||||
rm -rf /var/log/xray
|
||||
systemctl daemon-reload
|
||||
echo "Xray 及相关文件已卸载。"
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 自动安装依赖(curl 和 unzip)
|
||||
install_pkg() {
|
||||
PKG_NAME="$1"
|
||||
if command -v apt >/dev/null 2>&1; then
|
||||
apt update && apt install -y "$PKG_NAME"
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf install -y "$PKG_NAME"
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y "$PKG_NAME"
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
zypper install -y "$PKG_NAME"
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
pacman -Sy --noconfirm "$PKG_NAME"
|
||||
elif command -v emerge >/dev/null 2>&1; then
|
||||
emerge -qv "$PKG_NAME"
|
||||
else
|
||||
echo "未检测到支持的包管理器,请手动安装 $PKG_NAME 后重试。"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for cmd in curl unzip; do
|
||||
if ! command -v $cmd >/dev/null 2>&1; then
|
||||
echo "缺少依赖: $cmd,正在尝试自动安装..."
|
||||
install_pkg "$cmd"
|
||||
if ! command -v $cmd >/dev/null 2>&1; then
|
||||
echo "$cmd 安装失败,请手动安装后重试。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
ZIP_FILE="$TMP_DIR/xray.zip"
|
||||
|
||||
# 拼接加速前缀
|
||||
if [[ -n "$GH_PROXY" ]]; then
|
||||
DOWNLOAD_URL="${GH_PROXY%/}/$XRAY_BIN_URL"
|
||||
else
|
||||
DOWNLOAD_URL="https://$XRAY_BIN_URL"
|
||||
fi
|
||||
|
||||
echo "下载 Xray: $DOWNLOAD_URL"
|
||||
curl -L -o "$ZIP_FILE" "$DOWNLOAD_URL"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "下载失败,请检查网络或加速前缀。"
|
||||
rm -rf "$TMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unzip -q "$ZIP_FILE" -d "$TMP_DIR"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "解压失败。"
|
||||
rm -rf "$TMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -m 755 "$TMP_DIR/xray" "$INSTALL_PATH"
|
||||
|
||||
# 生成 systemd 服务文件(与原脚本一致,自动适配 User 和权限)
|
||||
INSTALL_USER="root"
|
||||
if [[ -f '/usr/local/bin/xray' ]]; then
|
||||
# 若已存在旧服务文件,尝试读取 User 字段
|
||||
OLD_USER=$(grep '^[ \t]*User[ \t]*=' /etc/systemd/system/xray.service 2>/dev/null | tail -n 1 | awk -F = '{print $2}' | awk '{print $1}')
|
||||
if [[ -n "$OLD_USER" ]]; then
|
||||
INSTALL_USER="$OLD_USER"
|
||||
fi
|
||||
fi
|
||||
if ! id "$INSTALL_USER" >/dev/null 2>&1; then
|
||||
INSTALL_USER="root"
|
||||
fi
|
||||
INSTALL_USER_UID=$(id -u "$INSTALL_USER")
|
||||
|
||||
# 权限相关字段
|
||||
temp_CapabilityBoundingSet="CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE"
|
||||
temp_AmbientCapabilities="AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE"
|
||||
temp_NoNewPrivileges="NoNewPrivileges=true"
|
||||
if [[ "$INSTALL_USER_UID" -eq 0 ]]; then
|
||||
temp_CapabilityBoundingSet="#${temp_CapabilityBoundingSet}"
|
||||
temp_AmbientCapabilities="#${temp_AmbientCapabilities}"
|
||||
temp_NoNewPrivileges="#${temp_NoNewPrivileges}"
|
||||
fi
|
||||
|
||||
cat > "$SERVICE_PATH" <<EOF
|
||||
[Unit]
|
||||
Description=Xray Service
|
||||
Documentation=https://github.com/xtls
|
||||
After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
User=$INSTALL_USER
|
||||
${temp_CapabilityBoundingSet}
|
||||
${temp_AmbientCapabilities}
|
||||
${temp_NoNewPrivileges}
|
||||
ExecStart=$INSTALL_PATH run -config $CONFIG_PATH
|
||||
Restart=on-failure
|
||||
RestartPreventExitStatus=23
|
||||
LimitNPROC=10000
|
||||
LimitNOFILE=1000000
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
chmod 644 "$SERVICE_PATH"
|
||||
systemctl daemon-reload
|
||||
|
||||
# 生成最简配置文件
|
||||
mkdir -p "$(dirname $CONFIG_PATH)"
|
||||
echo '{}' > "$CONFIG_PATH"
|
||||
|
||||
# 启动并设置开机自启
|
||||
systemctl enable xray
|
||||
systemctl restart xray
|
||||
sleep 1
|
||||
if systemctl is-active --quiet xray; then
|
||||
echo "Xray ${XRAY_VERSION} 安装并启动成功。"
|
||||
else
|
||||
echo "Xray 启动失败,请检查日志。"
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
rm -rf "$TMP_DIR"
|
||||
629
proxy/zhanqun.sh
Normal file
629
proxy/zhanqun.sh
Normal file
@@ -0,0 +1,629 @@
|
||||
#!/bin/bash
|
||||
# 站群多IP源进源出节点脚本 支持sk5和vless+tcp协议
|
||||
|
||||
# 生成随机8位数的用户名和密码
|
||||
generate_random_string() {
|
||||
local length=8
|
||||
tr -dc A-Za-z0-9 </dev/urandom | head -c $length
|
||||
}
|
||||
|
||||
# 生成随机UUID
|
||||
generate_uuid() {
|
||||
cat /proc/sys/kernel/random/uuid
|
||||
}
|
||||
|
||||
# 获取当前北京时间,精确到秒
|
||||
get_beijing_time() {
|
||||
TZ=Asia/Shanghai date +"%Y年%m月%d日%H点%M分%S秒"
|
||||
}
|
||||
|
||||
# 全局变量,保存当前操作使用的输出文件名
|
||||
OUTPUT_FILE=""
|
||||
|
||||
# 初始化输出文件名
|
||||
init_output_file() {
|
||||
OUTPUT_FILE="/home/$(get_beijing_time).txt"
|
||||
# 确保文件存在并清空内容
|
||||
touch "$OUTPUT_FILE"
|
||||
> "$OUTPUT_FILE"
|
||||
echo "将使用输出文件: $OUTPUT_FILE"
|
||||
}
|
||||
|
||||
install_jq() {
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "jq 未安装,正在安装 jq..."
|
||||
if [[ -f /etc/debian_version ]]; then
|
||||
apt update && apt install -yq jq
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
yum install -y epel-release jq
|
||||
else
|
||||
echo "无法确定系统发行版,请手动安装 jq。"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "jq 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
install_xray() {
|
||||
if ! command -v xray &> /dev/null; then
|
||||
echo "Xray 未安装,正在安装 Xray..."
|
||||
if ! bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4; then
|
||||
echo "Xray 安装失败,请检查网络连接或安装脚本。"
|
||||
exit 1
|
||||
fi
|
||||
echo "Xray 安装完成。"
|
||||
else
|
||||
echo "Xray 已安装。"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已有节点配置
|
||||
check_existing_nodes() {
|
||||
local config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
# 如果配置文件不存在,则没有节点配置
|
||||
if [ ! -f "$config_file" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查inbounds数量是否大于0
|
||||
local inbounds_count=$(jq '.inbounds | length' "$config_file" 2>/dev/null)
|
||||
|
||||
# 如果jq命令失败或inbounds为空,则认为没有节点配置
|
||||
if [ -z "$inbounds_count" ] || [ "$inbounds_count" -eq 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 有节点配置
|
||||
return 0
|
||||
}
|
||||
|
||||
get_public_ipv4() {
|
||||
ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}'
|
||||
}
|
||||
|
||||
# 获取已配置的IP列表
|
||||
get_configured_ips() {
|
||||
local config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
jq -r '.outbounds[] | .sendThrough' "$config_file" | sort | uniq
|
||||
}
|
||||
|
||||
print_node_info() {
|
||||
local ip=$1
|
||||
local socks_port=$2
|
||||
local vless_port=$3
|
||||
local username=$4
|
||||
local password=$5
|
||||
local uuid=$6
|
||||
|
||||
echo -e " IP: \033[32m$ip\033[0m"
|
||||
echo -e " Socks5 端口: \033[32m$socks_port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m"
|
||||
echo -e " VLESS 端口: \033[32m$vless_port\033[0m UUID: \033[32m$uuid\033[0m"
|
||||
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
|
||||
# 保存节点信息到文件
|
||||
echo "$ip:$socks_port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
echo "节点信息已保存到 $OUTPUT_FILE"
|
||||
}
|
||||
|
||||
# 导出所有节点配置
|
||||
export_all_nodes() {
|
||||
local config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "Xray配置文件不存在,无法导出节点信息。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
echo "正在导出所有节点配置到 $OUTPUT_FILE..."
|
||||
|
||||
# 获取所有Socks5节点
|
||||
local socks_nodes=$(jq -r '.inbounds[] | select(.protocol == "socks") | {port: .port, tag: .tag}' "$config_file")
|
||||
|
||||
if [ -z "$socks_nodes" ]; then
|
||||
echo "未找到任何节点配置。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 遍历所有Socks5节点,查找对应的信息
|
||||
for row in $(jq -r '.inbounds[] | select(.protocol == "socks") | @base64' "$config_file"); do
|
||||
inbound=$(echo $row | base64 --decode)
|
||||
|
||||
local port=$(echo "$inbound" | jq -r '.port')
|
||||
local tag=$(echo "$inbound" | jq -r '.tag')
|
||||
|
||||
# 查找对应的outbound以获取IP
|
||||
local outbound_tag="out-$port"
|
||||
local ip=$(jq -r --arg tag "$outbound_tag" '.outbounds[] | select(.tag == $tag) | .sendThrough' "$config_file")
|
||||
|
||||
# 获取Socks5的用户名和密码
|
||||
local username=$(echo "$inbound" | jq -r '.settings.accounts[0].user')
|
||||
local password=$(echo "$inbound" | jq -r '.settings.accounts[0].pass')
|
||||
|
||||
# 查找相应的VLESS节点
|
||||
local vless_port=$((port + 1))
|
||||
local vless_tag="in-$vless_port"
|
||||
|
||||
# 获取VLESS的UUID
|
||||
local uuid=$(jq -r --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .settings.clients[0].id' "$config_file")
|
||||
|
||||
if [ -n "$uuid" ]; then
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
|
||||
# 输出节点信息
|
||||
echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
echo -e "已导出节点: \033[32m$ip\033[0m Socks5端口:\033[32m$port\033[0m VLESS端口:\033[32m$vless_port\033[0m"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "所有节点导出完成,信息已保存到 $OUTPUT_FILE"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 查找配置中未使用的端口号
|
||||
find_next_unused_port() {
|
||||
local config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "10001" # 如果配置文件不存在,从10001开始
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取所有已使用的端口
|
||||
local used_ports=$(jq -r '.inbounds[].port' "$config_file" | sort -n)
|
||||
|
||||
if [ -z "$used_ports" ]; then
|
||||
echo "10001" # 如果没有已使用的端口,从10001开始
|
||||
return
|
||||
fi
|
||||
|
||||
# 获取最大的端口号并加1
|
||||
local max_port=$(echo "$used_ports" | tail -1)
|
||||
local next_port=$((max_port + 1))
|
||||
|
||||
# 确保端口号是奇数(用于socks5)
|
||||
if [ $((next_port % 2)) -eq 0 ]; then
|
||||
next_port=$((next_port + 1))
|
||||
fi
|
||||
|
||||
echo "$next_port"
|
||||
}
|
||||
|
||||
# 添加新节点(只添加未配置的IP)
|
||||
add_new_nodes() {
|
||||
# 获取当前系统的所有公网IP
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到公网IP地址,退出..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取已经配置的IP列表
|
||||
configured_ips=($(get_configured_ips))
|
||||
|
||||
# 初始化新IP列表
|
||||
new_ips=()
|
||||
|
||||
# 比对IP,找出未配置的IP
|
||||
for ip in "${public_ips[@]}"; do
|
||||
is_configured=false
|
||||
for configured_ip in "${configured_ips[@]}"; do
|
||||
if [[ "$ip" == "$configured_ip" ]]; then
|
||||
is_configured=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $is_configured; then
|
||||
new_ips+=("$ip")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查是否有新的IP需要配置
|
||||
if [[ ${#new_ips[@]} -eq 0 ]]; then
|
||||
echo "所有IP都已配置,无需添加新节点。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "发现 ${#new_ips[@]} 个未配置的IP: ${new_ips[@]}"
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
# 获取配置文件路径
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
# 如果配置文件不存在,创建基础配置
|
||||
if [ ! -f "$config_file" ]; then
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"routing": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# 获取下一个可用的端口
|
||||
socks_port=$(find_next_unused_port)
|
||||
|
||||
echo "将从端口 $socks_port 开始配置新节点"
|
||||
|
||||
# 为每个新IP配置节点
|
||||
for ip in "${new_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip"
|
||||
|
||||
# Socks5配置 (奇数端口)
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
|
||||
# VLESS配置 (偶数端口)
|
||||
vless_port=$((socks_port + 1))
|
||||
uuid=$(generate_uuid)
|
||||
|
||||
# 添加Socks5配置
|
||||
jq --argjson port "$socks_port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "password",
|
||||
"accounts": [{
|
||||
"user": $username,
|
||||
"pass": $password
|
||||
}],
|
||||
"udp": true,
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 添加VLESS配置
|
||||
jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"clients": [{
|
||||
"id": $uuid,
|
||||
"level": 0
|
||||
}],
|
||||
"decryption": "none"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 输出节点信息
|
||||
print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid"
|
||||
|
||||
# 增加端口号,为下一个IP准备
|
||||
socks_port=$((vless_port + 1))
|
||||
done
|
||||
|
||||
echo "新节点配置完成,共添加了 ${#new_ips[@]} 个节点"
|
||||
return 0
|
||||
}
|
||||
|
||||
configure_xray() {
|
||||
public_ips=($(get_public_ipv4))
|
||||
|
||||
if [[ ${#public_ips[@]} -eq 0 ]]; then
|
||||
echo "未找到额外IP地址,退出..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "找到的公网 IPv4 地址: ${public_ips[@]}"
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
config_file="/usr/local/etc/xray/config.json"
|
||||
|
||||
# 创建基础配置文件
|
||||
cat > $config_file <<EOF
|
||||
{
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"routing": {
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 初始端口
|
||||
socks_port=10001
|
||||
|
||||
# 配置 inbounds 和 outbounds
|
||||
for ip in "${public_ips[@]}"; do
|
||||
echo "正在配置 IP: $ip"
|
||||
|
||||
# Socks5配置 (奇数端口)
|
||||
username=$(generate_random_string)
|
||||
password=$(generate_random_string)
|
||||
|
||||
# VLESS配置 (偶数端口)
|
||||
vless_port=$((socks_port + 1))
|
||||
uuid=$(generate_uuid)
|
||||
|
||||
# 添加Socks5配置
|
||||
jq --argjson port "$socks_port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "password",
|
||||
"accounts": [{
|
||||
"user": $username,
|
||||
"pass": $password
|
||||
}],
|
||||
"udp": true,
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 添加VLESS配置
|
||||
jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{
|
||||
"port": $port,
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"clients": [{
|
||||
"id": $uuid,
|
||||
"level": 0
|
||||
}],
|
||||
"decryption": "none"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
},
|
||||
"tag": ("in-\($port)")
|
||||
}] | .outbounds += [{
|
||||
"protocol": "freedom",
|
||||
"settings": {},
|
||||
"sendThrough": $ip,
|
||||
"tag": ("out-\($port)")
|
||||
}] | .routing.rules += [{
|
||||
"type": "field",
|
||||
"inboundTag": ["in-\($port)"],
|
||||
"outboundTag": "out-\($port)"
|
||||
}]' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 输出节点信息
|
||||
print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid"
|
||||
|
||||
# 增加端口号,为下一个IP准备
|
||||
socks_port=$((vless_port + 1))
|
||||
done
|
||||
|
||||
echo "Xray 配置完成。"
|
||||
}
|
||||
|
||||
modify_by_ip() {
|
||||
local modify_file="/home/xiugai.txt"
|
||||
|
||||
if [ ! -f "$modify_file" ]; then
|
||||
echo "修改文件 $modify_file 不存在,跳过修改操作。"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "检测到修改文件,开始根据IP修改节点..."
|
||||
|
||||
# 读取当前配置
|
||||
local config_file="/usr/local/etc/xray/config.json"
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "Xray配置文件不存在,请先配置Xray。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 初始化输出文件
|
||||
init_output_file
|
||||
|
||||
local modify_success=false
|
||||
|
||||
# 逐行读取修改文件中的IP
|
||||
while IFS= read -r ip || [[ -n "$ip" ]]; do
|
||||
# 跳过空行和注释行
|
||||
[[ -z "$ip" || "$ip" =~ ^# ]] && continue
|
||||
|
||||
echo "正在处理IP: $ip"
|
||||
|
||||
# 查找此IP对应的出站配置
|
||||
local ip_exists=$(jq --arg ip "$ip" '.outbounds[] | select(.sendThrough == $ip) | .tag' "$config_file")
|
||||
|
||||
if [[ -z "$ip_exists" ]]; then
|
||||
echo "错误: IP $ip 在当前配置中未找到,停止脚本执行。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 找到对应的入站端口和标签
|
||||
local outbound_tags=$(jq -r --arg ip "$ip" '.outbounds[] | select(.sendThrough == $ip) | .tag' "$config_file")
|
||||
|
||||
for outbound_tag in $outbound_tags; do
|
||||
local port=$(echo $outbound_tag | cut -d'-' -f2)
|
||||
local inbound_tag="in-$port"
|
||||
|
||||
# 检查协议类型
|
||||
local protocol=$(jq -r --arg tag "$inbound_tag" '.inbounds[] | select(.tag == $tag) | .protocol' "$config_file")
|
||||
|
||||
if [[ "$protocol" == "socks" ]]; then
|
||||
# 更新socks协议的用户名和密码
|
||||
local username=$(generate_random_string)
|
||||
local password=$(generate_random_string)
|
||||
|
||||
jq --arg tag "$inbound_tag" --arg username "$username" --arg password "$password" '
|
||||
.inbounds[] |= if .tag == $tag then
|
||||
.settings.accounts[0].user = $username |
|
||||
.settings.accounts[0].pass = $password
|
||||
else . end' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 找到对应的vless端口
|
||||
local vless_port=$((port + 1))
|
||||
local vless_tag="in-$vless_port"
|
||||
|
||||
# 确认vless端口存在
|
||||
local vless_exists=$(jq --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .tag' "$config_file")
|
||||
|
||||
# 如果存在,更新vless协议的UUID
|
||||
if [[ -n "$vless_exists" ]]; then
|
||||
local uuid=$(generate_uuid)
|
||||
|
||||
jq --arg tag "$vless_tag" --arg uuid "$uuid" '
|
||||
.inbounds[] |= if .tag == $tag then
|
||||
.settings.clients[0].id = $uuid
|
||||
else . end' "$config_file" > temp.json && mv temp.json "$config_file"
|
||||
|
||||
# 构建vless链接,使用IP作为备注
|
||||
local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip"
|
||||
|
||||
# 保存修改后的节点信息
|
||||
echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE"
|
||||
|
||||
echo "已修改 IP: $ip 的Socks5(端口:$port)和VLESS(端口:$vless_port)配置"
|
||||
modify_success=true
|
||||
else
|
||||
echo "警告: 未找到IP $ip 对应的VLESS配置"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done < "$modify_file"
|
||||
|
||||
if $modify_success; then
|
||||
echo "节点修改完成,信息已保存到 $OUTPUT_FILE"
|
||||
else
|
||||
echo "未进行任何修改"
|
||||
fi
|
||||
}
|
||||
|
||||
restart_xray() {
|
||||
echo "正在重启 Xray 服务..."
|
||||
if ! systemctl restart xray; then
|
||||
echo "Xray 服务重启失败,请检查配置文件。"
|
||||
exit 1
|
||||
fi
|
||||
systemctl enable xray
|
||||
echo "Xray 服务已重启。"
|
||||
}
|
||||
|
||||
# 显示交互式菜单
|
||||
show_menu() {
|
||||
echo -e "\n\033[36m==== 站群多IP节点管理菜单 ====\033[0m"
|
||||
echo -e "\033[33m1. 部署节点(首次部署)\033[0m"
|
||||
echo -e "\033[33m2. 修改节点\033[0m"
|
||||
echo -e "\033[33m3. 导出所有节点\033[0m"
|
||||
echo -e "\033[33m4. 新增节点(自动添加未配置的IP)\033[0m"
|
||||
echo -e "\033[33m0. 退出\033[0m"
|
||||
echo -e "\033[36m==========================\033[0m"
|
||||
|
||||
read -p "请输入选项 [0-4]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
if check_existing_nodes; then
|
||||
echo -e "\033[31m警告: 检测到已有节点配置!\033[0m"
|
||||
echo -e "\033[31m选择此选项将会清空所有现有节点并重新部署所有IP的节点\033[0m"
|
||||
echo -e "\033[31m如果您只想添加新的IP节点,请使用选项4\033[0m"
|
||||
read -p "是否确认清空所有节点并重新部署? (y/n): " confirm
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
echo "已取消操作"
|
||||
show_menu
|
||||
return
|
||||
fi
|
||||
fi
|
||||
configure_xray
|
||||
restart_xray
|
||||
echo "节点部署完成"
|
||||
;;
|
||||
2)
|
||||
echo "请确保 /home/xiugai.txt 文件中包含需要修改的IP地址列表,每行一个IP"
|
||||
read -p "是否继续修改? (y/n): " confirm
|
||||
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
|
||||
modify_by_ip
|
||||
restart_xray
|
||||
echo "节点修改完成"
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
export_all_nodes
|
||||
;;
|
||||
4)
|
||||
add_new_nodes
|
||||
if [ $? -eq 0 ]; then
|
||||
restart_xray
|
||||
echo "新节点添加完成"
|
||||
fi
|
||||
;;
|
||||
0)
|
||||
echo "退出程序"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "无效选项,请重新选择"
|
||||
show_menu
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 检查是否已有节点配置
|
||||
if check_existing_nodes; then
|
||||
echo "检测到已有节点配置,跳过依赖安装..."
|
||||
show_menu
|
||||
else
|
||||
echo "未检测到节点配置,开始安装必要依赖..."
|
||||
install_jq
|
||||
install_xray
|
||||
show_menu
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user