Files
shell/proxy/duosk5-ss2022.sh
starry 1200c37643 add
2025-09-11 16:53:16 +08:00

557 lines
20 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 批量搭建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