mirror of
https://github.com/fscarmen/sing-box.git
synced 2026-08-11 18:25:22 +08:00
2183 lines
95 KiB
Bash
2183 lines
95 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# 当前脚本版本号
|
|
VERSION='v1.1.3'
|
|
|
|
# 各变量默认值
|
|
GH_PROXY='cdn2.cloudflare.now.cc/https://'
|
|
TEMP_DIR='/tmp/sing-box'
|
|
WORK_DIR='/etc/sing-box'
|
|
START_PORT_DEFAULT='8881'
|
|
MIN_PORT=100
|
|
MAX_PORT=65520
|
|
TLS_SERVER_DEFAULT=addons.mozilla.org
|
|
CDN_DEFAULT=cn.azhz.eu.org
|
|
PROTOCOL_LIST=("XTLS + reality" "hysteria2" "tuic" "ShadowTLS" "shadowsocks" "trojan" "vmess + ws" "vless + ws + tls" "H2 + reality" "gRPC + reality")
|
|
NODE_TAG=("xtls-reality" "hysteria2" "tuic" "ShadowTLS" "shadowsocks" "trojan" "vmess-ws" "vless-ws-tls" "h2-reality" "grpc-reality")
|
|
CONSECUTIVE_PORTS=${#PROTOCOL_LIST[@]}
|
|
CDN_DOMAIN=("cn.azhz.eu.org" "www.who.int" "skk.moe" "time.cloudflare.com" "csgo.com")
|
|
|
|
trap "rm -rf $TEMP_DIR >/dev/null 2>&1 ; echo -e '\n' ;exit 1" INT QUIT TERM EXIT
|
|
|
|
mkdir -p $TEMP_DIR
|
|
|
|
E[0]="Language:\n 1. English (default) \n 2. 简体中文"
|
|
C[0]="${E[0]}"
|
|
E[1]="1. Support v2rayN V6.33 Tuic and Hysteria2 protocol URLs; 2. Add DNS module to adapt Sing-box V1.9.0-alpha.8; 3. Reconstruct the installation protocol, add delete protocols and protocol export module, each parameter is more refined. ( Reinstall is required ); 4. Remove obfs obfuscation from Hysteria2."
|
|
C[1]="1. 支持 v2rayN V6.33 Tuic 和 Hysteria2 协议 URL; 2. 增加 DNS 模块以适配 Sing-box V1.9.0-alpha.8; 3. 重构安装协议,增加删除协议及协议输出模块,各参数更精细 (需要重新安装); 4. 去掉 Hysteria2 的 obfs 混淆"
|
|
E[2]="This project is designed to add sing-box support for multiple protocols to VPS, details: [https://github.com/fscarmen/sing-box]\n Script Features:\n\t • Deploy multiple protocols with one click, there is always one for you!\n\t • Custom ports for nat machine with limited open ports.\n\t • Built-in warp chained proxy to unlock chatGPT.\n\t • No domain name is required.\n\t • Support system: Ubuntu, Debian, CentOS, Alpine and Arch Linux 3.\n\t • Support architecture: AMD,ARM and s390x\n"
|
|
C[2]="本项目专为 VPS 添加 sing-box 支持的多种协议, 详细说明: [https://github.com/fscarmen/sing-box]\n 脚本特点:\n\t • 一键部署多协议,总有一款适合你\n\t • 自定义端口,适合有限开放端口的 nat 小鸡\n\t • 内置 warp 链式代理解锁 chatGPT\n\t • 不需要域名\n\t • 智能判断操作系统: Ubuntu 、Debian 、CentOS 、Alpine 和 Arch Linux,请务必选择 LTS 系统\n\t • 支持硬件结构类型: AMD 和 ARM\n"
|
|
E[3]="Input errors up to 5 times.The script is aborted."
|
|
C[3]="输入错误达5次,脚本退出"
|
|
E[4]="UUID should be 36 characters, please re-enter \(\${UUID_ERROR_TIME} times remaining\):"
|
|
C[4]="UUID 应为36位字符,请重新输入 \(剩余\${UUID_ERROR_TIME}次\):"
|
|
E[5]="The script supports Debian, Ubuntu, CentOS, Alpine, Fedora or Arch systems only. Feedback: [https://github.com/fscarmen/sing-box/issues]"
|
|
C[5]="本脚本只支持 Debian、Ubuntu、CentOS、Alpine、Fedora 或 Arch 系统,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[6]="Curren operating system is \$SYS.\\\n The system lower than \$SYSTEM \${MAJOR[int]} is not supported. Feedback: [https://github.com/fscarmen/sing-box/issues]"
|
|
C[6]="当前操作是 \$SYS\\\n 不支持 \$SYSTEM \${MAJOR[int]} 以下系统,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[7]="Install dependence-list:"
|
|
C[7]="安装依赖列表:"
|
|
E[8]="All dependencies already exist and do not need to be installed additionally."
|
|
C[8]="所有依赖已存在,不需要额外安装"
|
|
E[9]="To upgrade, press [y]. No upgrade by default:"
|
|
C[9]="升级请按 [y],默认不升级:"
|
|
E[10]="Please enter VPS IP \(Default is: \${SERVER_IP_DEFAULT}\):"
|
|
C[10]="请输入 VPS IP \(默认为: \${SERVER_IP_DEFAULT}\):"
|
|
E[11]="Please enter the starting port number. Must be \${MIN_PORT} - \${MAX_PORT}, consecutive \${NUM} free ports are required \(Default is: \${START_PORT_DEFAULT}\):"
|
|
C[11]="请输入开始的端口号,必须是 \${MIN_PORT} - \${MAX_PORT},需要连续\${NUM}个空闲的端口 \(默认为: \${START_PORT_DEFAULT}\):"
|
|
E[12]="Please enter UUID \(Default is \${UUID_DEFAULT}\):"
|
|
C[12]="请输入 UUID \(默认为 \${UUID_DEFAULT}\):"
|
|
E[13]="Please enter the node name. \(Default is \${NODE_NAME_DEFAULT}\):"
|
|
C[13]="请输入节点名称 \(默认为 \${NODE_NAME_DEFAULT}\):"
|
|
E[14]="Node name only allow uppercase and lowercase letters and numeric characters, please re-enter \(\${a} times remaining\):"
|
|
C[14]="节点名称只允许英文大小写及数字字符,请重新输入 \(剩余\${a}次\):"
|
|
E[15]="Sing-box script has not been installed yet."
|
|
C[15]="Sing-box 脚本还没有安装"
|
|
E[16]="Sing-box is completely uninstalled."
|
|
C[16]="Sing-box 已彻底卸载"
|
|
E[17]="Version"
|
|
C[17]="脚本版本"
|
|
E[18]="New features"
|
|
C[18]="功能新增"
|
|
E[19]="System infomation"
|
|
C[19]="系统信息"
|
|
E[20]="Operating System"
|
|
C[20]="当前操作系统"
|
|
E[21]="Kernel"
|
|
C[21]="内核"
|
|
E[22]="Architecture"
|
|
C[22]="处理器架构"
|
|
E[23]="Virtualization"
|
|
C[23]="虚拟化"
|
|
E[24]="Choose:"
|
|
C[24]="请选择:"
|
|
E[25]="Curren architecture \$(uname -m) is not supported. Feedback: [https://github.com/fscarmen/sing-box/issues]"
|
|
C[25]="当前架构 \$(uname -m) 暂不支持,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[26]="Not install"
|
|
C[26]="未安装"
|
|
E[27]="close"
|
|
C[27]="关闭"
|
|
E[28]="open"
|
|
C[28]="开启"
|
|
E[29]="View links (sb -n)"
|
|
C[29]="查看节点信息 (sb -n)"
|
|
E[30]="Change listen ports (sb -p)"
|
|
C[30]="更换监听端口 (sb -p)"
|
|
E[31]="Sync Sing-box to the latest version (sb -v)"
|
|
C[31]="同步 Sing-box 至最新版本 (sb -v)"
|
|
E[32]="Upgrade kernel, turn on BBR, change Linux system (sb -b)"
|
|
C[32]="升级内核、安装BBR、DD脚本 (sb -b)"
|
|
E[33]="Uninstall (sb -u)"
|
|
C[33]="卸载 (sb -u)"
|
|
E[34]="Install Sing-box multi-protocol script"
|
|
C[34]="安装 Sing-box 协议全家桶脚本"
|
|
E[35]="Exit"
|
|
C[35]="退出"
|
|
E[36]="Please enter the correct number"
|
|
C[36]="请输入正确数字"
|
|
E[37]="successful"
|
|
C[37]="成功"
|
|
E[38]="failed"
|
|
C[38]="失败"
|
|
E[39]="Sing-box is not installed."
|
|
C[39]="Sing-box 未安装"
|
|
E[40]="Sing-box local verion: \$LOCAL\\\t The newest verion: \$ONLINE"
|
|
C[40]="Sing-box 本地版本: \$LOCAL\\\t 最新版本: \$ONLINE"
|
|
E[41]="No upgrade required."
|
|
C[41]="不需要升级"
|
|
E[42]="Downloading the latest version Sing-box failed, script exits. Feedback:[https://github.com/fscarmen/sing-box/issues]"
|
|
C[42]="下载最新版本 Sing-box 失败,脚本退出,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[43]="The script must be run as root, you can enter sudo -i and then download and run again. Feedback:[https://github.com/fscarmen/sing-box/issues]"
|
|
C[43]="必须以root方式运行脚本,可以输入 sudo -i 后重新下载运行,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[44]="Ports are in used: \${IN_USED[*]}"
|
|
C[44]="正在使用中的端口: \${IN_USED[*]}"
|
|
E[45]="Ports used: \${NOW_START_PORT} - \$((NOW_START_PORT+NOW_CONSECUTIVE_PORTS-1))"
|
|
C[45]="使用端口: \${NOW_START_PORT} - \$((NOW_START_PORT+NOW_CONSECUTIVE_PORTS-1))"
|
|
E[46]="Warp / warp-go was detected to be running. Please enter the correct server IP:"
|
|
C[46]="检测到 warp / warp-go 正在运行,请输入确认的服务器 IP:"
|
|
E[47]="No server ip, script exits. Feedback:[https://github.com/fscarmen/sing-box/issues]"
|
|
C[47]="没有 server ip,脚本退出,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
|
|
E[48]="ShadowTLS - Copy the above two Neko links and manually set up the chained proxies in order. Tutorial: https://github.com/fscarmen/sing-box/blob/main/README.md#sekobox-%E8%AE%BE%E7%BD%AE-shadowtls-%E6%96%B9%E6%B3%95"
|
|
C[48]="ShadowTLS - 复制上面两条 Neko links 进去,并按顺序手动设置链式代理,详细教程: https://github.com/fscarmen/sing-box/blob/main/README.md#sekobox-%E8%AE%BE%E7%BD%AE-shadowtls-%E6%96%B9%E6%B3%95"
|
|
E[49]="Select more protocols to install (e.g. hgbd):\n a. all (default)"
|
|
C[49]="多选需要安装协议(比如 hgbd):\n a. all (默认)"
|
|
E[50]="Please enter the \$TYPE domain name:"
|
|
C[50]="请输入 \$TYPE 域名:"
|
|
E[51]="Please choose or custom a cdn, http support is required:"
|
|
C[51]="请选择或输入 cdn,要求支持 http:"
|
|
E[52]="Please set the ip \[\${WS_SERVER_IP}] to domain \[\${TYPE_HOST_DOMAIN}], and set the origin rule to \[\${TYPE_PORT_WS}] in Cloudflare."
|
|
C[52]="请在 Cloudflare 绑定 \[\${WS_SERVER_IP}] 的域名为 \[\${TYPE_HOST_DOMAIN}], 并设置 origin rule 为 \[\${TYPE_PORT_WS}]"
|
|
E[53]="Please select or enter the preferred domain, the default is \${CDN_DOMAIN[0]}:"
|
|
C[53]="请选择或者填入优选域名,默认为 \${CDN_DOMAIN[0]}:"
|
|
E[54]="The contents of the ShadowTLS configuration file need to be updated for the sing_box kernel."
|
|
C[54]="ShadowTLS 配置文件内容,需要更新 sing_box 内核"
|
|
E[55]="The script runs today: \$TODAY. Total: \$TOTAL"
|
|
C[55]="脚本当天运行次数: \$TODAY,累计运行次数: \$TOTAL"
|
|
E[56]="Process ID"
|
|
C[56]="进程ID"
|
|
E[57]="Runtime"
|
|
C[57]="运行时长"
|
|
E[58]="Memory Usage"
|
|
C[58]="内存占用"
|
|
E[59]="Install ArgoX scripts (argo + xray) [https://github.com/fscarmen/argox]"
|
|
C[59]="安装 ArgoX 脚本 (argo + xray) [https://github.com/fscarmen/argox]"
|
|
E[60]="The order of the selected protocols and ports is as follows:"
|
|
C[60]="选择的协议及端口次序如下:"
|
|
E[61]="(DNS your own domain in Cloudflare is required.)"
|
|
C[61]="(必须在 Cloudflare 解析自有域名)"
|
|
E[62]="Add / Remove protocols (sb -r)"
|
|
C[62]="增加 / 删除协议 (sb -r)"
|
|
E[63]="(1/3) Installed protocols."
|
|
C[63]="(1/3) 已安装的协议"
|
|
E[64]="Please select the protocols to be removed (multiple selections possible):"
|
|
C[64]="请选择需要删除的协议(可以多选):"
|
|
E[65]="(2/3) Uninstalled protocols."
|
|
C[65]="(2/3) 未安装的协议"
|
|
E[66]="Please select the protocols to be added (multiple choices possible):"
|
|
C[66]="请选择需要增加的协议(可以多选):"
|
|
E[67]="(3/3) Confirm all protocols for reloading."
|
|
C[67]="(3/3) 确认重装的所有协议"
|
|
E[68]="Press [n] if there is an error, other keys to continue:"
|
|
C[68]="如有错误请按 [n],其他键继续:"
|
|
E[69]="Install sba scripts (argo + sing-box) [https://github.com/fscarmen/sba]"
|
|
C[69]="安装 sba 脚本 (argo + sing-box) [https://github.com/fscarmen/sba]"
|
|
E[70]="Please set inSecure in tls to true."
|
|
C[70]="请把 tls 里的 inSecure 设置为 true"
|
|
E[71]="Create shortcut [ sb ] successfully."
|
|
C[71]="创建快捷 [ sb ] 指令成功!"
|
|
E[72]="The full template can be found at:\n https://t.me/ztvps/67\n https://github.com/chika0801/sing-box-examples/tree/main/Tun"
|
|
C[72]="完整模板可参照:\n https://t.me/ztvps/67\n https://github.com/chika0801/sing-box-examples/tree/main/Tun"
|
|
E[73]="There is no protocol left, if you are sure please re-run [ sb -u ] to uninstall all."
|
|
C[73]="没有协议剩下,如确定请重新执行 [ sb -u ] 卸载所有"
|
|
E[74]="Keep protocols"
|
|
C[74]="保留协议"
|
|
E[75]="Add protocols"
|
|
C[75]="新增协议"
|
|
E[76]="Install TCP brutal"
|
|
C[76]="安装 TCP brutal"
|
|
E[77]="With sing-box installed, the script exits."
|
|
C[77]="已安装 sing-box ,脚本退出"
|
|
E[78]="Parameter [ $ERROR_PARAMETER ] error, script exits."
|
|
C[78]="[ $ERROR_PARAMETER ] 参数错误,脚本退出"
|
|
|
|
# 自定义字体彩色,read 函数
|
|
warning() { echo -e "\033[31m\033[01m$*\033[0m"; } # 红色
|
|
error() { echo -e "\033[31m\033[01m$*\033[0m" && exit 1; } # 红色
|
|
info() { echo -e "\033[32m\033[01m$*\033[0m"; } # 绿色
|
|
hint() { echo -e "\033[33m\033[01m$*\033[0m"; } # 黄色
|
|
reading() { read -rp "$(info "$1")" "$2"; }
|
|
text() { grep -q '\$' <<< "${E[$*]}" && eval echo "\$(eval echo "\${${L}[$*]}")" || eval echo "\${${L}[$*]}"; }
|
|
|
|
# 自定义友道或谷歌翻译函数
|
|
translate() {
|
|
[ -n "$@" ] && EN="$@"
|
|
ZH=$(wget --no-check-certificate -qO- --tries=1 --timeout=2 "https://translate.google.com/translate_a/t?client=any_client_id_works&sl=en&tl=zh&q=${EN//[[:space:]]/}" 2>/dev/null)
|
|
[[ "$ZH" =~ ^\[\".+\"\]$ ]] && cut -d \" -f2 <<< "$ZH"
|
|
}
|
|
|
|
# 脚本当天及累计运行次数统计
|
|
statistics_of_run-times() {
|
|
local COUNT=$(wget --no-check-certificate -qO- --tries=2 --timeout=2 "https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffscarmen%2Fsing-box%2Fmain%2Fsing-box.sh" 2>&1 | grep -m1 -oE "[0-9]+[ ]+/[ ]+[0-9]+") &&
|
|
TODAY=$(cut -d " " -f1 <<< "$COUNT") &&
|
|
TOTAL=$(cut -d " " -f3 <<< "$COUNT")
|
|
}
|
|
|
|
# 选择中英语言
|
|
select_language() {
|
|
if [ -z "$L" ]; then
|
|
case $(cat $WORK_DIR/language 2>&1) in
|
|
E ) L=E ;;
|
|
C ) L=C ;;
|
|
* ) [ -z "$L" ] && L=E && hint "\n $(text 0) \n" && reading " $(text 24) " LANGUAGE
|
|
[ "$LANGUAGE" = 2 ] && L=C ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
# 字母与数字的 ASCII 码值转换
|
|
asc(){
|
|
if [[ "$1" = [a-z] ]]; then
|
|
[ "$2" = '++' ] && printf "\\$(printf '%03o' "$[ $(printf "%d" "'$1'") + 1 ]")" || printf "%d" "'$1'"
|
|
else
|
|
[[ "$1" =~ ^[0-9]+$ ]] && printf "\\$(printf '%03o' "$1")"
|
|
fi
|
|
}
|
|
|
|
input_cdn() {
|
|
# 提供网上热心网友的anycast域名
|
|
if [[ -z "$CDN" && -n "$VMESS_HOST_DOMAIN$VLESS_HOST_DOMAIN" ]]; then
|
|
echo ""
|
|
for c in "${!CDN_DOMAIN[@]}"; do hint " $[c+1]. ${CDN_DOMAIN[c]} "; done
|
|
|
|
reading "\n $(text 53) " CUSTOM_CDN
|
|
case "$CUSTOM_CDN" in
|
|
[1-${#CDN_DOMAIN[@]}] )
|
|
CDN="${CDN_DOMAIN[$((CUSTOM_CDN-1))]}"
|
|
;;
|
|
?????* )
|
|
CDN="$CUSTOM_CDN"
|
|
;;
|
|
* )
|
|
CDN="${CDN_DOMAIN[0]}"
|
|
esac
|
|
fi
|
|
}
|
|
|
|
check_root() {
|
|
[ "$(id -u)" != 0 ] && error "\n $(text 43) \n"
|
|
}
|
|
|
|
check_arch() {
|
|
# 判断处理器架构
|
|
case $(uname -m) in
|
|
aarch64|arm64 ) SING_BOX_ARCH=arm64 ;;
|
|
x86_64|amd64 ) [[ "$(awk -F ':' '/flags/{print $2; exit}' /proc/cpuinfo)" =~ avx2 ]] && SING_BOX_ARCH=amd64v3 || SING_BOX_ARCH=amd64 ;;
|
|
armv7l ) SING_BOX_ARCH=armv7 ;;
|
|
* ) error " $(text 25) "
|
|
esac
|
|
}
|
|
|
|
# 查安装及运行状态;状态码: 26 未安装, 27 已安装未运行, 28 运行中
|
|
check_install() {
|
|
STATUS=$(text 26) && [ -s /etc/systemd/system/sing-box.service ] && STATUS=$(text 27) && [ "$(systemctl is-active sing-box)" = 'active' ] && STATUS=$(text 28)
|
|
if [[ $STATUS = "$(text 26)" ]] && [ ! -s $WORK_DIR/sing-box ]; then
|
|
{
|
|
local VERSION_LATEST=$(wget --no-check-certificate -qO- "https://api.github.com/repos/SagerNet/sing-box/releases" | awk -F '["v-]' '/tag_name/{print $5}' | sort -r | sed -n '1p')
|
|
local ONLINE=$(wget --no-check-certificate -qO- "https://api.github.com/repos/SagerNet/sing-box/releases" | awk -F '["v]' -v var="tag_name.*$VERSION_LATEST" '$0 ~ var {print $5; exit}')
|
|
ONLINE=${ONLINE:-'1.9.0-alpha.8'}
|
|
wget --no-check-certificate -c https://${GH_PROXY}github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box
|
|
[ -s $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box ] && mv $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box $TEMP_DIR
|
|
}&
|
|
fi
|
|
}
|
|
|
|
# 检测 sing-box 的状态
|
|
check_sing-box_stats(){
|
|
case "$STATUS" in
|
|
"$(text 26)" )
|
|
error "\n Sing-box $(text 28) $(text 38) \n"
|
|
;;
|
|
"$(text 27)" )
|
|
cmd_systemctl enable sing-box && info "\n Sing-box $(text 28) $(text 37) \n" || error "\n Sing-box $(text 28) $(text 38) \n"
|
|
;;
|
|
"$(text 28)" )
|
|
info "\n Sing-box $(text 28) $(text 37) \n"
|
|
esac
|
|
}
|
|
|
|
# 为了适配 alpine,定义 cmd_systemctl 的函数
|
|
cmd_systemctl() {
|
|
local ENABLE_DISABLE=$1
|
|
local APP=$2
|
|
if [ "$ENABLE_DISABLE" = 'enable' ]; then
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
systemctl start $APP
|
|
cat > /etc/local.d/$APP.start << EOF
|
|
#!/usr/bin/env bash
|
|
|
|
systemctl start $APP
|
|
EOF
|
|
chmod +x /etc/local.d/$APP.start
|
|
rc-update add local >/dev/null 2>&1
|
|
else
|
|
systemctl enable --now $APP
|
|
fi
|
|
|
|
elif [ "$ENABLE_DISABLE" = 'disable' ]; then
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
systemctl stop $APP
|
|
rm -f /etc/local.d/$APP.start
|
|
else
|
|
systemctl disable --now $APP
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_system_info() {
|
|
# 判断虚拟化
|
|
if [ $(type -p systemd-detect-virt) ]; then
|
|
VIRT=$(systemd-detect-virt)
|
|
elif [ $(type -p hostnamectl) ]; then
|
|
VIRT=$(hostnamectl | awk '/Virtualization/{print $NF}')
|
|
elif [ $(type -p virt-what) ]; then
|
|
VIRT=$(virt-what)
|
|
fi
|
|
|
|
[ -s /etc/os-release ] && SYS="$(grep -i pretty_name /etc/os-release | cut -d \" -f2)"
|
|
[[ -z "$SYS" && $(type -p hostnamectl) ]] && SYS="$(hostnamectl | grep -i system | cut -d : -f2)"
|
|
[[ -z "$SYS" && $(type -p lsb_release) ]] && SYS="$(lsb_release -sd)"
|
|
[[ -z "$SYS" && -s /etc/lsb-release ]] && SYS="$(grep -i description /etc/lsb-release | cut -d \" -f2)"
|
|
[[ -z "$SYS" && -s /etc/redhat-release ]] && SYS="$(grep . /etc/redhat-release)"
|
|
[[ -z "$SYS" && -s /etc/issue ]] && SYS="$(grep . /etc/issue | cut -d '\' -f1 | sed '/^[ ]*$/d')"
|
|
|
|
REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "amazon linux" "arch linux" "alpine" "fedora")
|
|
RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Arch" "Alpine" "Fedora")
|
|
EXCLUDE=("")
|
|
MAJOR=("9" "16" "7" "7" "3" "" "37")
|
|
PACKAGE_UPDATE=("apt -y update" "apt -y update" "yum -y update" "yum -y update" "pacman -Sy" "apk update -f" "dnf -y update")
|
|
PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "yum -y install" "pacman -S --noconfirm" "apk add --no-cache" "dnf -y install")
|
|
PACKAGE_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove" "yum -y autoremove" "pacman -Rcnsu --noconfirm" "apk del -f" "dnf -y autoremove")
|
|
|
|
for int in "${!REGEX[@]}"; do
|
|
[[ "${SYS,,}" =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && break
|
|
done
|
|
[ -z "$SYSTEM" ] && error " $(text 5) "
|
|
|
|
# 先排除 EXCLUDE 里包括的特定系统,其他系统需要作大发行版本的比较
|
|
for ex in "${EXCLUDE[@]}"; do [[ ! "{$SYS,,}" =~ $ex ]]; done &&
|
|
[[ "$(echo "$SYS" | sed "s/[^0-9.]//g" | cut -d. -f1)" -lt "${MAJOR[int]}" ]] && error " $(text 6) "
|
|
}
|
|
|
|
# 检测 IPv4 IPv6 信息
|
|
check_system_ip() {
|
|
IP4=$(wget -4 -qO- --no-check-certificate --user-agent=Mozilla --tries=2 --timeout=1 http://ip-api.com/json/) &&
|
|
WAN4=$(expr "$IP4" : '.*query\":[ ]*\"\([^"]*\).*') &&
|
|
COUNTRY4=$(expr "$IP4" : '.*country\":[ ]*\"\([^"]*\).*') &&
|
|
ASNORG4=$(expr "$IP4" : '.*isp\":[ ]*\"\([^"]*\).*') &&
|
|
[[ "$L" = C && -n "$COUNTRY4" ]] && COUNTRY4=$(translate "$COUNTRY4")
|
|
|
|
IP6=$(wget -6 -qO- --no-check-certificate --user-agent=Mozilla --tries=2 --timeout=1 https://api.ip.sb/geoip) &&
|
|
WAN6=$(expr "$IP6" : '.*ip\":[ ]*\"\([^"]*\).*') &&
|
|
COUNTRY6=$(expr "$IP6" : '.*country\":[ ]*\"\([^"]*\).*') &&
|
|
ASNORG6=$(expr "$IP6" : '.*isp\":[ ]*\"\([^"]*\).*') &&
|
|
[[ "$L" = C && -n "$COUNTRY6" ]] && COUNTRY6=$(translate "$COUNTRY6")
|
|
}
|
|
|
|
# 输入起始 port 函数
|
|
enter_start_port() {
|
|
local NUM=$1
|
|
local PORT_ERROR_TIME=6
|
|
while true; do
|
|
[ "$PORT_ERROR_TIME" -lt 6 ] && unset IN_USED START_PORT
|
|
(( PORT_ERROR_TIME-- )) || true
|
|
if [ "$PORT_ERROR_TIME" = 0 ]; then
|
|
error "\n $(text 3) \n"
|
|
else
|
|
[ -z "$START_PORT" ] && reading "\n $(text 11) " START_PORT
|
|
fi
|
|
START_PORT=${START_PORT:-"$START_PORT_DEFAULT"}
|
|
if [[ "$START_PORT" =~ ^[1-9][0-9]{2,4}$ && "$START_PORT" -ge "$MIN_PORT" && "$START_PORT" -le "$MAX_PORT" ]]; then
|
|
for port in $(eval echo {$START_PORT..$[START_PORT+NUM-1]}); do
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
netstat -an | awk '/:[0-9]+/{print $4}' | awk -F ":" '{print $NF}' | grep -q $port && IN_USED+=("$port")
|
|
else
|
|
[[ $(lsof -i udp:$port) || $(lsof -i tcp:$port -sTCP:LISTEN) ]] && IN_USED+=("$port")
|
|
fi
|
|
done
|
|
[ "${#IN_USED[*]}" -eq 0 ] && break || warning "\n $(text 44) \n"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# 定义 Sing-box 变量
|
|
sing-box_variable() {
|
|
if grep -qi 'cloudflare' <<< "$ASNORG4$ASNORG6"; then
|
|
local a=6
|
|
until [ -n "$SERVER_IP" ]; do
|
|
((a--)) || true
|
|
[ "$a" = 0 ] && error "\n $(text 3) \n"
|
|
reading "\n $(text 46) " SERVER_IP
|
|
done
|
|
if [[ "$SERVER_IP" =~ : ]]; then
|
|
WARP_ENDPOINT=2606:4700:d0::a29f:c101
|
|
DOMAIN_STRATEG=prefer_ipv6
|
|
else
|
|
WARP_ENDPOINT=162.159.193.10
|
|
DOMAIN_STRATEG=prefer_ipv4
|
|
fi
|
|
elif [ -n "$WAN4" ]; then
|
|
SERVER_IP_DEFAULT=$WAN4
|
|
WARP_ENDPOINT=162.159.193.10
|
|
DOMAIN_STRATEG=prefer_ipv4
|
|
elif [ -n "$WAN6" ]; then
|
|
SERVER_IP_DEFAULT=$WAN6
|
|
WARP_ENDPOINT=2606:4700:d0::a29f:c101
|
|
DOMAIN_STRATEG=prefer_ipv6
|
|
fi
|
|
|
|
# 选择安装的协议,由于选项 a 为全部协议,所以选项数不是从 a 开始,而是从 b 开始,处理输入:把大写全部变为小写,把不符合的选项去掉,把重复的选项合并
|
|
MAX_CHOOSE_PROTOCOLS=$(asc $[CONSECUTIVE_PORTS+96+1])
|
|
if [ -z "$CHOOSE_PROTOCOLS" ]; then
|
|
hint "\n $(text 49) "
|
|
for e in "${!PROTOCOL_LIST[@]}"; do
|
|
[[ "$e" =~ '6'|'7' ]] && hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} $(text 61) " || hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} "
|
|
done
|
|
reading "\n $(text 24) " CHOOSE_PROTOCOLS
|
|
fi
|
|
|
|
# 对选择协议的输入处理逻辑:先把所有的大写转为小写,并把所有没有去选项剔除掉,最后按输入的次序排序。如果选项为 a(all) 和其他选项并存,将会忽略 a,如 abc 则会处理为 bc
|
|
[[ ! "${CHOOSE_PROTOCOLS,,}" =~ [b-$MAX_CHOOSE_PROTOCOLS] ]] && INSTALL_PROTOCOLS=($(eval echo {b..$MAX_CHOOSE_PROTOCOLS})) || INSTALL_PROTOCOLS=($(grep -o . <<< "$CHOOSE_PROTOCOLS" | sed "/[^b-$MAX_CHOOSE_PROTOCOLS]/d" | awk '!seen[$0]++'))
|
|
|
|
# 显示选择协议及其次序,输入开始端口号
|
|
if [ -z "$START_PORT" ]; then
|
|
hint "\n $(text 60) "
|
|
for d in "${!INSTALL_PROTOCOLS[@]}"; do
|
|
[ "$d" -ge 9 ] && hint " $[d+1]. ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[d]}) - 98))]} " || hint " $[d+1] . ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[d]}) - 98))]} "
|
|
done
|
|
enter_start_port ${#INSTALL_PROTOCOLS[@]}
|
|
fi
|
|
|
|
# 输入服务器 IP,默认为检测到的服务器 IP,如果全部为空,则提示并退出脚本
|
|
[ -z "$SERVER_IP" ] && reading "\n $(text 10) " SERVER_IP
|
|
SERVER_IP=${SERVER_IP:-"$SERVER_IP_DEFAULT"} && WS_SERVER_IP=$SERVER_IP
|
|
[ -z "$SERVER_IP" ] && error " $(text 47) "
|
|
|
|
# 如选择有 h. vmess + ws 或 i. vless + ws 时,先检测是否有支持的 http 端口可用,如有则要求输入域名和 cdn
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ 'h' ]]; then
|
|
local DOMAIN_ERROR_TIME=5
|
|
until [ -n "$VMESS_HOST_DOMAIN" ]; do
|
|
(( DOMAIN_ERROR_TIME-- )) || true
|
|
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VMESS && reading "\n $(text 50) " VMESS_HOST_DOMAIN || error "\n $(text 3) \n"
|
|
done
|
|
fi
|
|
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ 'i' ]]; then
|
|
local DOMAIN_ERROR_TIME=5
|
|
until [ -n "$VLESS_HOST_DOMAIN" ]; do
|
|
(( DOMAIN_ERROR_TIME-- )) || true
|
|
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VLESS && reading "\n $(text 50) " VLESS_HOST_DOMAIN || error "\n $(text 3) \n"
|
|
done
|
|
fi
|
|
|
|
# 选择或者输入 cdn
|
|
input_cdn
|
|
|
|
wait
|
|
|
|
# 输入 UUID ,错误超过 5 次将会退出
|
|
UUID_DEFAULT=$($TEMP_DIR/sing-box generate uuid)
|
|
[ -z "$UUID" ] && reading "\n $(text 12) " UUID
|
|
local UUID_ERROR_TIME=5
|
|
until [[ -z "$UUID" || "$UUID" =~ ^[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}$ ]]; do
|
|
(( UUID_ERROR_TIME-- )) || true
|
|
[ "$UUID_ERROR_TIME" = 0 ] && error "\n $(text 3) \n" || reading "\n $(text 4) \n" UUID
|
|
done
|
|
UUID=${UUID:-"$UUID_DEFAULT"}
|
|
|
|
# 输入节点名,以系统的 hostname 作为默认
|
|
if [ -s /etc/hostname ]; then
|
|
NODE_NAME_DEFAULT="$(cat /etc/hostname)"
|
|
elif [ $(type -p hostname) ]; then
|
|
NODE_NAME_DEFAULT="$(hostname)"
|
|
else
|
|
NODE_NAME_DEFAULT="Sing-Box"
|
|
fi
|
|
reading "\n $(text 13) " NODE_NAME
|
|
NODE_NAME="${NODE_NAME:-"$NODE_NAME_DEFAULT"}"
|
|
}
|
|
|
|
check_dependencies() {
|
|
# 如果是 Alpine,先升级 wget ,安装 systemctl-py 版
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
CHECK_WGET=$(wget 2>&1 | head -n 1)
|
|
grep -qi 'busybox' <<< "$CHECK_WGET" && ${PACKAGE_INSTALL[int]} wget >/dev/null 2>&1
|
|
|
|
DEPS_CHECK=("bash" "python3" "rc-update" "openssl" "virt-what")
|
|
DEPS_INSTALL=("bash" "python3" "openrc" "openssl" "virt-what")
|
|
for g in "${!DEPS_CHECK[@]}"; do [ ! $(type -p ${DEPS_CHECK[g]}) ] && [[ ! "${DEPS[@]}" =~ "${DEPS_INSTALL[g]}" ]] && DEPS+=(${DEPS_INSTALL[g]}); done
|
|
if [ "${#DEPS[@]}" -ge 1 ]; then
|
|
info "\n $(text 7) ${DEPS[@]} \n"
|
|
${PACKAGE_UPDATE[int]} >/dev/null 2>&1
|
|
${PACKAGE_INSTALL[int]} ${DEPS[@]} >/dev/null 2>&1
|
|
fi
|
|
|
|
[ ! $(type -p systemctl) ] && wget --no-check-certificate https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py -O /bin/systemctl && chmod a+x /bin/systemctl
|
|
fi
|
|
|
|
# 检测 Linux 系统的依赖,升级库并重新安装依赖
|
|
unset DEPS_CHECK DEPS_INSTALL DEPS
|
|
DEPS_CHECK=("wget" "systemctl" "ip" "unzip" "lsof" "bash" "openssl")
|
|
DEPS_INSTALL=("wget" "systemctl" "iproute2" "unzip" "lsof" "bash" "openssl")
|
|
for g in "${!DEPS_CHECK[@]}"; do [ ! $(type -p ${DEPS_CHECK[g]}) ] && [[ ! "${DEPS[@]}" =~ "${DEPS_INSTALL[g]}" ]] && DEPS+=(${DEPS_INSTALL[g]}); done
|
|
if [ "${#DEPS[@]}" -ge 1 ]; then
|
|
info "\n $(text 7) ${DEPS[@]} \n"
|
|
${PACKAGE_UPDATE[int]} >/dev/null 2>&1
|
|
${PACKAGE_INSTALL[int]} ${DEPS[@]} >/dev/null 2>&1
|
|
else
|
|
info "\n $(text 8) \n"
|
|
fi
|
|
}
|
|
|
|
# 生成100年的自签证书
|
|
ssl_certificate() {
|
|
mkdir -p $WORK_DIR/cert
|
|
openssl ecparam -genkey -name prime256v1 -out $WORK_DIR/cert/private.key && openssl req -new -x509 -days 36500 -key $WORK_DIR/cert/private.key -out $WORK_DIR/cert/cert.pem -subj "/CN=$(awk -F . '{print $(NF-1)"."$NF}' <<< "$TLS_SERVER_DEFAULT")"
|
|
}
|
|
|
|
# 生成 sing-box 配置文件
|
|
sing-box_json() {
|
|
local IS_CHANGE=$1
|
|
mkdir -p $WORK_DIR/conf $WORK_DIR/logs
|
|
|
|
# 判断是否为新安装,不为 change 就是新安装
|
|
if [ "$IS_CHANGE" = 'change' ]; then
|
|
# 判断 sing-box 主程序所在路径
|
|
DIR=$WORK_DIR
|
|
else
|
|
DIR=$TEMP_DIR
|
|
|
|
# 生成 dns 配置
|
|
cat > $WORK_DIR/conf/00_log.json << EOF
|
|
{
|
|
"log":{
|
|
"disabled":false,
|
|
"level":"error",
|
|
"output":"$WORK_DIR/logs/box.log",
|
|
"timestamp":true
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# 生成 outbound 配置
|
|
cat > $WORK_DIR/conf/01_outbounds.json << EOF
|
|
{
|
|
"outbounds":[
|
|
{
|
|
"type":"direct",
|
|
"tag":"direct",
|
|
"domain_strategy":"$DOMAIN_STRATEG"
|
|
},
|
|
{
|
|
"type":"direct",
|
|
"tag":"warp-IPv4-out",
|
|
"detour":"wireguard-out",
|
|
"domain_strategy":"ipv4_only"
|
|
},
|
|
{
|
|
"type":"direct",
|
|
"tag":"warp-IPv6-out",
|
|
"detour":"wireguard-out",
|
|
"domain_strategy":"ipv6_only"
|
|
},
|
|
{
|
|
"type":"wireguard",
|
|
"tag":"wireguard-out",
|
|
"server":"${WARP_ENDPOINT}",
|
|
"server_port":2408,
|
|
"local_address":[
|
|
"172.16.0.2/32",
|
|
"2606:4700:110:8a36:df92:102a:9602:fa18/128"
|
|
],
|
|
"private_key":"YFYOAdbw1bKTHlNNi+aEjBM3BO7unuFC5rOkMRAz9XY=",
|
|
"peer_public_key":"bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
|
|
"reserved":[
|
|
78,
|
|
135,
|
|
76
|
|
],
|
|
"mtu":1280
|
|
},
|
|
{
|
|
"type":"block",
|
|
"tag":"block"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
# 生成 route 配置
|
|
cat > $WORK_DIR/conf/02_route.json << EOF
|
|
{
|
|
"route":{
|
|
"rule_set":[
|
|
{
|
|
"tag":"geosite-openai",
|
|
"type":"remote",
|
|
"format":"binary",
|
|
"url":"https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-openai.srs"
|
|
}
|
|
],
|
|
"rules":[
|
|
{
|
|
"domain":"api.openai.com",
|
|
"outbound":"warp-IPv4-out"
|
|
},
|
|
{
|
|
"rule_set":"geosite-openai",
|
|
"outbound":"warp-IPv6-out"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# 生成缓存文件
|
|
cat > $WORK_DIR/conf/03_experimental.json << EOF
|
|
{
|
|
"experimental": {
|
|
"cache_file": {
|
|
"enabled": true,
|
|
"path": "$WORK_DIR/cache.db"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# 生成 dns 配置文件
|
|
cat > $WORK_DIR/conf/04_dns.json << EOF
|
|
{
|
|
"dns":{
|
|
"servers":[
|
|
{
|
|
"address":"local"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 Reality 公私钥,第一次安装的时候使用新生成的;添加协议的时,使用相应数组里的第一个非空值,如全空则像第一次安装那样使用新生成的
|
|
if [[ "${#REALITY_PRIVATE[@]}" = 0 || "${#REALITY_PUBLIC[@]}" = 0 ]]; then
|
|
REALITY_KEYPAIR=$($DIR/sing-box generate reality-keypair) && REALITY_PRIVATE=$(awk '/PrivateKey/{print $NF}' <<< "$REALITY_KEYPAIR") && REALITY_PUBLIC=$(awk '/PublicKey/{print $NF}' <<< "$REALITY_KEYPAIR")
|
|
else
|
|
REALITY_PRIVATE=$(awk '{print $1}' <<< "${REALITY_PRIVATE[@]}") && REALITY_PUBLIC=$(awk '{print $1}' <<< "${REALITY_PUBLIC[@]}")
|
|
fi
|
|
|
|
# 生成 TLS 网站
|
|
[ "${#TLS_SERVER[@]}" -gt 0 ] && TLS_SERVER=$(awk '{print $1}' <<< "${TLS_SERVER[@]}") || TLS_SERVER=$TLS_SERVER_DEFAULT
|
|
|
|
# 第1个协议为 b (a为全部),生成 XTLS + Reality 配置
|
|
CHECK_PROTOCOLS=b
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_XTLS_REALITY" ] && PORT_XTLS_REALITY=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[11]=${NODE_NAME[11]:-"$NODE_NAME"} && UUID[11]=${UUID[11]:-"$UUID"} && TLS_SERVER[11]=${TLS_SERVER[11]:-"$TLS_SERVER"} && REALITY_PRIVATE[11]=${REALITY_PRIVATE[11]:-"$REALITY_PRIVATE"} && REALITY_PUBLIC[11]=${REALITY_PUBLIC[11]:-"$REALITY_PUBLIC"}
|
|
cat > $WORK_DIR/conf/11_${NODE_TAG[0]}_inbounds.json << EOF
|
|
// "public_key":"${REALITY_PUBLIC[11]}"
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"vless",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[11]} ${NODE_TAG[0]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_XTLS_REALITY,
|
|
"users":[
|
|
{
|
|
"uuid":"${UUID[11]}",
|
|
"flow":""
|
|
}
|
|
],
|
|
"tls":{
|
|
"enabled":true,
|
|
"server_name":"${TLS_SERVER[11]}",
|
|
"reality":{
|
|
"enabled":true,
|
|
"handshake":{
|
|
"server":"${TLS_SERVER[11]}",
|
|
"server_port":443
|
|
},
|
|
"private_key":"${REALITY_PRIVATE[11]}",
|
|
"short_id":[
|
|
""
|
|
]
|
|
}
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 Hysteria2 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_HYSTERIA2" ] && PORT_HYSTERIA2=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[12]=${NODE_NAME[12]:-"$NODE_NAME"} && UUID[12]=${UUID[12]:-"$UUID"}
|
|
cat > $WORK_DIR/conf/12_${NODE_TAG[1]}_inbounds.json << EOF
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"hysteria2",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[12]} ${NODE_TAG[1]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_HYSTERIA2,
|
|
"users":[
|
|
{
|
|
"password":"${UUID[12]}"
|
|
}
|
|
],
|
|
"ignore_client_bandwidth":false,
|
|
"tls":{
|
|
"enabled":true,
|
|
"server_name":"",
|
|
"alpn":[
|
|
"h3"
|
|
],
|
|
"min_version":"1.3",
|
|
"max_version":"1.3",
|
|
"certificate_path":"$WORK_DIR/cert/cert.pem",
|
|
"key_path":"$WORK_DIR/cert/private.key"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 Tuic V5 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_TUIC" ] && PORT_TUIC=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[13]=${NODE_NAME[13]:-"$NODE_NAME"} && UUID[13]=${UUID[13]:-"$UUID"} && TUIC_PASSWORD=${TUIC_PASSWORD:-"$UUID"} && TUIC_CONGESTION_CONTROL=${TUIC_CONGESTION_CONTROL:-"bbr"}
|
|
cat > $WORK_DIR/conf/13_${NODE_TAG[2]}_inbounds.json << EOF
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"tuic",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[13]} ${NODE_TAG[2]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_TUIC,
|
|
"users":[
|
|
{
|
|
"uuid":"${UUID[13]}",
|
|
"password":"$TUIC_PASSWORD"
|
|
}
|
|
],
|
|
"congestion_control": "$TUIC_CONGESTION_CONTROL",
|
|
"zero_rtt_handshake": false,
|
|
"tls":{
|
|
"enabled":true,
|
|
"alpn":[
|
|
"h3"
|
|
],
|
|
"certificate_path":"$WORK_DIR/cert/cert.pem",
|
|
"key_path":"$WORK_DIR/cert/private.key"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 ShadowTLS V5 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_SHADOWTLS" ] && PORT_SHADOWTLS=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[14]=${NODE_NAME[14]:-"$NODE_NAME"} && UUID[14]=${UUID[14]:-"$UUID"} && TLS_SERVER[14]=${TLS_SERVER[14]:-"$TLS_SERVER"} && SHADOWTLS_PASSWORD=${SHADOWTLS_PASSWORD:-"$($DIR/sing-box generate rand --base64 16)"} && SHADOWTLS_METHOD=${SHADOWTLS_METHOD:-"2022-blake3-aes-128-gcm"}
|
|
|
|
cat > $WORK_DIR/conf/14_${NODE_TAG[3]}_inbounds.json << EOF
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"shadowtls",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[14]} ${NODE_TAG[3]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_SHADOWTLS,
|
|
"detour":"shadowtls-in",
|
|
"version":3,
|
|
"users":[
|
|
{
|
|
"password":"${UUID[14]}"
|
|
}
|
|
],
|
|
"handshake":{
|
|
"server":"${TLS_SERVER[14]}",
|
|
"server_port":443
|
|
},
|
|
"strict_mode":true
|
|
},
|
|
{
|
|
"type":"shadowsocks",
|
|
"tag":"shadowtls-in",
|
|
"listen":"127.0.0.1",
|
|
"network":"tcp",
|
|
"method":"$SHADOWTLS_METHOD",
|
|
"password":"$SHADOWTLS_PASSWORD",
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 Shadowsocks 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_SHADOWSOCKS" ] && PORT_SHADOWSOCKS=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[15]=${NODE_NAME[15]:-"$NODE_NAME"} && UUID[15]=${UUID[15]:-"$UUID"} && SHADOWSOCKS_METHOD=${SHADOWSOCKS_METHOD:-"aes-128-gcm"}
|
|
cat > $WORK_DIR/conf/15_${NODE_TAG[4]}_inbounds.json << EOF
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"shadowsocks",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[15]} ${NODE_TAG[4]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_SHADOWSOCKS,
|
|
"method":"$SHADOWSOCKS_METHOD",
|
|
"password":"${UUID[15]}",
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 Trojan 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_TROJAN" ] && PORT_TROJAN=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[16]=${NODE_NAME[16]:-"$NODE_NAME"} && TROJAN_PASSWORD=${TROJAN_PASSWORD:-"$UUID"}
|
|
cat > $WORK_DIR/conf/16_${NODE_TAG[5]}_inbounds.json << EOF
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"trojan",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[16]} ${NODE_TAG[5]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_TROJAN,
|
|
"users":[
|
|
{
|
|
"password":"$TROJAN_PASSWORD"
|
|
}
|
|
],
|
|
"tls":{
|
|
"enabled":true,
|
|
"certificate_path":"$WORK_DIR/cert/cert.pem",
|
|
"key_path":"$WORK_DIR/cert/private.key"
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 vmess + ws 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_VMESS_WS" ] && PORT_VMESS_WS=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[17]=${NODE_NAME[17]:-"$NODE_NAME"} && UUID[17]=${UUID[17]:-"$UUID"} && WS_SERVER_IP[17]=${WS_SERVER_IP[17]:-"$SERVER_IP"} && CDN[17]=${CDN[17]:-"$CDN"} && VMESS_WS_PATH=${VMESS_WS_PATH:-"${UUID[17]}-vmess"}
|
|
cat > $WORK_DIR/conf/17_${NODE_TAG[6]}_inbounds.json << EOF
|
|
// "WS_SERVER_IP": "${WS_SERVER_IP[17]}"
|
|
// "VMESS_HOST_DOMAIN": "$VMESS_HOST_DOMAIN"
|
|
// "CDN": "${CDN[17]}"
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"vmess",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[17]} ${NODE_TAG[6]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_VMESS_WS,
|
|
"tcp_fast_open":false,
|
|
"proxy_protocol":false,
|
|
"users":[
|
|
{
|
|
"uuid":"${UUID[17]}",
|
|
"alterId":0
|
|
}
|
|
],
|
|
"transport":{
|
|
"type":"ws",
|
|
"path":"/$VMESS_WS_PATH",
|
|
"max_early_data":2048,
|
|
"early_data_header_name":"Sec-WebSocket-Protocol"
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 vless + ws + tls 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_VLESS_WS" ] && PORT_VLESS_WS=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[18]=${NODE_NAME[18]:-"$NODE_NAME"} && UUID[18]=${UUID[18]:-"$UUID"} && WS_SERVER_IP[18]=${WS_SERVER_IP[18]:-"$SERVER_IP"} && CDN[18]=${CDN[18]:-"$CDN"} && VLESS_WS_PATH=${VLESS_WS_PATH:-"${UUID[18]}-vless"}
|
|
cat > $WORK_DIR/conf/18_${NODE_TAG[7]}_inbounds.json << EOF
|
|
// "WS_SERVER_IP": "${WS_SERVER_IP[18]}"
|
|
// "CDN": "${CDN[18]}"
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"vless",
|
|
"sniff_override_destination":true,
|
|
"sniff":true,
|
|
"tag":"${NODE_NAME[18]} ${NODE_TAG[7]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_VLESS_WS,
|
|
"tcp_fast_open":false,
|
|
"proxy_protocol":false,
|
|
"users":[
|
|
{
|
|
"name":"sing-box",
|
|
"uuid":"${UUID[18]}"
|
|
}
|
|
],
|
|
"transport":{
|
|
"type":"ws",
|
|
"path":"/$VLESS_WS_PATH",
|
|
"max_early_data":2048,
|
|
"early_data_header_name":"Sec-WebSocket-Protocol"
|
|
},
|
|
"tls":{
|
|
"enabled":true,
|
|
"server_name":"$VLESS_HOST_DOMAIN",
|
|
"min_version":"1.3",
|
|
"max_version":"1.3",
|
|
"certificate_path":"${WORK_DIR}/cert/cert.pem",
|
|
"key_path":"${WORK_DIR}/cert/private.key"
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 H2 + Reality 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_H2_REALITY" ] && PORT_H2_REALITY=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[19]=${NODE_NAME[19]:-"$NODE_NAME"} && UUID[19]=${UUID[19]:-"$UUID"} && TLS_SERVER[19]=${TLS_SERVER[19]:-"$TLS_SERVER"} && REALITY_PRIVATE[19]=${REALITY_PRIVATE[19]:-"$REALITY_PRIVATE"} && REALITY_PUBLIC[19]=${REALITY_PUBLIC[19]:-"$REALITY_PUBLIC"}
|
|
cat > $WORK_DIR/conf/19_${NODE_TAG[8]}_inbounds.json << EOF
|
|
// "public_key":"${REALITY_PUBLIC[19]}"
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"vless",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[19]} ${NODE_TAG[8]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_H2_REALITY,
|
|
"users":[
|
|
{
|
|
"uuid":"${UUID[19]}"
|
|
}
|
|
],
|
|
"tls":{
|
|
"enabled":true,
|
|
"server_name":"${TLS_SERVER[19]}",
|
|
"reality":{
|
|
"enabled":true,
|
|
"handshake":{
|
|
"server":"${TLS_SERVER[19]}",
|
|
"server_port":443
|
|
},
|
|
"private_key":"${REALITY_PRIVATE[19]}",
|
|
"short_id":[
|
|
""
|
|
]
|
|
}
|
|
},
|
|
"transport": {
|
|
"type": "http"
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# 生成 gRPC + Reality 配置
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
[ -z "$PORT_GRPC_REALITY" ] && PORT_GRPC_REALITY=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")]
|
|
NODE_NAME[20]=${NODE_NAME[20]:-"$NODE_NAME"} && UUID[20]=${UUID[20]:-"$UUID"} && TLS_SERVER[20]=${TLS_SERVER[20]:-"$TLS_SERVER"} && REALITY_PRIVATE[20]=${REALITY_PRIVATE[20]:-"$REALITY_PRIVATE"} && REALITY_PUBLIC[20]=${REALITY_PUBLIC[20]:-"$REALITY_PUBLIC"}
|
|
cat > $WORK_DIR/conf/20_${NODE_TAG[9]}_inbounds.json << EOF
|
|
// "public_key":"${REALITY_PUBLIC[20]}"
|
|
{
|
|
"inbounds":[
|
|
{
|
|
"type":"vless",
|
|
"sniff":true,
|
|
"sniff_override_destination":true,
|
|
"tag":"${NODE_NAME[20]} ${NODE_TAG[9]}",
|
|
"listen":"::",
|
|
"listen_port":$PORT_GRPC_REALITY,
|
|
"users":[
|
|
{
|
|
"uuid":"${UUID[20]}"
|
|
}
|
|
],
|
|
"tls":{
|
|
"enabled":true,
|
|
"server_name":"${TLS_SERVER[20]}",
|
|
"reality":{
|
|
"enabled":true,
|
|
"handshake":{
|
|
"server":"${TLS_SERVER[20]}",
|
|
"server_port":443
|
|
},
|
|
"private_key":"${REALITY_PRIVATE[20]}",
|
|
"short_id":[
|
|
""
|
|
]
|
|
}
|
|
},
|
|
"transport": {
|
|
"type": "grpc",
|
|
"service_name": "grpc"
|
|
},
|
|
"multiplex":{
|
|
"enabled":true,
|
|
"padding":true,
|
|
"brutal":{
|
|
"enabled":true,
|
|
"up_mbps":1000,
|
|
"down_mbps":1000
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Sing-box 生成守护进程文件
|
|
sing-box_systemd() {
|
|
cat > /etc/systemd/system/sing-box.service << EOF
|
|
[Unit]
|
|
Description=sing-box service
|
|
Documentation=https://sing-box.sagernet.org
|
|
After=network.target nss-lookup.target
|
|
|
|
[Service]
|
|
User=root
|
|
WorkingDirectory=$WORK_DIR
|
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
|
ExecStart=$WORK_DIR/sing-box run -C $WORK_DIR/conf/
|
|
ExecReload=/bin/kill -HUP \$MAINPID
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
LimitNOFILE=infinity
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
}
|
|
|
|
# 获取原有各协议的参数,先清空所有的 key-value
|
|
fetch_nodes_value() {
|
|
unset FILE NODE_NAME PORT_XTLS_REALITY UUID TLS_SERVER REALITY_PRIVATE REALITY_PUBLIC PORT_HYSTERIA2 OBFS PORT_TUIC TUIC_PASSWORD TUIC_CONGESTION_CONTROL PORT_SHADOWTLS SHADOWTLS_PASSWORD SHADOWSOCKS_METHOD PORT_SHADOWSOCKS PORT_TROJAN TROJAN_PASSWORD PORT_VMESS_WS VMESS_WS_PATH WS_SERVER_IP VMESS_HOST_DOMAIN CDN PORT_VLESS_WS VLESS_WS_PATH VLESS_HOST_DOMAIN PORT_H2_REALITY PORT_GRPC_REALITY
|
|
|
|
# 获取公共数据
|
|
SERVER_IP=$(grep -A1 '"tag"' $WORK_DIR/list | sed -E '/-ws(-tls)*",$/{N;d}' | awk -F '"' '/"server"/{count++; if (count == 1) {print $4; exit}}')
|
|
EXISTED_PORTS=$(awk -F ':|,' '/listen_port/{print $2}' $WORK_DIR/conf/*_inbounds.json)
|
|
START_PORT=$(awk 'NR == 1 { min = $0 } { if ($0 < min) min = $0; count++ } END {print min}' <<< "$EXISTED_PORTS")
|
|
|
|
# 获取 XTLS + Reality key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[0]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[0]}_inbounds.json) && NODE_NAME[11]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[0]}.*/\1/p" <<< "$JSON") && PORT_XTLS_REALITY=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[11]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && TLS_SERVER[11]=$(awk -F '"' '/"server_name"/{print $4}' <<< "$JSON") && REALITY_PRIVATE[11]=$(awk -F '"' '/"private_key"/{print $4}' <<< "$JSON") && REALITY_PUBLIC[11]=$(awk -F '"' '/"public_key"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 Hysteria2 key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[1]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[1]}_inbounds.json) && NODE_NAME[12]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[1]}.*/\1/p" <<< "$JSON") && PORT_HYSTERIA2=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[12]=$(awk -F '"' '/"password"/{count++; if (count == 1) {print $4; exit}}' <<< "$JSON")
|
|
|
|
# 获取 Tuic V5 key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[2]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[2]}_inbounds.json) && NODE_NAME[13]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[2]}.*/\1/p" <<< "$JSON") && PORT_TUIC=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[13]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && TUIC_PASSWORD=$(awk -F '"' '/"password"/{print $4}' <<< "$JSON") && TUIC_CONGESTION_CONTROL=$(awk -F '"' '/"congestion_control"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 ShadowTLS key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[3]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[3]}_inbounds.json) && NODE_NAME[14]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[3]}.*/\1/p" <<< "$JSON") && PORT_SHADOWTLS=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[14]=$(awk -F '"' '/"password"/{count++; if (count == 1) {print $4; exit}}' <<< "$JSON") && SHADOWTLS_PASSWORD=$(awk -F '"' '/"password"/{count++; if (count == 2) {print $4; exit}}' <<< "$JSON") && TLS_SERVER[14]=$(awk -F '"' '/"server"/{print $4}' <<< "$JSON") && SHADOWTLS_METHOD=$(awk -F '"' '/"method"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 Shadowsocks key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[4]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[4]}_inbounds.json) && NODE_NAME[15]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[4]}.*/\1/p" <<< "$JSON") && PORT_SHADOWSOCKS=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[15]=$(awk -F '"' '/"password"/{print $4}' <<< "$JSON") && SHADOWSOCKS_METHOD=$(awk -F '"' '/"method"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 Trojan key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[5]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[5]}_inbounds.json) && NODE_NAME[16]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[5]}.*/\1/p" <<< "$JSON") && PORT_TROJAN=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && TROJAN_PASSWORD=$(awk -F '"' '/"password"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 vmess + ws key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[6]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[6]}_inbounds.json) && NODE_NAME[17]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[6]}.*/\1/p" <<< "$JSON") && PORT_VMESS_WS=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[17]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && VMESS_WS_PATH=$(sed -n 's#.*"path":"/\(.*\)",#\1#p' <<< "$JSON") && WS_SERVER_IP[17]=$(awk -F '"' '/"WS_SERVER_IP"/{print $4}' <<< "$JSON") && VMESS_HOST_DOMAIN=$(awk -F '"' '/"VMESS_HOST_DOMAIN"/{print $4}' <<< "$JSON") && CDN[17]=$(awk -F '"' '/"CDN"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 vless + ws + tls key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[7]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[7]}_inbounds.json) && NODE_NAME[18]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[7]}.*/\1/p" <<< "$JSON") && PORT_VLESS_WS=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[18]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && VLESS_WS_PATH=$(sed -n 's#.*"path":"/\(.*\)",#\1#p' <<< "$JSON") && WS_SERVER_IP[18]=$(awk -F '"' '/"WS_SERVER_IP"/{print $4}' <<< "$JSON") && VLESS_HOST_DOMAIN=$(awk -F '"' '/"server_name"/{print $4}' <<< "$JSON") && CDN[18]=$(awk -F '"' '/"CDN"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 H2 + Reality key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[8]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[8]}_inbounds.json) && NODE_NAME[19]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[8]}.*/\1/p" <<< "$JSON") && PORT_H2_REALITY=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[19]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && TLS_SERVER[19]=$(awk -F '"' '/"server"/{print $4}' <<< "$JSON") && REALITY_PRIVATE[19]=$(awk -F '"' '/"private_key"/{print $4}' <<< "$JSON") && REALITY_PUBLIC[19]=$(awk -F '"' '/"public_key"/{print $4}' <<< "$JSON")
|
|
|
|
# 获取 gRPC + Reality key-value
|
|
[ -s $WORK_DIR/conf/*_${NODE_TAG[9]}_inbounds.json ] && local JSON=$(cat $WORK_DIR/conf/*_${NODE_TAG[9]}_inbounds.json) && NODE_NAME[20]=$(sed -n "s/.*\"tag\":\"\(.*\) ${NODE_TAG[9]}.*/\1/p" <<< "$JSON") && PORT_GRPC_REALITY=$(sed -n 's/.*"listen_port":\([0-9]\+\),/\1/gp' <<< "$JSON") && UUID[20]=$(awk -F '"' '/"uuid"/{print $4}' <<< "$JSON") && TLS_SERVER[20]=$(awk -F '"' '/"server"/{print $4}' <<< "$JSON") && REALITY_PRIVATE[20]=$(awk -F '"' '/"private_key"/{print $4}' <<< "$JSON") && REALITY_PUBLIC[20]=$(awk -F '"' '/"public_key"/{print $4}' <<< "$JSON")
|
|
}
|
|
|
|
install_sing-box() {
|
|
sing-box_variable
|
|
[ ! -d /etc/systemd/system ] && mkdir -p /etc/systemd/system
|
|
[ ! -d $WORK_DIR/logs ] && mkdir -p $WORK_DIR/logs
|
|
ssl_certificate
|
|
sing-box_json
|
|
echo "$L" > $WORK_DIR/language
|
|
cp $TEMP_DIR/sing-box $WORK_DIR
|
|
sing-box_systemd
|
|
|
|
# 如果 Alpine 系统,放到开机自启动
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
cat > /etc/local.d/sing-box.start << EOF
|
|
#!/usr/bin/env bash
|
|
|
|
systemctl start sing-box
|
|
EOF
|
|
chmod +x /etc/local.d/sing-box.start
|
|
rc-update add local >/dev/null 2>&1
|
|
fi
|
|
|
|
# 再次检测状态,运行 Sing-box
|
|
check_install
|
|
|
|
check_sing-box_stats
|
|
}
|
|
|
|
export_list() {
|
|
IS_INSTALL=$1
|
|
|
|
check_install
|
|
|
|
[ "$IS_INSTALL" != 'install' ] && fetch_nodes_value
|
|
|
|
# IPv6 时的 IP 处理
|
|
if [[ "$SERVER_IP" =~ : ]]; then
|
|
SERVER_IP_1="[$SERVER_IP]"
|
|
SERVER_IP_2="[[$SERVER_IP]]"
|
|
else
|
|
SERVER_IP_1="$SERVER_IP"
|
|
SERVER_IP_2="$SERVER_IP"
|
|
fi
|
|
|
|
# 生成配置文件
|
|
cat > $WORK_DIR/list << EOF
|
|
*******************************************
|
|
┌────────────────┐
|
|
│ │
|
|
│ $(warning "V2rayN") │
|
|
│ │
|
|
└────────────────┘
|
|
EOF
|
|
[ -n "$PORT_XTLS_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "vless://${UUID[11]}@${SERVER_IP_1}:${PORT_XTLS_REALITY}?encryption=none&security=reality&sni=${TLS_SERVER[11]}&fp=chrome&pbk=${REALITY_PUBLIC[11]}&type=tcp&headerType=none#${NODE_NAME[11]} ${NODE_TAG[0]}")
|
|
EOF
|
|
[ -n "$PORT_HYSTERIA2" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "hysteria2://${UUID[12]}@${SERVER_IP_1}:${PORT_HYSTERIA2}?insecure=1#${NODE_NAME[12]} ${NODE_TAG[1]}")
|
|
EOF
|
|
[ -n "$PORT_TUIC" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "tuic://${UUID[13]}:${TUIC_PASSWORD}@${SERVER_IP_1}:${PORT_TUIC}?alpn=h3&congestion_control=$TUIC_CONGESTION_CONTROL#${NODE_NAME[13]} ${NODE_TAG[2]}
|
|
|
|
$(text 70)")
|
|
EOF
|
|
[ -n "$PORT_SHADOWTLS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "$(text 54)
|
|
|
|
{
|
|
\"log\":{
|
|
\"level\":\"warn\"
|
|
},
|
|
\"inbounds\":[
|
|
{
|
|
\"domain_strategy\":\"\",
|
|
\"listen\":\"127.0.0.1\",
|
|
\"listen_port\":${PORT_SHADOWTLS},
|
|
\"sniff\":true,
|
|
\"sniff_override_destination\":false,
|
|
\"tag\": \"ShadowTLS\",
|
|
\"type\":\"mixed\"
|
|
}
|
|
],
|
|
\"outbounds\":[
|
|
{
|
|
\"detour\":\"shadowtls-out\",
|
|
\"domain_strategy\":\"\",
|
|
\"method\":\"$SHADOWTLS_METHOD\",
|
|
\"password\":\"$SHADOWTLS_PASSWORD\",
|
|
\"type\":\"shadowsocks\",
|
|
\"udp_over_tcp\": false,
|
|
\"multiplex\": {
|
|
\"enabled\": true,
|
|
\"protocol\": \"h2mux\",
|
|
\"max_connections\": 8,
|
|
\"min_streams\": 16,
|
|
\"padding\": true
|
|
}
|
|
},
|
|
{
|
|
\"domain_strategy\":\"\",
|
|
\"password\":\"${UUID[14]}\",
|
|
\"server\":\"${SERVER_IP}\",
|
|
\"server_port\":${PORT_SHADOWTLS},
|
|
\"tag\": \"shadowtls-out\",
|
|
\"tls\":{
|
|
\"enabled\":true,
|
|
\"server_name\":\"${TLS_SERVER[14]}\",
|
|
\"utls\": {
|
|
\"enabled\": true,
|
|
\"fingerprint\": \"chrome\"
|
|
}
|
|
},
|
|
\"type\":\"shadowtls\",
|
|
\"version\":3
|
|
}
|
|
]
|
|
}")
|
|
EOF
|
|
[ -n "$PORT_SHADOWSOCKS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "ss://$(echo -n "$SHADOWSOCKS_METHOD:${UUID[15]}@${SERVER_IP_1}:$PORT_SHADOWSOCKS" | base64 -w0)#${NODE_NAME[15]} ${NODE_TAG[4]}")
|
|
EOF
|
|
[ -n "$PORT_TROJAN" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "trojan://$TROJAN_PASSWORD@${SERVER_IP_1}:$PORT_TROJAN?security=tls&type=tcp&headerType=none#${NODE_NAME[16]} ${NODE_TAG[5]}
|
|
|
|
$(text 70)")
|
|
EOF
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", \"add\": \"${CDN[18]}\", \"port\": \"80\", \"id\": \"${UUID[18]}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"$VMESS_HOST_DOMAIN\", \"path\": \"/$VMESS_WS_PATH\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\" }" | base64 -w0)
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "vless://${UUID[18]}@${CDN[18]}:443?encryption=none&security=tls&sni=$VLESS_HOST_DOMAIN&type=ws&host=$VLESS_HOST_DOMAIN&path=%2F$VLESS_WS_PATH%3Fed%3D2048#${NODE_NAME[18]} ${NODE_TAG[7]}
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_H2_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "vless://${UUID[19]}@${SERVER_IP_1}:${PORT_H2_REALITY}?encryption=none&security=reality&sni=${TLS_SERVER[19]}&fp=chrome&pbk=${REALITY_PUBLIC[19]}&type=http#${NODE_NAME[19]} ${NODE_TAG[8]}")
|
|
EOF
|
|
[ -n "$PORT_GRPC_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(info "vless://${UUID[20]}@${SERVER_IP_1}:${PORT_GRPC_REALITY}?encryption=none&security=reality&sni=${TLS_SERVER[20]}&fp=chrome&pbk=${REALITY_PUBLIC[20]}&type=grpc&serviceName=grpc&mode=gun#${NODE_NAME[20]} ${NODE_TAG[9]}")
|
|
EOF
|
|
|
|
cat >> $WORK_DIR/list << EOF
|
|
|
|
*******************************************
|
|
┌────────────────┐
|
|
│ │
|
|
│ $(warning "Shadowrocket") │
|
|
│ │
|
|
└────────────────┘
|
|
----------------------------
|
|
EOF
|
|
[ -n "$PORT_XTLS_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "vless://$(echo -n "auto:${UUID[11]}@${SERVER_IP_2}:${PORT_XTLS_REALITY}" | base64 -w0)?remarks=${NODE_NAME[11]}%20${NODE_TAG[0]}&obfs=none&tls=1&peer=${TLS_SERVER[11]}&mux=1&pbk=${REALITY_PUBLIC[11]}")
|
|
EOF
|
|
[ -n "$PORT_HYSTERIA2" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "hysteria2://${UUID[12]}@${SERVER_IP_2}:${PORT_HYSTERIA2}?insecure=1&obfs=none#${NODE_NAME[12]}%20${NODE_TAG[1]}")
|
|
EOF
|
|
[ -n "$PORT_TUIC" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "tuic://${TUIC_PASSWORD}:${UUID[13]}@${SERVER_IP_2}:${PORT_TUIC}?congestion_control=$TUIC_CONGESTION_CONTROL&udp_relay_mode=native&alpn=h3&allow_insecure=1#${NODE_NAME[13]}%20${NODE_TAG[2]}")
|
|
EOF
|
|
[ -n "$PORT_SHADOWTLS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "ss://$(echo -n "$SHADOWTLS_METHOD:$SHADOWTLS_PASSWORD@${SERVER_IP_2}:${PORT_SHADOWTLS}" | base64 -w0)?shadow-tls=$(echo -n "{\"version\":\"3\",\"host\":\"${TLS_SERVER[14]}\",\"password\":\"${UUID[14]}\"}" | base64 -w0)#${NODE_NAME[14]}%20${NODE_TAG[3]}")
|
|
EOF
|
|
[ -n "$PORT_SHADOWSOCKS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "ss://$(echo -n "$SHADOWSOCKS_METHOD:${UUID[15]}@${SERVER_IP_2}:$PORT_SHADOWSOCKS" | base64 -w0)#${NODE_NAME[15]}%20${NODE_TAG[4]}")
|
|
EOF
|
|
[ -n "$PORT_TROJAN" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "trojan://$TROJAN_PASSWORD@${SERVER_IP_1}:$PORT_TROJAN?allowInsecure=1#${NODE_NAME[16]}%20${NODE_TAG[5]}")
|
|
EOF
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(hint "vmess://$(echo -n "none:${UUID[17]}@${CDN[17]}:80" | base64 -w0)?remarks=${NODE_NAME[17]}%20${NODE_TAG[6]}&obfsParam=$VMESS_HOST_DOMAIN&path=/$VMESS_WS_PATH&obfs=websocket&alterId=0
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(hint "vless://$(echo -n "auto:${UUID[18]}@${CDN[18]}:443" | base64 -w0)?remarks=${NODE_NAME[18]}%20${NODE_TAG[7]}&obfsParam=$VLESS_HOST_DOMAIN&path=/$VLESS_WS_PATH?ed=2048&obfs=websocket&tls=1&peer=$VLESS_HOST_DOMAIN&allowInsecure=1
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_H2_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
----------------------------
|
|
$(hint "vless://$(echo -n auto:${UUID[19]}@${SERVER_IP_2}:${PORT_H2_REALITY} | base64 -w0)?remarks=${NODE_NAME[19]}%20${NODE_TAG[8]}&path=/&obfs=h2&tls=1&peer=${TLS_SERVER[19]}&alpn=h2&mux=1&pbk=${REALITY_PUBLIC[19]}")
|
|
EOF
|
|
[ -n "$PORT_GRPC_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(hint "vless://$(echo -n "auto:${UUID[20]}@${SERVER_IP_2}:${PORT_GRPC_REALITY}" | base64 -w0)?remarks=${NODE_NAME[20]}%20${NODE_TAG[9]}&path=grpc&obfs=grpc&tls=1&peer=${TLS_SERVER[20]}&pbk=${REALITY_PUBLIC[20]}")
|
|
EOF
|
|
|
|
cat >> $WORK_DIR/list << EOF
|
|
|
|
*******************************************
|
|
┌────────────────┐
|
|
│ │
|
|
│ $(warning "Clash Meta") │
|
|
│ │
|
|
└────────────────┘
|
|
----------------------------
|
|
EOF
|
|
[ -n "$PORT_XTLS_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[11]} ${NODE_TAG[0]}\", type: vless, server: ${SERVER_IP}, port: ${PORT_XTLS_REALITY}, uuid: ${UUID[11]}, network: tcp, udp: true, tls: true, servername: ${TLS_SERVER[11]}, client-fingerprint: chrome, reality-opts: {public-key: ${REALITY_PUBLIC[11]}, short-id: \"\"}, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }")
|
|
EOF
|
|
[ -n "$PORT_HYSTERIA2" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[12]} ${NODE_TAG[1]}\", type: hysteria2, server: ${SERVER_IP}, port: ${PORT_HYSTERIA2}, up: \"200 Mbps\", down: \"1000 Mbps\", password: ${UUID[12]}, skip-cert-verify: true}")
|
|
EOF
|
|
[ -n "$PORT_TUIC" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[13]} ${NODE_TAG[2]}\", type: tuic, server: ${SERVER_IP}, port: ${PORT_TUIC}, uuid: ${UUID[13]}, password: ${TUIC_PASSWORD}, alpn: [h3], disable-sni: true, reduce-rtt: true, request-timeout: 8000, udp-relay-mode: native, congestion-controller: $TUIC_CONGESTION_CONTROL, skip-cert-verify: true}")
|
|
EOF
|
|
[ -n "$PORT_SHADOWTLS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[14]} ${NODE_TAG[3]}\", type: ss, server: ${SERVER_IP}, port: ${PORT_SHADOWTLS}, cipher: $SHADOWTLS_METHOD, password: \"$SHADOWTLS_PASSWORD\", plugin: shadow-tls, client-fingerprint: chrome, plugin-opts: {host: \"${TLS_SERVER[14]}\", password: \"${UUID[14]}\", version: 3}, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }")
|
|
EOF
|
|
[ -n "$PORT_SHADOWSOCKS" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[15]} ${NODE_TAG[4]}\", type: ss, server: ${SERVER_IP}, port: $PORT_SHADOWSOCKS, cipher: $SHADOWSOCKS_METHOD, password: \"${UUID[15]}\", smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }")
|
|
EOF
|
|
[ -n "$PORT_TROJAN" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[16]} ${NODE_TAG[5]}\", type: trojan, server: ${SERVER_IP}, port: $PORT_TROJAN, password: $TROJAN_PASSWORD, client-fingerprint: random, skip-cert-verify: true, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }")
|
|
EOF
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[17]} ${NODE_TAG[6]}\", type: vmess, server: ${CDN[17]}, port: 80, uuid: ${UUID[17]}, udp: true, tls: false, alterId: 0, cipher: none, skip-cert-verify: true, network: ws, ws-opts: { path: \"/$VMESS_WS_PATH\", headers: { Host: $VMESS_HOST_DOMAIN, max-early-data: 2048, early-data-header-name: Sec-WebSocket-Protocol} }, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[18]} ${NODE_TAG[7]}\", type: vless, server: ${CDN[18]}, port: 443, uuid: ${UUID[18]}, udp: true, tls: true, servername: $VLESS_HOST_DOMAIN, network: ws, skip-cert-verify: true, ws-opts: { path: \"/$VLESS_WS_PATH?ed=2048\", headers: { Host: $VLESS_HOST_DOMAIN } }, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }
|
|
|
|
$(text 52)")
|
|
EOF
|
|
|
|
# Clash 的 H2 传输层未实现多路复用功能,在 Clash.Meta 中更建议使用 gRPC 协议,故不输出相关配置。 https://wiki.metacubex.one/config/proxies/vless/
|
|
|
|
[ -n "$PORT_GRPC_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
|
|
$(info "- {name: \"${NODE_NAME[20]} ${NODE_TAG[9]}\", type: vless, server: ${SERVER_IP}, port: ${PORT_GRPC_REALITY}, uuid: ${UUID[20]}, network: grpc, tls: true, udp: true, flow:, client-fingerprint: chrome, servername: ${TLS_SERVER[20]}, grpc-opts: { grpc-service-name: \"grpc\" }, reality-opts: { public-key: ${REALITY_PUBLIC[20]}, short-id: \"\" }, smux: { enabled: true, protocol: 'h2mux', padding: true, max-connections: '8', min-streams: '16', statistic: true, only-tcp: false } }")
|
|
EOF
|
|
|
|
cat >> $WORK_DIR/list << EOF
|
|
|
|
*******************************************
|
|
┌────────────────┐
|
|
│ │
|
|
│ $(warning "NekoBox") │
|
|
│ │
|
|
└────────────────┘
|
|
EOF
|
|
[ -n "$PORT_XTLS_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "vless://${UUID[11]}@${SERVER_IP_1}:${PORT_XTLS_REALITY}?security=reality&sni=${TLS_SERVER[11]}&fp=chrome&pbk=${REALITY_PUBLIC[11]}&type=tcp&encryption=none#${NODE_NAME[11]}%20${NODE_TAG[0]}")
|
|
EOF
|
|
[ -n "$PORT_HYSTERIA2" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "hy2://${UUID[12]}@${SERVER_IP_1}:${PORT_HYSTERIA2}?insecure=1#${NODE_NAME[12]} ${NODE_TAG[1]}")
|
|
|
|
EOF
|
|
[ -n "$PORT_TUIC" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "tuic://${TUIC_PASSWORD}:${UUID[13]}@${SERVER_IP_1}:${PORT_TUIC}?congestion_control=$TUIC_CONGESTION_CONTROL&alpn=h3&udp_relay_mode=native&allow_insecure=1&disable_sni=1#${NODE_NAME[13]} ${NODE_TAG[2]}")
|
|
EOF
|
|
[ -n "$PORT_SHADOWTLS" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "nekoray://custom#$(echo -n "{\"_v\":0,\"addr\":\"127.0.0.1\",\"cmd\":[\"\"],\"core\":\"internal\",\"cs\":\"{\n \\\"password\\\": \\\"${UUID[14]}\\\",\n \\\"server\\\": \\\"${SERVER_IP_1}\\\",\n \\\"server_port\\\": ${PORT_SHADOWTLS},\n \\\"tag\\\": \\\"shadowtls-out\\\",\n \\\"tls\\\": {\n \\\"enabled\\\": true,\n \\\"server_name\\\": \\\"${TLS_SERVER[14]}\\\"\n },\n \\\"type\\\": \\\"shadowtls\\\",\n \\\"version\\\": 3\n}\n\",\"mapping_port\":0,\"name\":\"1-tls-not-use\",\"port\":1080,\"socks_port\":0}" | base64 -w0)
|
|
|
|
nekoray://shadowsocks#$(echo -n "{\"_v\":0,\"method\":\"$SHADOWTLS_METHOD\",\"name\":\"2-ss-not-use\",\"pass\":\"$SHADOWTLS_PASSWORD\",\"port\":0,\"stream\":{\"ed_len\":0,\"insecure\":false,\"mux_s\":0,\"net\":\"tcp\"},\"uot\":0}" | base64 -w0)
|
|
|
|
$(text 48)")
|
|
EOF
|
|
[ -n "$PORT_SHADOWSOCKS" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "ss://$(echo -n "$SHADOWSOCKS_METHOD:${UUID[15]}" | base64 -w0)@${SERVER_IP_1}:$PORT_SHADOWSOCKS#${NODE_NAME[15]} ${NODE_TAG[4]}")
|
|
EOF
|
|
[ -n "$PORT_TROJAN" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "trojan://$TROJAN_PASSWORD@${SERVER_IP_1}:$PORT_TROJAN?security=tls&allowInsecure=1&fp=random&type=tcp#${NODE_NAME[16]} ${NODE_TAG[5]}")
|
|
EOF
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "vmess://$(echo -n "{\"add\":\"${CDN[17]}\",\"aid\":\"0\",\"host\":\"$VMESS_HOST_DOMAIN\",\"id\":\"${UUID[17]}\",\"net\":\"ws\",\"path\":\"/$VMESS_WS_PATH\",\"port\":\"80\",\"ps\":\"${NODE_NAME[17]} ${NODE_TAG[6]}\",\"scy\":\"none\",\"sni\":\"\",\"tls\":\"\",\"type\":\"\",\"v\":\"2\"}" | base64 -w0)
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "vless://${UUID[18]}@${CDN[18]}:443?security=tls&sni=$VLESS_HOST_DOMAIN&type=ws&path=/$VLESS_WS_PATH?ed%3D2048&host=$VLESS_HOST_DOMAIN&encryption=none#${NODE_NAME[18]}%20${NODE_TAG[7]}
|
|
|
|
$(text 52)")
|
|
EOF
|
|
[ -n "$PORT_H2_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "vless://${UUID[19]}@${SERVER_IP_1}:${PORT_H2_REALITY}?security=reality&sni=${TLS_SERVER[19]}&alpn=h2&fp=chrome&pbk=${REALITY_PUBLIC[19]}&type=http&encryption=none#${NODE_NAME[19]}%20${NODE_TAG[8]}")
|
|
EOF
|
|
[ -n "$PORT_GRPC_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
----------------------------
|
|
$(hint "vless://${UUID[20]}@${SERVER_IP_1}:${PORT_GRPC_REALITY}?security=reality&sni=${TLS_SERVER[20]}&fp=chrome&pbk=${REALITY_PUBLIC[20]}&type=grpc&serviceName=grpc&encryption=none#${NODE_NAME[20]}%20${NODE_TAG[9]}")
|
|
EOF
|
|
|
|
cat >> $WORK_DIR/list << EOF
|
|
|
|
*******************************************
|
|
┌────────────────┐
|
|
│ │
|
|
│ $(warning "Sing-box") │
|
|
│ │
|
|
└────────────────┘
|
|
----------------------------
|
|
$(info "{
|
|
\"outbounds\":[")
|
|
EOF
|
|
[ -n "$PORT_XTLS_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"vless\",
|
|
\"tag\": \"${NODE_NAME[11]} ${NODE_TAG[0]}\",
|
|
\"server\":\"${SERVER_IP}\",
|
|
\"server_port\":${PORT_XTLS_REALITY},
|
|
\"uuid\":\"${UUID[11]}\",
|
|
\"flow\":\"\",
|
|
\"packet_encoding\":\"xudp\",
|
|
\"tls\":{
|
|
\"enabled\":true,
|
|
\"server_name\":\"${TLS_SERVER[11]}\",
|
|
\"utls\":{
|
|
\"enabled\":true,
|
|
\"fingerprint\":\"chrome\"
|
|
},
|
|
\"reality\":{
|
|
\"enabled\":true,
|
|
\"public_key\":\"${REALITY_PUBLIC[11]}\",
|
|
\"short_id\":\"\"
|
|
}
|
|
},
|
|
\"multiplex\": {
|
|
\"enabled\": true,
|
|
\"protocol\": \"h2mux\",
|
|
\"max_connections\": 8,
|
|
\"min_streams\": 16,
|
|
\"padding\": true
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_HYSTERIA2" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"hysteria2\",
|
|
\"tag\": \"${NODE_NAME[12]} ${NODE_TAG[1]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": ${PORT_HYSTERIA2},
|
|
\"up_mbps\": 200,
|
|
\"down_mbps\": 1000,
|
|
\"password\": \"${UUID[12]}\",
|
|
\"tls\": {
|
|
\"enabled\": true,
|
|
\"insecure\": true,
|
|
\"server_name\": \"\",
|
|
\"alpn\": [ \"h3\" ]
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_TUIC" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"tuic\",
|
|
\"tag\": \"${NODE_NAME[13]} ${NODE_TAG[2]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": ${PORT_TUIC},
|
|
\"uuid\": \"${UUID[13]}\",
|
|
\"password\": \"${TUIC_PASSWORD}\",
|
|
\"congestion_control\": \"$TUIC_CONGESTION_CONTROL\",
|
|
\"udp_relay_mode\": \"native\",
|
|
\"zero_rtt_handshake\": false,
|
|
\"heartbeat\": \"10s\",
|
|
\"tls\": {
|
|
\"enabled\": true,
|
|
\"insecure\": true,
|
|
\"server_name\": \"\",
|
|
\"alpn\": [ \"h3\" ]
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_SHADOWTLS" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"shadowsocks\",
|
|
\"tag\": \"${NODE_NAME[14]} ${NODE_TAG[3]}\",
|
|
\"method\": \"$SHADOWTLS_METHOD\",
|
|
\"password\": \"$SHADOWTLS_PASSWORD\",
|
|
\"detour\": \"shadowtls-out\",
|
|
\"udp_over_tcp\": false,
|
|
\"multiplex\": {
|
|
\"enabled\": true,
|
|
\"protocol\": \"h2mux\",
|
|
\"max_connections\": 8,
|
|
\"min_streams\": 16,
|
|
\"padding\": true
|
|
}
|
|
},
|
|
{
|
|
\"type\": \"shadowtls\",
|
|
\"tag\": \"shadowtls-out\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": ${PORT_SHADOWTLS},
|
|
\"version\": 3,
|
|
\"password\": \"${UUID[14]}\",
|
|
\"tls\": {
|
|
\"enabled\": true,
|
|
\"server_name\": \"${TLS_SERVER[14]}\",
|
|
\"utls\": {
|
|
\"enabled\": true,
|
|
\"fingerprint\": \"chrome\"
|
|
}
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_SHADOWSOCKS" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"shadowsocks\",
|
|
\"tag\": \"${NODE_NAME[15]} ${NODE_TAG[4]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": $PORT_SHADOWSOCKS,
|
|
\"method\": \"$SHADOWSOCKS_METHOD\",
|
|
\"password\": \"${UUID[14]}\",
|
|
\"multiplex\": {
|
|
\"enabled\": true,
|
|
\"protocol\": \"h2mux\",
|
|
\"max_connections\": 8,
|
|
\"min_streams\": 16,
|
|
\"padding\": true
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_TROJAN" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"trojan\",
|
|
\"tag\": \"${NODE_NAME[16]} ${NODE_TAG[5]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": $PORT_TROJAN,
|
|
\"password\": \"$TROJAN_PASSWORD\",
|
|
\"tls\": {
|
|
\"enabled\":true,
|
|
\"insecure\": true,
|
|
\"server_name\":\"\",
|
|
\"utls\": {
|
|
\"enabled\":true,
|
|
\"fingerprint\":\"chrome\"
|
|
}
|
|
},
|
|
\"multiplex\": {
|
|
\"enabled\":true,
|
|
\"protocol\":\"h2mux\",
|
|
\"max_connections\": 8,
|
|
\"min_streams\": 16,
|
|
\"padding\": true
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"vmess\",
|
|
\"tag\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\",
|
|
\"server\":\"${CDN[17]}\",
|
|
\"server_port\":80,
|
|
\"uuid\":\"${UUID[17]}\",
|
|
\"transport\": {
|
|
\"type\":\"ws\",
|
|
\"path\":\"/$VMESS_WS_PATH\",
|
|
\"headers\": {
|
|
\"Host\": \"$VMESS_HOST_DOMAIN\"
|
|
},
|
|
\"max_early_data\":2048,
|
|
\"early_data_header_name\":\"Sec-WebSocket-Protocol\"
|
|
},
|
|
\"multiplex\": {
|
|
\"enabled\":true,
|
|
\"protocol\":\"h2mux\",
|
|
\"max_streams\":16,
|
|
\"padding\": true
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"vless\",
|
|
\"tag\": \"${NODE_NAME[18]} ${NODE_TAG[7]}\",
|
|
\"server\":\"${CDN[18]}\",
|
|
\"server_port\":443,
|
|
\"uuid\":\"${UUID[18]}\",
|
|
\"tls\": {
|
|
\"enabled\":true,
|
|
\"server_name\":\"$VLESS_HOST_DOMAIN\",
|
|
\"utls\": {
|
|
\"enabled\":true,
|
|
\"fingerprint\":\"chrome\"
|
|
}
|
|
},
|
|
\"transport\": {
|
|
\"type\":\"ws\",
|
|
\"path\":\"/$VLESS_WS_PATH\",
|
|
\"headers\": {
|
|
\"Host\": \"$VLESS_HOST_DOMAIN\"
|
|
},
|
|
\"max_early_data\":2048,
|
|
\"early_data_header_name\":\"Sec-WebSocket-Protocol\"
|
|
},
|
|
\"multiplex\": {
|
|
\"enabled\":true,
|
|
\"protocol\":\"h2mux\",
|
|
\"max_streams\":16,
|
|
\"padding\": true
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_H2_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"vless\",
|
|
\"tag\": \"${NODE_NAME[19]} ${NODE_TAG[8]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": ${PORT_H2_REALITY},
|
|
\"uuid\":\"${UUID[19]}\",
|
|
\"tls\": {
|
|
\"enabled\":true,
|
|
\"server_name\":\"${TLS_SERVER[19]}\",
|
|
\"utls\": {
|
|
\"enabled\":true,
|
|
\"fingerprint\":\"chrome\"
|
|
},
|
|
\"reality\":{
|
|
\"enabled\":true,
|
|
\"public_key\":\"${REALITY_PUBLIC[19]}\",
|
|
\"short_id\":\"\"
|
|
}
|
|
},
|
|
\"packet_encoding\": \"xudp\",
|
|
\"transport\": {
|
|
\"type\": \"http\"
|
|
}
|
|
},")
|
|
EOF
|
|
[ -n "$PORT_GRPC_REALITY" ] && cat >> $WORK_DIR/list << EOF
|
|
$(info " {
|
|
\"type\": \"vless\",
|
|
\"tag\": \"${NODE_NAME[20]} ${NODE_TAG[9]}\",
|
|
\"server\": \"${SERVER_IP}\",
|
|
\"server_port\": ${PORT_GRPC_REALITY},
|
|
\"uuid\":\"${UUID[20]}\",
|
|
\"tls\": {
|
|
\"enabled\":true,
|
|
\"server_name\":\"${TLS_SERVER[20]}\",
|
|
\"utls\": {
|
|
\"enabled\":true,
|
|
\"fingerprint\":\"chrome\"
|
|
},
|
|
\"reality\":{
|
|
\"enabled\":true,
|
|
\"public_key\":\"${REALITY_PUBLIC[20]}\",
|
|
\"short_id\":\"\"
|
|
}
|
|
},
|
|
\"packet_encoding\": \"xudp\",
|
|
\"transport\": {
|
|
\"type\": \"grpc\",
|
|
\"service_name\": \"grpc\"
|
|
}
|
|
},")
|
|
EOF
|
|
|
|
sed -i '$s/},/}/' $WORK_DIR/list
|
|
|
|
cat >> $WORK_DIR/list << EOF
|
|
$(info " ]
|
|
}
|
|
|
|
$(text 72)")
|
|
EOF
|
|
|
|
[ -n "$PORT_VMESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && hint "\n $(text 52)" >> $WORK_DIR/list
|
|
|
|
[ -n "$PORT_VLESS_WS" ] && WS_SERVER_IP=${WS_SERVER_IP[18]} && TYPE_HOST_DOMAIN=$VLESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VLESS_WS && hint "\n $(text 52)" >> $WORK_DIR/list
|
|
|
|
|
|
# 显示节点信息
|
|
cat $WORK_DIR/list
|
|
|
|
# 显示脚本使用情况数据
|
|
info "\n $(text 55) \n"
|
|
}
|
|
|
|
# 创建快捷方式
|
|
create_shortcut() {
|
|
cat > $WORK_DIR/sb.sh << EOF
|
|
#!/usr/bin/env bash
|
|
|
|
bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fscarmen/sing-box/main/sing-box.sh) \$1
|
|
EOF
|
|
chmod +x $WORK_DIR/sb.sh
|
|
ln -sf $WORK_DIR/sb.sh /usr/bin/sb
|
|
[ -s /usr/bin/sb ] && info "\n $(text 71) "
|
|
}
|
|
|
|
# 更换各协议的监听端口
|
|
change_start_port() {
|
|
OLD_PORTS=$(awk -F ':|,' '/listen_port/{print $2}' $WORK_DIR/conf/*)
|
|
OLD_START_PORT=$(awk 'NR == 1 { min = $0 } { if ($0 < min) min = $0; count++ } END {print min}' <<< "$OLD_PORTS")
|
|
OLD_CONSECUTIVE_PORTS=$(awk 'END { print NR }' <<< "$OLD_PORTS")
|
|
enter_start_port $OLD_CONSECUTIVE_PORTS
|
|
systemctl stop sing-box
|
|
for ((a=0; a<$OLD_CONSECUTIVE_PORTS; a++)) do
|
|
[ -s $WORK_DIR/conf/${CONF_FILES[a]} ] && sed -i "s/\(.*listen_port.*:\)$((OLD_START_PORT+a))/\1$((START_PORT+a))/" $WORK_DIR/conf/*
|
|
done
|
|
systemctl start sing-box
|
|
sleep 2
|
|
export_list
|
|
[ "$(systemctl is-active sing-box)" = 'active' ] && info " Sing-box $(text 30) $(text 37) " || error " Sing-box $(text 30) $(text 38) "
|
|
}
|
|
|
|
# 增加或删除协议
|
|
change_protocols() {
|
|
check_install
|
|
[ "$STATUS" = "$(text 26)" ] && error "\n Sing-box $(text 26) "
|
|
|
|
# 查找已安装的协议,并遍历其在所有协议列表中的名称,获取协议名后存放在 EXISTED_PROTOCOLS; 没有的协议存放在 NOT_EXISTED_PROTOCOLS
|
|
INSTALLED_PROTOCOLS_LIST=$(awk -F '"' '/"tag":/{print $4}' $WORK_DIR/conf/*_inbounds.json | grep -v 'shadowtls-in' | awk '{print $NF}')
|
|
for f in ${!NODE_TAG[@]}; do [[ $INSTALLED_PROTOCOLS_LIST =~ "${NODE_TAG[f]}" ]] && EXISTED_PROTOCOLS+=("${PROTOCOL_LIST[f]}") || NOT_EXISTED_PROTOCOLS+=("${PROTOCOL_LIST[f]}"); done
|
|
|
|
# 列出已安装协议
|
|
hint "\n $(text 63) (${#EXISTED_PROTOCOLS[@]})"
|
|
for h in "${!EXISTED_PROTOCOLS[@]}"; do
|
|
hint " $(asc $[h+97]). ${EXISTED_PROTOCOLS[h]} "
|
|
done
|
|
|
|
# 从已安装的协议中选择需要删除的协议名,并存放在 REMOVE_PROTOCOLS,把保存的协议的协议存放在 KEEP_PROTOCOLS
|
|
reading "\n $(text 64) " REMOVE_SELECT
|
|
# 统一为小写,去掉重复选项,处理不在可选列表里的选项,把特殊符号处理
|
|
REMOVE_SELECT=$(sed "s/[^a-$(asc $[${#EXISTED_PROTOCOLS[@]} + 96])]//g" <<< "${REMOVE_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}')
|
|
|
|
for ((j=0; j<${#REMOVE_SELECT}; j++)); do
|
|
REMOVE_PROTOCOLS+=("${EXISTED_PROTOCOLS[$[$(asc "$(awk "NR==$[j+1] {print}" <<< "$(grep -o . <<< "$REMOVE_SELECT")")") - 97]]}")
|
|
done
|
|
|
|
for k in "${EXISTED_PROTOCOLS[@]}"; do
|
|
[[ ! "${REMOVE_PROTOCOLS[@]}" =~ "$k" ]] && KEEP_PROTOCOLS+=("$k")
|
|
done
|
|
|
|
# 如有未安装的协议,列表显示并选择安装,把增加的协议存在放在 ADD_PROTOCOLS
|
|
if [ "${#NOT_EXISTED_PROTOCOLS[@]}" -gt 0 ]; then
|
|
hint "\n $(text 65) (${#NOT_EXISTED_PROTOCOLS[@]}) "
|
|
for i in "${!NOT_EXISTED_PROTOCOLS[@]}"; do
|
|
hint " $(asc $[i+97]). ${NOT_EXISTED_PROTOCOLS[i]} "
|
|
done
|
|
reading "\n $(text 66) " ADD_SELECT
|
|
# 统一为小写,去掉重复选项,处理不在可选列表里的选项,把特殊符号处理
|
|
ADD_SELECT=$(sed "s/[^a-$(asc $[${#NOT_EXISTED_PROTOCOLS[@]} + 96])]//g" <<< "${ADD_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}')
|
|
|
|
for ((l=0; l<${#ADD_SELECT}; l++)); do
|
|
ADD_PROTOCOLS+=("${NOT_EXISTED_PROTOCOLS[$[$(asc "$(awk "NR==$[l+1] {print}" <<< "$(grep -o . <<< "$ADD_SELECT")")") - 97]]}")
|
|
done
|
|
fi
|
|
|
|
# 重新安装 = 保留 + 新增,如数量为 0 ,则触发卸载
|
|
REINSTALL_PROTOCOLS=("${KEEP_PROTOCOLS[@]}" "${ADD_PROTOCOLS[@]}")
|
|
[ "${#REINSTALL_PROTOCOLS[@]}" = 0 ] && error "\n $(text 73) "
|
|
|
|
# 显示重新安装的协议列表,并确认是否正确
|
|
hint "\n $(text 67) (${#REINSTALL_PROTOCOLS[@]}) "
|
|
[ "${#KEEP_PROTOCOLS[@]}" -gt 0 ] && hint "\n $(text 74) (${#KEEP_PROTOCOLS[@]}) "
|
|
for r in "${!KEEP_PROTOCOLS[@]}"; do
|
|
hint " $[r+1]. ${KEEP_PROTOCOLS[r]} "
|
|
done
|
|
|
|
[ "${#ADD_PROTOCOLS[@]}" -gt 0 ] && hint "\n $(text 75) (${#ADD_PROTOCOLS[@]}) "
|
|
for r in "${!ADD_PROTOCOLS[@]}"; do
|
|
hint " $[r+1]. ${ADD_PROTOCOLS[r]} "
|
|
done
|
|
|
|
reading "\n $(text 68) " CONFIRM
|
|
[ "${CONFIRM,,}" = 'n' ] && exit 0
|
|
|
|
# 把确认安装的协议遍历所有协议列表的数组,找出其下标并变为英文小写的形式
|
|
for m in "${!REINSTALL_PROTOCOLS[@]}"; do
|
|
for n in "${!PROTOCOL_LIST[@]}"; do
|
|
if [ "${REINSTALL_PROTOCOLS[m]}" = "${PROTOCOL_LIST[n]}" ]; then
|
|
INSTALL_PROTOCOLS+=($(asc $[n+98]))
|
|
fi
|
|
done
|
|
done
|
|
|
|
systemctl stop sing-box
|
|
|
|
# 获取各节点信息
|
|
fetch_nodes_value
|
|
|
|
# 用于新节点的配置信息
|
|
UUID=$(awk '{print $1}' <<< "${UUID[@]} $TROJAN_PASSWORD")
|
|
for v in "${NODE_NAME[@]}"; do
|
|
[ -n "$v" ] && NODE_NAME="$v" && break
|
|
done
|
|
[ "${#WS_SERVER_IP[@]}" -gt 0 ] && WS_SERVER_IP=$(awk '{print $1}' <<< "${WS_SERVER_IP[@]}") && CDN=$(awk '{print $1}' <<< "${CDN[@]}")
|
|
|
|
# 寻找待删除协议的 inbound 文件名
|
|
for o in "${REMOVE_PROTOCOLS[@]}"; do
|
|
for ((s=0; s<${#PROTOCOL_LIST[@]}; s++)); do
|
|
[ "$o" = "${PROTOCOL_LIST[s]}" ] && REMOVE_FILE+=("${NODE_TAG[s]}_inbounds.json")
|
|
done
|
|
done
|
|
|
|
# 删除不需要的协议配置文件
|
|
[ "${#REMOVE_FILE[@]}" -gt 0 ] && for t in "${REMOVE_FILE[@]}"; do
|
|
rm -f $WORK_DIR/conf/*${t}
|
|
done
|
|
|
|
# 寻找已存在协议中原有的端口号
|
|
for p in "${KEEP_PROTOCOLS[@]}"; do
|
|
for ((u=0; u<${#PROTOCOL_LIST[@]}; u++)); do
|
|
[ "$p" = "${PROTOCOL_LIST[u]}" ] && KEEP_PORTS+=("$(awk -F '[:,]' '/listen_port/{print $2}' $WORK_DIR/conf/*${NODE_TAG[u]}_inbounds.json)")
|
|
done
|
|
done
|
|
|
|
# 根据全部协议,找到空余的端口号
|
|
for q in "${!REINSTALL_PROTOCOLS[@]}"; do
|
|
[[ ! ${KEEP_PORTS[@]} =~ $[START_PORT + q] ]] && ADD_PORTS+=($[START_PORT + q])
|
|
done
|
|
|
|
# 所有协议的端口号
|
|
REINSTALL_PORTS=(${KEEP_PORTS[@]} ${ADD_PORTS[@]})
|
|
|
|
CHECK_PROTOCOLS=b
|
|
# 获取原始 Reality 配置信息
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_XTLS_REALITY=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 Hysteria2 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_HYSTERIA2=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 Tuic V5 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_TUIC=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 ShadowTLS 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_SHADOWTLS=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 Shadowsocks 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_SHADOWSOCKS=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 Trojan 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_TROJAN=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 vmess + ws 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
local DOMAIN_ERROR_TIME=5
|
|
until [ -n "$VMESS_HOST_DOMAIN" ]; do
|
|
(( DOMAIN_ERROR_TIME-- )) || true
|
|
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VMESS && reading "\n $(text 50) " VMESS_HOST_DOMAIN || error "\n $(text 3) \n"
|
|
done
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_VMESS_WS=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 vless + ws + tls 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
local DOMAIN_ERROR_TIME=5
|
|
until [ -n "$VLESS_HOST_DOMAIN" ]; do
|
|
(( DOMAIN_ERROR_TIME-- )) || true
|
|
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VLESS && reading "\n $(text 50) " VLESS_HOST_DOMAIN || error "\n $(text 3) \n"
|
|
done
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_VLESS_WS=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 H2 + Reality 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_H2_REALITY=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 获取原始 gRPC + Reality 配置信息
|
|
CHECK_PROTOCOLS=$(asc "$CHECK_PROTOCOLS" ++)
|
|
if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then
|
|
POSITION=$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")
|
|
PORT_GRPC_REALITY=${REINSTALL_PORTS[POSITION]}
|
|
fi
|
|
|
|
# 选择可以输入 cdn
|
|
input_cdn
|
|
|
|
# 生成各协议的 json 文件
|
|
sing-box_json change
|
|
|
|
systemctl start sing-box
|
|
|
|
# 再次检测状态,运行 Sing-box
|
|
check_install
|
|
|
|
check_sing-box_stats
|
|
|
|
export_list
|
|
}
|
|
|
|
# 卸载 Sing-box 全家桶
|
|
uninstall() {
|
|
if [ -d $WORK_DIR ]; then
|
|
[ "$SYSTEM" = 'Alpine' ] && systemctl stop sing-box 2>/dev/null || cmd_systemctl disable sing-box 2>/dev/null
|
|
rm -rf $WORK_DIR $TEMP_DIR /etc/systemd/system/sing-box.service /usr/bin/sb
|
|
info "\n $(text 16) \n"
|
|
else
|
|
error "\n $(text 15) \n"
|
|
fi
|
|
|
|
# 如果 Alpine 系统,删除开机自启动
|
|
[ "$SYSTEM" = 'Alpine' ] && ( rm -f /etc/local.d/sing-box.start; rc-update add local >/dev/null 2>&1 )
|
|
}
|
|
|
|
# Sing-box 的最新版本
|
|
version() {
|
|
local VERSION_LATEST=$(wget --no-check-certificate -qO- "https://api.github.com/repos/SagerNet/sing-box/releases" | awk -F '["v-]' '/tag_name/{print $5}' | sort -r | sed -n '1p')
|
|
local ONLINE=$(wget --no-check-certificate -qO- "https://api.github.com/repos/SagerNet/sing-box/releases" | awk -F '["v]' -v var="tag_name.*$VERSION_LATEST" '$0 ~ var {print $5; exit}')
|
|
local LOCAL=$($WORK_DIR/sing-box version | awk '/version/{print $NF}')
|
|
info "\n $(text 40) "
|
|
[[ -n "$ONLINE" && "$ONLINE" != "$LOCAL" ]] && reading "\n $(text 9) " UPDATE || info " $(text 41) "
|
|
|
|
if [ "${UPDATE,,}" = 'y' ]; then
|
|
check_system_info
|
|
wget --no-check-certificate -c https://${GH_PROXY}github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box
|
|
|
|
if [ -s $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box ]; then
|
|
systemctl stop sing-box
|
|
chmod +x $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box && mv $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box $WORK_DIR/sing-box
|
|
systemctl start sing-box && sleep 2 && [ "$(systemctl is-active sing-box)" = 'active' ] && info " Sing-box $(text 28) $(text 37)" || error "Sing-box $(text 28) $(text 38) "
|
|
else
|
|
local error "\n $(text 42) "
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# 判断当前 Sing-box 的运行状态,并对应的给菜单和动作赋值
|
|
menu_setting() {
|
|
if [[ "$STATUS" =~ $(text 27)|$(text 28) ]]; then
|
|
# 查进程号,sing-box 运行时长 和 内存占用
|
|
if [ "$STATUS" = "$(text 28)" ]; then
|
|
if [ "$SYSTEM" = 'Alpine' ]; then
|
|
PID=$(pidof sing-box | sed -n 1p)
|
|
else
|
|
SYSTEMCTL_STATUS=$(systemctl status sing-box)
|
|
PID=$(awk '/PID/{print $3}' <<< "$SYSTEMCTL_STATUS")
|
|
RUNTIME=$(awk '/Active:/{for (i=5;i<=NF;i++)printf("%s ", $i);print ""}' <<< "$SYSTEMCTL_STATUS")
|
|
fi
|
|
MEMORY_USAGE="$(awk '/VmRSS/{printf "%.1f\n", $2/1024}' /proc/$PID/status)"
|
|
fi
|
|
NOW_PORTS=$(awk -F ':|,' '/listen_port/{print $2}' $WORK_DIR/conf/*)
|
|
NOW_START_PORT=$(awk 'NR == 1 { min = $0 } { if ($0 < min) min = $0; count++ } END {print min}' <<< "$NOW_PORTS")
|
|
NOW_CONSECUTIVE_PORTS=$(awk 'END { print NR }' <<< "$NOW_PORTS")
|
|
[ -s $WORK_DIR/sing-box ] && SING_BOX_VERSION="version: $($WORK_DIR/sing-box version | awk '/version/{print $NF}')"
|
|
[ -s $WORK_DIR/conf/02_route.json ] && { grep -q 'direct' $WORK_DIR/conf/02_route.json && RETURN_STATUS=$(text 27) || RETURN_STATUS=$(text 28); }
|
|
OPTION[1]="1 . $(text 29)"
|
|
[ "$STATUS" = "$(text 28)" ] && OPTION[2]="2 . $(text 27) Sing-box (sb -o)" || OPTION[2]="2 . $(text 28) Sing-box (sb -o)"
|
|
OPTION[3]="3 . $(text 30)"
|
|
OPTION[4]="4 . $(text 31)"
|
|
OPTION[5]="5 . $(text 32)"
|
|
OPTION[6]="6 . $(text 62)"
|
|
OPTION[7]="7 . $(text 33)"
|
|
OPTION[8]="8 . $(text 59)"
|
|
OPTION[9]="9 . $(text 69)"
|
|
OPTION[10]="10. $(text 76)"
|
|
|
|
ACTION[1]() { export_list; exit 0; }
|
|
[ "$STATUS" = "$(text 28)" ] && ACTION[2]() { cmd_systemctl disable sing-box; [ "$(systemctl is-active sing-box)" = 'inactive' ] && info " Sing-box $(text 27) $(text 37)" || error " Sing-box $(text 27) $(text 38) "; } || ACTION[2]() { cmd_systemctl enable sing-box && [ "$(systemctl is-active sing-box)" = 'active' ] && info " Sing-box $(text 28) $(text 37)" || error " Sing-box $(text 28) $(text 38) "; }
|
|
ACTION[3]() { change_start_port; exit; }
|
|
ACTION[4]() { version; exit; }
|
|
ACTION[5]() { bash <(wget --no-check-certificate -qO- "https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh"); exit; }
|
|
ACTION[6]() { change_protocols; exit; }
|
|
ACTION[7]() { uninstall; exit; }
|
|
ACTION[8]() { bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fscarmen/argox/main/argox.sh) -$L; exit; }
|
|
ACTION[9]() { bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fscarmen/sba/main/sba.sh) -$L; exit; }
|
|
ACTION[10]() { bash <(wget --no-check-certificate -qO- https://tcp.hy2.sh/); exit; }
|
|
else
|
|
OPTION[1]="1. $(text 34)"
|
|
OPTION[2]="2. $(text 32)"
|
|
OPTION[3]="3. $(text 59)"
|
|
OPTION[4]="4. $(text 69)"
|
|
OPTION[5]="5. $(text 76)"
|
|
|
|
ACTION[1]() { install_sing-box; export_list install; create_shortcut; exit; }
|
|
ACTION[2]() { bash <(wget --no-check-certificate -qO- "https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh"); exit; }
|
|
ACTION[3]() { bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fscarmen/argox/main/argox.sh) -$L; exit; }
|
|
ACTION[4]() { bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fscarmen/sba/main/sba.sh) -$L; exit; }
|
|
ACTION[5]() { bash <(wget --no-check-certificate -qO- https://tcp.hy2.sh/); exit; }
|
|
fi
|
|
|
|
[ "${#OPTION[@]}" -ge '10' ] && OPTION[0]="0 . $(text 35)" || OPTION[0]="0. $(text 35)"
|
|
ACTION[0]() { exit; }
|
|
}
|
|
|
|
menu() {
|
|
clear
|
|
hint " $(text 2) "
|
|
echo -e "======================================================================================================================\n"
|
|
info " $(text 17): $VERSION\n $(text 18): $(text 1)\n $(text 19):\n\t $(text 20): $SYS\n\t $(text 21): $(uname -r)\n\t $(text 22): $SING_BOX_ARCH\n\t $(text 23): $VIRT "
|
|
info "\t IPv4: $WAN4 $WARPSTATUS4 $COUNTRY4 $ASNORG4 "
|
|
info "\t IPv6: $WAN6 $WARPSTATUS6 $COUNTRY6 $ASNORG6 "
|
|
info "\t Sing-box: $STATUS\t $SING_BOX_VERSION "
|
|
[ -n "$PID" ] && info "\t $(text 56): $PID "
|
|
[ -n "$RUNTIME" ] && info "\t $(text 57): $RUNTIME "
|
|
[ -n "$MEMORY_USAGE" ] && info "\t $(text 58): $MEMORY_USAGE MB"
|
|
[ -n "$NOW_START_PORT" ] && info "\t $(text 45) "
|
|
echo -e "\n======================================================================================================================\n"
|
|
for ((b=1;b<${#OPTION[*]};b++)); do hint " ${OPTION[b]} "; done
|
|
hint " ${OPTION[0]} "
|
|
reading "\n $(text 24) " CHOOSE
|
|
|
|
# 输入必须是数字且少于等于最大可选项
|
|
if grep -qE "^[0-9]{1,2}$" <<< "$CHOOSE" && [ "$CHOOSE" -lt "${#OPTION[*]}" ]; then
|
|
ACTION[$CHOOSE]
|
|
else
|
|
warning " $(text 36) [0-$((${#OPTION[*]}-1))] " && sleep 1 && menu
|
|
fi
|
|
}
|
|
|
|
statistics_of_run-times
|
|
|
|
# 传参
|
|
[[ "${*,,}" =~ '-e' ]] && L=E
|
|
[[ "${*,,}" =~ '-c' ]] && L=C
|
|
|
|
while getopts ":P:p:OoUuVvNnBbRr" OPTNAME; do
|
|
case "${OPTNAME,,}" in
|
|
p ) START_PORT=$OPTARG; select_language; check_install; [ "$STATUS" = "$(text 26)" ] && error "\n Sing-box $(text 26) "; change_start_port; exit 0 ;;
|
|
o ) select_language; check_system_info; check_install; [ "$STATUS" = "$(text 26)" ] && error "\n Sing-box $(text 26) "; [ "$STATUS" = "$(text 28)" ] && ( cmd_systemctl disable sing-box; [ "$(systemctl is-active sing-box)" = 'inactive' ] && info "\n Sing-box $(text 27) $(text 37)" ) || ( cmd_systemctl enable sing-box && [ "$(systemctl is-active sing-box)" = 'active' ] && info "\n Sing-box $(text 28) $(text 37)" ); exit 0;;
|
|
u ) select_language; check_system_info; uninstall; exit 0 ;;
|
|
n ) select_language; [ ! -s $WORK_DIR/list ] && error " Sing-box $(text 26) "; export_list; exit 0 ;;
|
|
v ) select_language; check_arch; version; exit 0 ;;
|
|
b ) select_language; bash <(wget --no-check-certificate -qO- "https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh"); exit ;;
|
|
r ) select_language; check_system_info; change_protocols; exit 0 ;;
|
|
esac
|
|
done
|
|
|
|
select_language
|
|
check_root
|
|
check_arch
|
|
check_system_info
|
|
check_dependencies
|
|
check_system_ip
|
|
check_install
|
|
menu_setting
|
|
menu |