Files
Sing-box/sb_serv00.sh
T
2025-12-09 22:56:22 +08:00

792 lines
29 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
re="\033[0m"
red="\033[1;91m"
green="\e[1;32m"
yellow="\e[1;33m"
purple="\e[1;35m"
red() { echo -e "\e[1;91m$1\033[0m"; }
green() { echo -e "\e[1;32m$1\033[0m"; }
yellow() { echo -e "\e[1;33m$1\033[0m"; }
purple() { echo -e "\e[1;35m$1\033[0m"; }
reading() { read -p "$(red "$1")" "$2"; }
export LC_ALL=C
HOSTNAME=$(hostname)
USERNAME=$(whoami | tr '[:upper:]' '[:lower:]')
export UUID=${UUID:-$(uuidgen -r)}
export NEZHA_SERVER=${NEZHA_SERVER:-''} # v1哪吒形式:nezha.abc.com:8008,v0哪吒形式:nezha.abc.com
export NEZHA_PORT=${NEZHA_PORT:-''} # v1哪吒不需要此变量
export NEZHA_KEY=${NEZHA_KEY:-''} # v1的NZ_CLIENT_SECRET或v0的agent密钥
export ARGO_DOMAIN=${ARGO_DOMAIN:-''}
export ARGO_AUTH=${ARGO_AUTH:-''}
export CFIP=${CFIP:-'cf.877774.xyz'}
export CFPORT=${CFPORT:-'443'}
export SUB_TOKEN=${SUB_TOKEN:-${UUID:0:8}}
export UPLOAD_URL=${UPLOAD_URL:-''} # 订阅自动添加到汇聚订阅器,需要先部署Merge-sub项目,环境变量填写部署后的首页地址,例如: SUB_URL=https://merge.serv00.net
if [[ "$HOSTNAME" =~ ct8 ]]; then
CURRENT_DOMAIN="ct8.pl"
elif [[ "$HOSTNAME" =~ hostuno ]]; then
CURRENT_DOMAIN="useruno.com"
else
CURRENT_DOMAIN="serv00.net"
fi
WORKDIR="${HOME}/domains/${USERNAME}.${CURRENT_DOMAIN}/logs"
FILE_PATH="${HOME}/domains/${USERNAME}.${CURRENT_DOMAIN}/public_html"
rm -rf "$WORKDIR" && mkdir -p "$WORKDIR" "$FILE_PATH" && chmod 777 "$WORKDIR" "$FILE_PATH" >/dev/null 2>&1
command -v curl &>/dev/null && COMMAND="curl -so" || command -v wget &>/dev/null && COMMAND="wget -qO" || { red "Error: neither curl nor wget found, please install one of them." >&2; exit 1; }
check_port () {
port_list=$(devil port list)
tcp_ports=$(echo "$port_list" | grep -c "tcp")
udp_ports=$(echo "$port_list" | grep -c "udp")
if [[ $tcp_ports -ne 1 || $udp_ports -ne 2 ]]; then
red "端口规则不符合要求,正在调整..."
if [[ $tcp_ports -gt 1 ]]; then
tcp_to_delete=$((tcp_ports - 1))
echo "$port_list" | awk '/tcp/ {print $1, $2}' | head -n $tcp_to_delete | while read port type; do
devil port del $type $port >/dev/null 2>&1
green "已删除TCP端口: $port"
done
fi
if [[ $udp_ports -gt 2 ]]; then
udp_to_delete=$((udp_ports - 2))
echo "$port_list" | awk '/udp/ {print $1, $2}' | head -n $udp_to_delete | while read port type; do
devil port del $type $port >/dev/null 2>&1
green "已删除UDP端口: $port"
done
fi
if [[ $tcp_ports -lt 1 ]]; then
while true; do
tcp_port=$(shuf -i 10000-65535 -n 1)
result=$(devil port add tcp $tcp_port 2>&1)
if [[ $result == *"Ok"* ]]; then
green "已添加TCP端口: $tcp_port"
break
else
yellow "端口 $tcp_port 不可用,尝试其他端口..."
fi
done
fi
if [[ $udp_ports -lt 2 ]]; then
udp_ports_to_add=$((2 - udp_ports))
udp_ports_added=0
while [[ $udp_ports_added -lt $udp_ports_to_add ]]; do
udp_port=$(shuf -i 10000-65535 -n 1)
result=$(devil port add udp $udp_port 2>&1)
if [[ $result == *"Ok"* ]]; then
green "已添加UDP端口: $udp_port"
if [[ $udp_ports_added -eq 0 ]]; then
udp_port1=$udp_port
else
udp_port2=$udp_port
fi
udp_ports_added=$((udp_ports_added + 1))
else
yellow "端口 $udp_port 不可用,尝试其他端口..."
fi
done
fi
green "端口已调整完成,将断开ssh连接,请重新连接shh重新执行脚本"
quick_command
devil binexec on >/dev/null 2>&1
kill -9 $(ps -o ppid= -p $$) >/dev/null 2>&1
else
tcp_port=$(echo "$port_list" | awk '/tcp/ {print $1}')
udp_ports=$(echo "$port_list" | awk '/udp/ {print $1}')
udp_port1=$(echo "$udp_ports" | sed -n '1p')
udp_port2=$(echo "$udp_ports" | sed -n '2p')
fi
purple "vmess-argo使用的tcp端口为: $tcp_port"
purple "tuic和hy2使用的udp端口分别为: $udp_port1$udp_port2"
export VMESS_PORT=$tcp_port
export TUIC_PORT=$udp_port1
export HY2_PORT=$udp_port2
}
changge_ports() {
reading "将删除全部端口然后随机开放1个tcp端口和2个udp端口,确定继续吗?(直接回车即确认更换)【y/n】: " choice
if [[ -z "$choice" || "$choice" == "y" || "$choice" == "Y" ]]; then
devil port list | grep -E "^\s*[0-9]+" | while read -r line; do
port=$(echo "$line" | awk '{print $1}')
proto=$(echo "$line" | awk '{print $2}')
if [[ "$proto" != "tcp" && "$proto" != "udp" ]]; then
continue
fi
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
continue
fi
if devil port del "${proto}" "${port}" > /dev/null 2>&1; then
green "Port ${port}/${proto} has been removed successfully"
else
red "Failed to remove port ${port}/${proto}"
fi
done
check_port
else
menu
fi
}
check_website() {
FULL_DOMAIN="${USERNAME}.${CURRENT_DOMAIN}"
CURRENT_SITE=$(devil www list | awk -v domain="$FULL_DOMAIN" '$1 == domain && $2 == "php"')
if [ -n "$CURRENT_SITE" ]; then
green "已存在 ${FULL_DOMAIN} 的PHP站点,无需修改"
else
EXIST_SITE=$(devil www list | awk -v domain="$FULL_DOMAIN" '$1 == domain')
if [ -n "$EXIST_SITE" ]; then
devil www del "$FULL_DOMAIN" >/dev/null 2>&1
devil www add "$FULL_DOMAIN" php "$HOME/domains/$FULL_DOMAIN" >/dev/null 2>&1
green "已删除旧的站点并添加新的php站点"
else
devil www add "$FULL_DOMAIN" php "$HOME/domains/$FULL_DOMAIN" >/dev/null 2>&1
green "已创建新PHP站点 ${FULL_DOMAIN}"
fi
fi
index_url="https://github.com/eooce/Sing-box/releases/download/00/index.html"
[ -f "${FILE_PATH}/index.html" ] || $COMMAND "${FILE_PATH}/index.html" "$index_url"
}
read_variables() {
if [ -n "$NEZHA_SERVER" ] && [ -n "$NEZHA_KEY" ]; then
green "使用自定义变量哪吒运行哪吒探针"
return
else
reading "是否需要安装哪吒探针?(直接回车则不安装)【y/n】: " nz_choice
[[ -z $nz_choice ]] && return
[[ "$nz_choice" != "y" && "$nz_choice" != "Y" ]] && return
reading "\n请输入哪吒探针域名或ip\nv1哪吒形式:nezha.abc.com:8008,v0哪吒形式:nezha.abc.com " NEZHA_SERVER
green "你的哪吒域名为: $NEZHA_SERVER"
if [[ "$NEZHA_SERVER" != *":"* ]]; then
reading "请输入哪吒v0探针端口(直接回车将设置为5555):" NEZHA_PORT
[[ -z $NEZHA_PORT ]] && NEZHA_PORT="5555"
green "你的哪吒端口为: $NEZHA_PORT"
else
NEZHA_PORT=""
fi
reading "请输入v0的agent密钥或v1的NZ_CLIENT_SECRET" NEZHA_KEY
green "你的哪吒密钥为: $NEZHA_PORT"
reading "是否需要Telegram通知?(直接回车则不启用)【y/n】: " tg_notification
if [[ "$tg_notification" == "y" || "$tg_notification" == "Y" ]]; then
reading "请输入Telegram chat ID (tg上@laowang_serv00_bot获取): " tg_chat_id
[[ -z $tg_chat_id ]] && { red "Telegram chat ID不能为空"; return; }
green "你设置的Telegram chat_id为: ${tg_chat_id}"
reading "请输入Telegram Bot Token (直接回车使用老王的bot通知或填写自己的): " tg_token
[[ -z $tg_token ]] && tg_token=""
green "你设置的Telegram bot token为: ${tg_token}"
fi
fi
}
install_singbox() {
bash -c 'ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk "{print \$2}" | xargs -r kill -9 >/dev/null 2>&1' >/dev/null 2>&1
echo -e "${yellow}本脚本同时四协议共存${purple}(vmess-ws,vmess-ws-tls(argo),hysteria2,tuic)${re}"
reading "\n确定继续安装吗?(直接回车即确认安装)【y/n】: " choice
case "${choice:-y}" in
[Yy]|"")
clear
cd $WORKDIR
check_port
check_website
read_variables
argo_configure
generate_config
download_singbox
get_links
;;
[Nn]) exit 0 ;;
*) red "无效的选择,请输入y或n" && menu ;;
esac
}
uninstall_singbox() {
reading "\n确定要卸载吗?【y/n】: " choice
case "$choice" in
[Yy])
bash -c 'ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk "{print \$2}" | xargs -r kill -9 >/dev/null 2>&1' >/dev/null 2>&1
rm -rf $WORKDIR && find ${FILE_PATH} -mindepth 1 ! -name 'index.html' -exec rm -rf {} +
devil www del keep.${USERNAME}.${CURRENT_DOMAIN} 2>/dev/null || true
rm -rf ${HOME}/domains/${USERNAME}.${CURRENT_DOMAIN}/public_nodejs 2 >/dev/null || true
rm -rf "${HOME}/bin/00" >/dev/null 2>&1
[ -d "${HOME}/bin" ] && [ -z "$(ls -A "${HOME}/bin")" ] && rmdir "${HOME}/bin"
sed -i '/export PATH="\$HOME\/bin:\$PATH"/d' "${HOME}/.bashrc" >/dev/null 2>&1
source "${HOME}/.bashrc"
clear
green "Sing-box四合一已完全卸载"
;;
[Nn]) exit 0 ;;
*) red "无效的选择,请输入y或n" && menu ;;
esac
}
reset_system() {
reading "\n确定重置系统吗吗?【y/n】: " choice
case "$choice" in
[Yy]) yellow "\n初始化系统中,请稍后...\n"
bash -c 'ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk "{print \$2}" | xargs -r kill -9 >/dev/null 2>&1' >/dev/null 2>&1
find "${HOME}" -mindepth 1 ! -name "domains" ! -name "mail" ! -name "repo" ! -name "backups" -exec rm -rf {} + > /dev/null 2>&1
devil www list | awk 'NF>=2 && $1 ~ /\./ {print $1}' | while read -r domain; do devil www del "$domain"; done
rm -rf $HOME/domains/* > /dev/null 2>&1
green "\n初始化系统完成!\n"
;;
*) menu ;;
esac
}
argo_configure() {
if [[ -z $ARGO_AUTH || -z $ARGO_DOMAIN ]]; then
reading "是否需要使用固定argo隧道?(直接回车将使用临时隧道)【y/n】: " argo_choice
[[ -z $argo_choice ]] && return
[[ "$argo_choice" != "y" && "$argo_choice" != "Y" && "$argo_choice" != "n" && "$argo_choice" != "N" ]] && { red "无效的选择,请输入y或n"; return; }
if [[ "$argo_choice" == "y" || "$argo_choice" == "Y" ]]; then
reading "请输入argo固定隧道域名: " ARGO_DOMAIN
green "你的argo固定隧道域名为: $ARGO_DOMAIN"
reading "请输入argo固定隧道密钥(Json或Token: " ARGO_AUTH
green "你的argo固定隧道密钥为: $ARGO_AUTH"
else
green "ARGO隧道变量未设置,将使用临时隧道"
return
fi
fi
if [[ $ARGO_AUTH =~ TunnelSecret ]]; then
echo $ARGO_AUTH > tunnel.json
cat > tunnel.yml << EOF
tunnel: $(cut -d\" -f12 <<< "$ARGO_AUTH")
credentials-file: tunnel.json
protocol: http2
ingress:
- hostname: $ARGO_DOMAIN
service: http://localhost:$VMESS_PORT
originRequest:
noTLSVerify: true
- service: http_status:404
EOF
else
yellow "\n当前使用的是token,请在cloudflare后台设置隧道端口为${purple}${VMESS_PORT}${re}"
fi
}
generate_config() {
openssl ecparam -genkey -name prime256v1 -out "private.key"
openssl req -new -x509 -days 3650 -key "private.key" -out "cert.pem" -subj "/CN=$USERNAME.${CURRENT_DOMAIN}"
yellow "获取可用IP中,请稍等..."
available_ip=$(get_ip)
purple "当前选择IP为:$available_ip 如安装完后节点不通可尝试重新安装"
cat > config.json <<EOF
{
"log": {
"disabled": true,
"level": "info",
"timestamp": true
},
"dns": {
"servers": [
{
"address": "8.8.8.8",
"address_resolver": "local"
},
{
"tag": "local",
"address": "local"
}
]
},
"inbounds": [
{
"tag": "hysteria-in",
"type": "hysteria2",
"listen": "$available_ip",
"listen_port": $HY2_PORT,
"users": [
{
"password": "$UUID"
}
],
"masquerade": "https://bing.com",
"tls": {
"enabled": true,
"alpn": ["h3"],
"certificate_path": "cert.pem",
"key_path": "private.key"
}
},
{
"tag": "vmess-ws-in",
"type": "vmess",
"listen": "::",
"listen_port": $VMESS_PORT,
"users": [
{
"uuid": "$UUID"
}
],
"transport": {
"type": "ws",
"path": "/vmess-argo",
"early_data_header_name": "Sec-WebSocket-Protocol"
}
},
{
"tag": "tuic-in",
"type": "tuic",
"listen": "$available_ip",
"listen_port": $TUIC_PORT,
"users": [
{
"uuid": "$UUID",
"password": "admin123"
}
],
"congestion_control": "bbr",
"tls": {
"enabled": true,
"alpn": ["h3"],
"certificate_path": "cert.pem",
"key_path": "private.key"
}
}
],
EOF
# 如果是s14/s15,google/youtube/spotify相关的服务走warp出站
if [[ "$HOSTNAME" =~ s14|s15 ]]; then
cat >> config.json <<EOF
"outbounds": [
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
},
{
"type": "wireguard",
"tag": "wireguard-out",
"server": "162.159.192.200",
"server_port": 4500,
"local_address": [
"172.16.0.2/32",
"2606:4700:110:8f77:1ca9:f086:846c:5f9e/128"
],
"private_key": "wIxszdR2nMdA7a2Ul3XQcniSfSZqdqjPb6w6opvf5AU=",
"peer_public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
"reserved": [126, 246, 173]
}
],
"route": {
"rule_set": [
{
"tag": "youtube",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo-lite/geosite/youtube.srs",
"download_detour": "direct"
},
{
"tag": "google",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo-lite/geosite/google.srs",
"download_detour": "direct"
},
{
"tag": "spotify",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo-lite/geosite/spotify.srs",
"download_detour": "direct"
}
],
"rules": [
{
"rule_set": ["google", "youtube", "spotify"],
"outbound": "wireguard-out"
}
],
"final": "direct"
}
}
EOF
else
cat >> config.json <<EOF
"outbounds": [
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
}
]
}
EOF
fi
}
download_singbox() {
ARCH=$(uname -m) && DOWNLOAD_DIR="." && mkdir -p "$DOWNLOAD_DIR" && FILE_INFO=()
if [ "$ARCH" == "arm" ] || [ "$ARCH" == "arm64" ] || [ "$ARCH" == "aarch64" ]; then
BASE_URL="https://github.com/eooce/test/releases/download/freebsd-arm64"
elif [ "$ARCH" == "amd64" ] || [ "$ARCH" == "x86_64" ] || [ "$ARCH" == "x86" ]; then
BASE_URL="https://github.com/eooce/test/releases/download/freebsd"
else
echo "Unsupported architecture: $ARCH"
exit 1
fi
FILE_INFO=("$BASE_URL/sb web" "$BASE_URL/server bot")
if [ -n "$NEZHA_PORT" ]; then
FILE_INFO+=("$BASE_URL/npm npm")
else
FILE_INFO+=("$BASE_URL/v1 php")
NEZHA_TLS=$(case "${NEZHA_SERVER##*:}" in 443|8443|2096|2087|2083|2053) echo -n tls;; *) echo -n false;; esac)
cat > "${WORKDIR}/config.yaml" << EOF
client_secret: ${NEZHA_KEY}
debug: false
disable_auto_update: true
disable_command_execute: false
disable_force_update: true
disable_nat: false
disable_send_query: false
gpu: false
insecure_tls: false
ip_report_period: 1800
report_delay: 1
server: ${NEZHA_SERVER}
skip_connection_count: false
skip_procs_count: false
temperature: false
tls: ${NEZHA_TLS}
use_gitee_to_upgrade: false
use_ipv6_country_code: false
uuid: ${UUID}
EOF
fi
declare -A FILE_MAP
generate_random_name() {
local chars=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
local name=""
for i in {1..6}; do
name="$name${chars:RANDOM%${#chars}:1}"
done
echo "$name"
}
download_with_fallback() {
local URL=$1
local NEW_FILENAME=$2
curl -L -sS --max-time 2 -o "$NEW_FILENAME" "$URL" &
CURL_PID=$!
CURL_START_SIZE=$(stat -c%s "$NEW_FILENAME" 2>/dev/null || echo 0)
sleep 1
CURL_CURRENT_SIZE=$(stat -c%s "$NEW_FILENAME" 2>/dev/null || echo 0)
if [ "$CURL_CURRENT_SIZE" -le "$CURL_START_SIZE" ]; then
kill $CURL_PID 2>/dev/null
wait $CURL_PID 2>/dev/null
wget -q -O "$NEW_FILENAME" "$URL"
green "Downloading $NEW_FILENAME by wget"
else
wait $CURL_PID
green "Downloading $NEW_FILENAME by curl"
fi
}
for entry in "${FILE_INFO[@]}"; do
URL=$(echo "$entry" | cut -d ' ' -f 1)
RANDOM_NAME=$(generate_random_name)
NEW_FILENAME="$DOWNLOAD_DIR/$RANDOM_NAME"
download_with_fallback "$URL" "$NEW_FILENAME"
chmod +x "$NEW_FILENAME"
FILE_MAP[$(echo "$entry" | cut -d ' ' -f 2)]="$NEW_FILENAME"
done
wait
if [ -e "$(basename ${FILE_MAP[web]})" ]; then
nohup ./"$(basename ${FILE_MAP[web]})" run -c config.json >/dev/null 2>&1 &
sleep 2
pgrep -x "$(basename ${FILE_MAP[web]})" > /dev/null && green "$(basename ${FILE_MAP[web]}) is running" || { red "$(basename ${FILE_MAP[web]}) is not running, restarting..."; pkill -x "$(basename ${FILE_MAP[web]})" && nohup ./"$(basename ${FILE_MAP[web]})" run -c config.json >/dev/null 2>&1 & sleep 2; purple "$(basename ${FILE_MAP[web]}) restarted"; }
fi
if [ -e "$(basename ${FILE_MAP[bot]})" ]; then
if [[ $ARGO_AUTH =~ ^[A-Z0-9a-z=]{120,250}$ ]]; then
args="tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}"
elif [[ $ARGO_AUTH =~ TunnelSecret ]]; then
args="tunnel --edge-ip-version auto --config tunnel.yml run"
else
args="tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile boot.log --loglevel info --url http://localhost:$VMESS_PORT"
fi
nohup ./"$(basename ${FILE_MAP[bot]})" $args >/dev/null 2>&1 &
sleep 2
pgrep -x "$(basename ${FILE_MAP[bot]})" > /dev/null && green "$(basename ${FILE_MAP[bot]}) is running" || { red "$(basename ${FILE_MAP[bot]}) is not running, restarting..."; pkill -x "$(basename ${FILE_MAP[bot]})" && nohup ./"$(basename ${FILE_MAP[bot]})" "${args}" >/dev/null 2>&1 & sleep 2; purple "$(basename ${FILE_MAP[bot]}) restarted"; }
fi
if [ -n "$NEZHA_SERVER" ] && [ -n "$NEZHA_PORT" ] && [ -n "$NEZHA_KEY" ]; then
if [ -e "$(basename ${FILE_MAP[npm]})" ]; then
tlsPorts=("443" "8443" "2096" "2087" "2083" "2053")
[[ "${tlsPorts[*]}" =~ "${NEZHA_PORT}" ]] && NEZHA_TLS="--tls" || NEZHA_TLS=""
export TMPDIR=$(pwd)
nohup ./"$(basename ${FILE_MAP[npm]})" -s ${NEZHA_SERVER}:${NEZHA_PORT} -p ${NEZHA_KEY} ${NEZHA_TLS} >/dev/null 2>&1 &
sleep 2
pgrep -x "$(basename ${FILE_MAP[npm]})" > /dev/null && green "$(basename ${FILE_MAP[npm]}) is running" || { red "$(basename ${FILE_MAP[npm]}) is not running, restarting..."; pkill -x "$(basename ${FILE_MAP[npm]})" && nohup ./"$(basename ${FILE_MAP[npm]})" -s "${NEZHA_SERVER}:${NEZHA_PORT}" -p "${NEZHA_KEY}" ${NEZHA_TLS} >/dev/null 2>&1 & sleep 2; purple "$(basename ${FILE_MAP[npm]}) restarted"; }
fi
elif [ -n "$NEZHA_SERVER" ] && [ -n "$NEZHA_KEY" ]; then
if [ -e "$(basename ${FILE_MAP[php]})" ]; then
nohup ./"$(basename ${FILE_MAP[php]})" -c "${WORKDIR}/config.yaml" >/dev/null 2>&1 &
sleep 2
pgrep -x "$(basename ${FILE_MAP[php]})" > /dev/null && green "$(basename ${FILE_MAP[php]}) is running" || { red "$(basename ${FILE_MAP[php]}) is not running, restarting..."; pkill -x "$(basename ${FILE_MAP[php]})" && nohup ./"$(basename ${FILE_MAP[php]})" -s -c "${WORKDIR}/config.yaml" >/dev/null 2>&1 & sleep 2; purple "$(basename ${FILE_MAP[php]}) restarted"; }
fi
else
purple "NEZHA variable is empty, skipping running"
fi
for key in "${!FILE_MAP[@]}"; do
if [ -e "$(basename ${FILE_MAP[$key]})" ]; then
rm -rf "$(basename ${FILE_MAP[$key]})" >/dev/null 2>&1
fi
done
}
get_argodomain() {
if [[ -n $ARGO_AUTH ]]; then
echo "$ARGO_DOMAIN"
else
local retry=0
local max_retries=6
local argodomain=""
while [[ $retry -lt $max_retries ]]; do
((retry++))
argodomain=$(grep -oE 'https://[[:alnum:]+\.-]+\.trycloudflare\.com' boot.log | sed 's@https://@@')
if [[ -n $argodomain ]]; then
break
fi
sleep 1
done
echo "$argodomain"
fi
}
get_ip() {
IP_LIST=($(devil vhost list | awk '/^[0-9]+/ {print $1}'))
API_URL="https://status.eooce.com/api"
IP=""
THIRD_IP=${IP_LIST[2]}
RESPONSE=$(curl -s --max-time 2 "${API_URL}/${THIRD_IP}")
if [[ $(echo "$RESPONSE" | jq -r '.status') == "Available" ]]; then
IP=$THIRD_IP
else
FIRST_IP=${IP_LIST[0]}
RESPONSE=$(curl -s --max-time 2 "${API_URL}/${FIRST_IP}")
if [[ $(echo "$RESPONSE" | jq -r '.status') == "Available" ]]; then
IP=$FIRST_IP
else
IP=${IP_LIST[1]}
fi
fi
echo "$IP"
}
generate_sub_link () {
echo ""
rm -rf ${FILE_PATH}/.htaccess
base64 -w0 ${FILE_PATH}/list.txt > ${FILE_PATH}/v2.log
PHP_URL="https://00.ssss.nyc.mn/sub.php"
QR_URL="https://00.ssss.nyc.mn/qrencode"
$COMMAND "${FILE_PATH}/${SUB_TOKEN}.php" "$PHP_URL"
V2rayN_LINK="https://${USERNAME}.${CURRENT_DOMAIN}/v2.log"
AUTO_LINK="https://${USERNAME}.${CURRENT_DOMAIN}/${SUB_TOKEN}"
$COMMAND "${WORKDIR}/qrencode" "$QR_URL" && chmod +x "${WORKDIR}/qrencode"
curl -sS "https://sublink.eooce.com/clash?config=${V2rayN_LINK}" -o ${FILE_PATH}/clash.yaml
curl -sS "https://sublink.eooce.com/singbox?config=${V2rayN_LINK}" -o ${FILE_PATH}/singbox.yaml
"${WORKDIR}/qrencode" -m 2 -t UTF8 "${AUTO_LINK}"
purple "\n自适应节点订阅链接: ${AUTO_LINK}\n"
green "二维码和节点订阅链接适用于 V2rayN/Nekoray/ShadowRocket/Clash/Mihomo/Sing-box/karing/Loon/sterisand 等\n\n"
cat > ${FILE_PATH}/.htaccess << EOF
RewriteEngine On
DirectoryIndex index.html
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(\?|$)
RewriteRule ^$ /index.html [L]
<FilesMatch "^(index\.html|${SUB_TOKEN}\.php)$">
Order Allow,Deny
Allow from all
</FilesMatch>
<FilesMatch "^(clash\.yaml|singbox\.yaml|list\.txt|v2\.log|sub\.php)$">
Order Allow,Deny
Deny from all
</FilesMatch>
RewriteRule ^${SUB_TOKEN}$ ${SUB_TOKEN}.php [L]
EOF
}
install_keepalive () {
purple "正在安装保活服务中,请稍等......"
devil www del keep.${USERNAME}.${CURRENT_DOMAIN} > /dev/null 2>&1
devil www add keep.${USERNAME}.${CURRENT_DOMAIN} nodejs /usr/local/bin/node18 > /dev/null 2>&1
keep_path="$HOME/domains/keep.${USERNAME}.${CURRENT_DOMAIN}/public_nodejs"
[ -d "$keep_path" ] || mkdir -p "$keep_path"
app_file_url="https://00.ssss.nyc.mn/sbx4.js"
$COMMAND "${keep_path}/app.js" "$app_file_url"
cat > ${keep_path}/.env <<EOF
UUID=${UUID}
CFIP=${CFIP}
CFPORT=${CFPORT}
SUB_TOKEN=${SUB_TOKEN}
${UPLOAD_URL:+API_SUB_URL=$UPLOAD_URL}
${tg_chat_id:+TELEGRAM_CHAT_ID=$tg_chat_id}
${tg_token:+TELEGRAM_BOT_TOKEN=$tg_token}
${NEZHA_SERVER:+NEZHA_SERVER=$NEZHA_SERVER}
${NEZHA_PORT:+NEZHA_PORT=$NEZHA_PORT}
${NEZHA_KEY:+NEZHA_KEY=$NEZHA_KEY}
ARGO_DOMAIN=$ARGO_DOMAIN
ARGO_AUTH=$([[ -z "$ARGO_AUTH" ]] && echo "" || ([[ "$ARGO_AUTH" =~ ^\{.* ]] && echo "'$ARGO_AUTH'" || echo "$ARGO_AUTH"))
EOF
# devil ssl www add $available_ip le le keep.${USERNAME}.${CURRENT_DOMAIN} > /dev/null 2>&1
ln -fs /usr/local/bin/node18 ~/bin/node > /dev/null 2>&1
ln -fs /usr/local/bin/npm18 ~/bin/npm > /dev/null 2>&1
mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:~/bin:$PATH' >> $HOME/.bash_profile && source $HOME/.bash_profile
rm -rf $HOME/.npmrc > /dev/null 2>&1
cd ${keep_path} && npm install dotenv axios --silent > /dev/null 2>&1
rm $HOME/domains/keep.${USERNAME}.${CURRENT_DOMAIN}/public_nodejs/public/index.html > /dev/null 2>&1
# devil www options keep.${USERNAME}.${CURRENT_DOMAIN} sslonly on > /dev/null 2>&1
devil www restart keep.${USERNAME}.${CURRENT_DOMAIN}> /dev/null 2>&1
if curl -skL "http://keep.${USERNAME}.${CURRENT_DOMAIN}/${USERNAME}" | grep -q "running"; then
green "\n全自动保活服务安装成功\n"
green "所有服务都运行正常,全自动保活任务添加成功\n\n"
purple "访问 http://keep.${USERNAME}.${CURRENT_DOMAIN}/stop 结束进程\n"
purple "访问 http://keep.${USERNAME}.${CURRENT_DOMAIN}/list 全部进程列表\n"
yellow "访问 http://keep.${USERNAME}.${CURRENT_DOMAIN}/${USERNAME} 调起保活程序 备用保活路径: /run /go /start\n"
purple "访问 http://keep.${USERNAME}.${CURRENT_DOMAIN}/status 查看进程状态\n\n"
purple "如果需要TG通知,在${yellow}https://t.me/laowang_serv00_bot${re}${purple}获取CHAT_ID,并带CHAT_ID环境变量运行${re}\n\n"
quick_command
else
red "\n全自动保活服务安装失败,存在未运行的进程,请执行以下命令后重装: \n\ndevil www del ${USERNAME}.${CURRENT_DOMAIN}\ndevil www del keep.${USERNAME}.${CURRENT_DOMAIN}\nrm -rf $HOME/domains/*\n\n"
fi
}
get_links(){
argodomain=$(get_argodomain)
echo -e "\e[1;32mArgoDomain: \e[1;35m${argodomain}\e[0m\n"
ISP=$(curl -sm 3 -H "User-Agent: Mozilla/5.0" "https://api.ip.sb/geoip" | awk -F\" '{print $32}' | sed -e 's/ /_/g' || echo "0")
get_name() { if [ "$HOSTNAME" = "s1.ct8.pl" ]; then SERVER="CT8"; else SERVER=$(echo "$HOSTNAME" | cut -d '.' -f 1); fi; echo "$SERVER"; }
NAME="$ISP-$(get_name)"
yellow "注意:v2ray或其他软件的跳过证书验证需设置为true,否则hy2或tuic节点可能不通\n"
cat > ${FILE_PATH}/list.txt <<EOF
vmess://$(echo "{ \"v\": \"2\", \"ps\": \"$NAME-vmess\", \"add\": \"$available_ip\", \"port\": \"$VMESS_PORT\", \"id\": \"$UUID\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"\", \"path\": \"/vmess-argo?ed=2048\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\", \"fp\": \"\"}" | base64 -w0)
vmess://$(echo "{ \"v\": \"2\", \"ps\": \"$NAME-vmess-argo\", \"add\": \"$CFIP\", \"port\": \"$CFPORT\", \"id\": \"$UUID\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"$argodomain\", \"path\": \"/vmess-argo?ed=2048\", \"tls\": \"tls\", \"sni\": \"$argodomain\", \"alpn\": \"\", \"fp\": \"\"}" | base64 -w0)
hysteria2://$UUID@$available_ip:$HY2_PORT/?sni=www.bing.com&alpn=h3&insecure=1#$NAME-hysteria2
tuic://$UUID:admin123@$available_ip:$TUIC_PORT?sni=www.bing.com&congestion_control=bbr&udp_relay_mode=native&alpn=h3&allow_insecure=1#$NAME-tuic
EOF
cat ${FILE_PATH}/list.txt
generate_sub_link
install_keepalive
rm -rf boot.log config.json sb.log core tunnel.yml tunnel.json fake_useragent_0.2.0.json
green "Running done!\n"
}
quick_command() {
COMMAND="00"
SCRIPT_PATH="$HOME/bin/$COMMAND"
mkdir -p "$HOME/bin"
set +H
printf '#!/bin/bash\n' > "$SCRIPT_PATH"
echo "bash <(curl -Ls https://raw.githubusercontent.com/eooce/sing-box/main/sb_serv00.sh)" >> "$SCRIPT_PATH"
chmod +x "$SCRIPT_PATH"
if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then
echo 'export PATH="$HOME/bin:$PATH"' >> "$HOME/.bashrc" 2>/dev/null
source "$HOME/.bashrc"
fi
green "快捷指令00创建成功,下次运行输入00快速进入菜单\n"
}
get_url_info() {
if devil www list 2>&1 | grep -q "keep.${USERNAME}.${CURRENT_DOMAIN}"; then
purple "\n-------------------保活相关链接------------------\n\n"
purple "http://keep.${USERNAME}.${CURRENT_DOMAIN}/stop 结束进程\n"
purple "http://keep.${USERNAME}.${CURRENT_DOMAIN}/list 全部进程列表\n"
yellow "http://keep.${USERNAME}.${CURRENT_DOMAIN}/${USERNAME} 调起保活程序\n"
purple "http://keep.${USERNAME}.${CURRENT_DOMAIN}/status 查看进程状态\n\n"
else
red "尚未安装自动保活服务\n" && sleep 2 && menu
fi
}
get_nodes(){
cat ${FILE_PATH}/list.txt
TOKEN=$(sed -n 's/^SUB_TOKEN=\(.*\)/\1/p' $HOME/domains/keep.${USERNAME}.${CURRENT_DOMAIN}/public_nodejs/.env)
yellow "\n自适应节点订阅链接: https://${USERNAME}.${CURRENT_DOMAIN}/${TOKEN}\n二维码和节点订阅链接适用于V2rayN/Nekoray/ShadowRocket/Clash/Sing-box/karing/Loon/sterisand 等\n"
}
menu() {
clear
echo ""
purple "=== Serv00|ct8老王sing-box一键四合一安装脚本 ===\n"
echo -e "${green}脚本地址:${re}${yellow}https://github.com/eooce/Sing-box${re}\n"
echo -e "${green}反馈论坛:${re}${yellow}https://bbs.vps8.me${re}\n"
echo -e "${green}TG反馈群组:${re}${yellow}https://t.me/vps888${re}\n"
purple "转载请著名出处,请勿滥用\n"
yellow "快速启动命令00\n"
green "1. 安装四合一"
echo "==============="
red "2. 卸载四合一"
echo "==============="
green "3. 查看节点信息"
echo "==============="
green "4. 查看保活链接"
echo "==============="
yellow "5. 更换节点端口"
echo "==============="
yellow "6. 初始化系统"
echo "==============="
red "0. 退出脚本"
echo "==========="
reading "请输入选择(0-6): " choice
echo ""
case "${choice}" in
1) install_singbox ;;
2) uninstall_singbox ;;
3) get_nodes ;;
4) get_url_info ;;
5) changge_ports ;;
6) reset_system ;;
0) exit 0 ;;
*) red "无效的选项,请输入 0 到 6" ;;
esac
}
menu