Files
Sing-box/sing-box.sh
T

1425 lines
48 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"
skybule="\e[1;36m"
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"; }
skyblue() { echo -e "\e[1;36m$1\033[0m"; }
reading() { read -p "$(red "$1")" "$2"; }
# 定义常量
server_name="sing-box"
work_dir="/etc/sing-box"
config_dir="${work_dir}/config.json"
client_dir="${work_dir}/url.txt"
export vless_port=${PORT:-$(shuf -i 1000-65000 -n 1)}
export CFIP=${CFIP:-'www.visa.com.tw'}
export CFPORT=${CFPORT:-'443'}
# 检查是否为root下运行
[[ $EUID -ne 0 ]] && red "请在root用户下运行脚本" && exit 1
# 检查 sing-box 是否已安装
check_singbox() {
if [ -f "${work_dir}/${server_name}" ]; then
if [ -f /etc/alpine-release ]; then
rc-service sing-box status | grep -q "started" && green "running" && return 0 || yellow "not running" && return 1
else
[ "$(systemctl is-active sing-box)" = "active" ] && green "running" && return 0 || yellow "not running" && return 1
fi
else
red "not installed"
return 2
fi
}
# 检查 argo 是否已安装
check_argo() {
if [ -f "${work_dir}/argo" ]; then
if [ -f /etc/alpine-release ]; then
rc-service argo status | grep -q "started" && green "running" && return 0 || yellow "not running" && return 1
else
[ "$(systemctl is-active argo)" = "active" ] && green "running" && return 0 || yellow "not running" && return 1
fi
else
red "not installed"
return 2
fi
}
# 检查 nginx 是否已安装
check_nginx() {
if command -v nginx &>/dev/null; then
if [ -f /etc/alpine-release ]; then
rc-service nginx status | grep -q "stoped" && yellow "not running" && return 1 || green "running" && return 0
else
[ "$(systemctl is-active nginx)" = "active" ] && green "running" && return 0 || yellow "not running" && return 1
fi
else
red "not installed"
return 2
fi
}
#根据系统类型安装、卸载依赖
manage_packages() {
if [ $# -lt 2 ]; then
red "Unspecified package name or action"
return 1
fi
action=$1
shift
for package in "$@"; do
if [ "$action" == "install" ]; then
if command -v "$package" &>/dev/null; then
green "${package} already installed"
continue
fi
yellow "正在安装 ${package}..."
if command -v apt &>/dev/null; then
apt install -y "$package"
elif command -v dnf &>/dev/null; then
dnf install -y "$package"
elif command -v yum &>/dev/null; then
yum install -y "$package"
elif command -v apk &>/dev/null; then
apk update
apk add "$package"
else
red "Unknown system!"
return 1
fi
elif [ "$action" == "uninstall" ]; then
if ! command -v "$package" &>/dev/null; then
yellow "${package} is not installed"
continue
fi
yellow "正在卸载 ${package}..."
if command -v apt &>/dev/null; then
apt remove -y "$package" && apt autoremove -y
elif command -v dnf &>/dev/null; then
dnf remove -y "$package" && dnf autoremove -y
elif command -v yum &>/dev/null; then
yum remove -y "$package" && yum autoremove -y
elif command -v apk &>/dev/null; then
apk del "$package"
else
red "Unknown system!"
return 1
fi
else
red "Unknown action: $action"
return 1
fi
done
return 0
}
# 获取ip
get_realip() {
ip=$(curl -s --max-time 2 ipv4.ip.sb)
if [ -z "$ip" ]; then
ipv6=$(curl -s --max-time 1 ipv6.ip.sb)
echo "[$ipv6]"
else
if echo "$(curl -s http://ipinfo.io/org)" | grep -qE 'Cloudflare|UnReal|AEZA|Andrei'; then
ipv6=$(curl -s --max-time 1 ipv6.ip.sb)
echo "[$ipv6]"
else
echo "$ip"
fi
fi
}
# 下载并安装 sing-box,cloudflared
install_singbox() {
clear
purple "正在安装sing-box中,请稍后..."
# 判断系统架构
ARCH_RAW=$(uname -m)
case "${ARCH_RAW}" in
'x86_64') ARCH='amd64' ;;
'x86' | 'i686' | 'i386') ARCH='386' ;;
'aarch64' | 'arm64') ARCH='arm64' ;;
'armv7l') ARCH='armv7' ;;
's390x') ARCH='s390x' ;;
*) red "不支持的架构: ${ARCH_RAW}"; exit 1 ;;
esac
# 下载sing-box,cloudflared
[ ! -d "${work_dir}" ] && mkdir -p "${work_dir}" && chmod 777 "${work_dir}"
latest_version=$(curl -s "https://api.github.com/repos/SagerNet/sing-box/releases" | jq -r '[.[] | select(.prerelease==false)][0].tag_name | sub("^v"; "")')
curl -sLo "${work_dir}/${server_name}.tar.gz" "https://github.com/SagerNet/sing-box/releases/download/v${latest_version}/sing-box-${latest_version}-linux-${ARCH}.tar.gz"
curl -sLo "${work_dir}/qrencode" "https://github.com/eooce/test/releases/download/${ARCH}/qrencode-linux-${ARCH}"
curl -sLo "${work_dir}/argo" "https://github.com/eooce/test/releases/download/$ARCH/bot13"
tar -xzvf "${work_dir}/${server_name}.tar.gz" -C "${work_dir}/" && \
mv "${work_dir}/sing-box-${latest_version}-linux-${ARCH}/sing-box" "${work_dir}/" && \
rm -rf "${work_dir}/${server_name}.tar.gz" "${work_dir}/sing-box-${latest_version}-linux-${ARCH}"
chown root:root ${work_dir} && chmod +x ${work_dir}/${server_name} ${work_dir}/argo ${work_dir}/qrencode
# 生成随机端口和密码
nginx_port=$(($vless_port + 1))
tuic_port=$(($vless_port + 2))
hy2_port=$(($vless_port + 3))
uuid=$(cat /proc/sys/kernel/random/uuid)
password=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 24)
output=$(/etc/sing-box/sing-box generate reality-keypair)
private_key=$(echo "${output}" | awk '/PrivateKey:/ {print $2}')
public_key=$(echo "${output}" | awk '/PublicKey:/ {print $2}')
iptables -F > /dev/null 2>&1 && iptables -P INPUT ACCEPT > /dev/null 2>&1 && iptables -P FORWARD ACCEPT > /dev/null 2>&1 && iptables -P OUTPUT ACCEPT > /dev/null 2>&1
command -v ip6tables &> /dev/null && ip6tables -F > /dev/null 2>&1 && ip6tables -P INPUT ACCEPT > /dev/null 2>&1 && ip6tables -P FORWARD ACCEPT > /dev/null 2>&1 && ip6tables -P OUTPUT ACCEPT > /dev/null 2>&1
manage_packages uninstall ufw firewalld > /dev/null 2>&1
# 生成自签名证书
openssl ecparam -genkey -name prime256v1 -out "${work_dir}/private.key"
openssl req -new -x509 -days 3650 -key "${work_dir}/private.key" -out "${work_dir}/cert.pem" -subj "/CN=bing.com"
# 生成配置文件
cat > "${config_dir}" << EOF
{
"log": {
"disabled": false,
"level": "info",
"output": "$work_dir/sb.log",
"timestamp": true
},
"dns": {
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
}
]
},
"inbounds": [
{
"tag": "vless-reality-vesion",
"type": "vless",
"listen": "::",
"listen_port": $vless_port,
"users": [
{
"uuid": "$uuid",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "www.iij.ad.jp",
"reality": {
"enabled": true,
"handshake": {
"server": "www.iij.ad.jp",
"server_port": 443
},
"private_key": "$private_key",
"short_id": [
""
]
}
}
},
{
"tag": "vmess-ws",
"type": "vmess",
"listen": "::",
"listen_port": 8001,
"users": [
{
"uuid": "$uuid"
}
],
"transport": {
"type": "ws",
"path": "/vmess",
"early_data_header_name": "Sec-WebSocket-Protocol"
}
},
{
"tag": "hysteria2",
"type": "hysteria2",
"listen": "::",
"listen_port": $hy2_port,
"sniff": true,
"sniff_override_destination": false,
"users": [
{
"password": "$uuid"
}
],
"ignore_client_bandwidth":false,
"masquerade": "https://bing.com",
"tls": {
"enabled": true,
"alpn": [
"h3"
],
"min_version":"1.3",
"max_version":"1.3",
"certificate_path": "$work_dir/cert.pem",
"key_path": "$work_dir/private.key"
}
},
{
"tag": "tuic",
"type": "tuic",
"listen": "::",
"listen_port": $tuic_port,
"users": [
{
"uuid": "$uuid",
"password": "$password"
}
],
"congestion_control": "bbr",
"tls": {
"enabled": true,
"alpn": [
"h3"
],
"certificate_path": "$work_dir/cert.pem",
"key_path": "$work_dir/private.key"
}
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct"
},
{
"type": "direct",
"tag": "direct-ipv4-prefer-out",
"domain_strategy": "prefer_ipv4"
},
{
"type": "direct",
"tag": "direct-ipv4-only-out",
"domain_strategy": "ipv4_only"
},
{
"type": "direct",
"tag": "direct-ipv6-prefer-out",
"domain_strategy": "prefer_ipv6"
},
{
"type": "direct",
"tag": "direct-ipv6-only-out",
"domain_strategy": "ipv6_only"
},
{
"type": "wireguard",
"tag": "wireguard-out",
"server": "engage.cloudflareclient.com",
"server_port": 2408,
"local_address": [
"172.16.0.2/32",
"2606:4700:110:812a:4929:7d2a:af62:351c/128"
],
"private_key": "gBthRjevHDGyV0KvYwYE52NIPy29sSrVr6rcQtYNcXA=",
"peer_public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
"reserved": [
6,
146,
6
]
},
{
"type": "direct",
"tag": "wireguard-ipv4-prefer-out",
"detour": "wireguard-out",
"domain_strategy": "prefer_ipv4"
},
{
"type": "direct",
"tag": "wireguard-ipv4-only-out",
"detour": "wireguard-out",
"domain_strategy": "ipv4_only"
},
{
"type": "direct",
"tag": "wireguard-ipv6-prefer-out",
"detour": "wireguard-out",
"domain_strategy": "prefer_ipv6"
},
{
"type": "direct",
"tag": "wireguard-ipv6-only-out",
"detour": "wireguard-out",
"domain_strategy": "ipv6_only"
}
],
"route": {
"rule_set": [
{
"tag": "geosite-netflix",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-netflix.srs",
"update_interval": "1d"
},
{
"tag": "geosite-openai",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geosite/openai.srs",
"update_interval": "1d"
}
],
"rules": [
{
"rule_set": [
"geosite-netflix"
],
"outbound": "wireguard-ipv6-only-out"
},
{
"domain": [
"api.statsig.com",
"browser-intake-datadoghq.com",
"cdn.openai.com",
"chat.openai.com",
"auth.openai.com",
"chat.openai.com.cdn.cloudflare.net",
"ios.chat.openai.com",
"o33249.ingest.sentry.io",
"openai-api.arkoselabs.com",
"openaicom-api-bdcpf8c6d2e9atf6.z01.azurefd.net",
"openaicomproductionae4b.blob.core.windows.net",
"production-openaicom-storage.azureedge.net",
"static.cloudflareinsights.com"
],
"domain_suffix": [
".algolia.net",
".auth0.com",
".chatgpt.com",
".challenges.cloudflare.com",
".client-api.arkoselabs.com",
".events.statsigapi.net",
".featuregates.org",
".identrust.com",
".intercom.io",
".intercomcdn.com",
".launchdarkly.com",
".oaistatic.com",
".oaiusercontent.com",
".observeit.net",
".openai.com",
".openaiapi-site.azureedge.net",
".openaicom.imgix.net",
".segment.io",
".sentry.io",
".stripe.com"
],
"domain_keyword": [
"openaicom-api"
],
"outbound": "wireguard-ipv6-prefer-out"
}
],
"final": "direct"
},
"experimental": {
"cache_file": {
"enabled": true,
"path": "$work_dir/cache.db",
"cache_id": "mycacheid",
"store_fakeip": true
}
}
}
EOF
}
# debian/ubuntu/centos 守护进程
main_systemd_services() {
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=/etc/sing-box
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
ExecStart=/etc/sing-box/sing-box run -c /etc/sing-box/config.json
ExecReload=/bin/kill -HUP \$MAINPID
Restart=on-failure
RestartSec=10
LimitNOFILE=infinity
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/argo.service << EOF
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
NoNewPrivileges=yes
TimeoutStartSec=0
ExecStart=/bin/sh -c "/etc/sing-box/argo tunnel --url http://localhost:8001 --no-autoupdate --edge-ip-version auto --protocol http2 > /etc/sing-box/argo.log 2>&1"
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
if [ -f /etc/centos-release ]; then
yum install -y chrony
systemctl start chronyd
systemctl enable chronyd
chronyc -a makestep
yum update -y ca-certificates
bash -c 'echo "0 0" > /proc/sys/net/ipv4/ping_group_range'
fi
systemctl daemon-reload
systemctl enable sing-box
systemctl start sing-box
systemctl enable argo
systemctl start argo
}
# 适配alpine 守护进程
alpine_openrc_services() {
cat > /etc/init.d/sing-box << 'EOF'
#!/sbin/openrc-run
description="sing-box service"
command="/etc/sing-box/sing-box"
command_args="run -c /etc/sing-box/config.json"
command_background=true
pidfile="/var/run/sing-box.pid"
EOF
cat > /etc/init.d/argo << 'EOF'
#!/sbin/openrc-run
description="Cloudflare Tunnel"
command="/bin/sh"
command_args="-c '/etc/sing-box/argo tunnel --url http://localhost:8001 --no-autoupdate --edge-ip-version auto --protocol http2 > /etc/sing-box/argo.log 2>&1'"
command_background=true
pidfile="/var/run/argo.pid"
EOF
chmod +x /etc/init.d/sing-box
chmod +x /etc/init.d/argo
rc-update add sing-box default
rc-update add argo default
}
get_info() {
clear
server_ip=$(get_realip)
isp=$(curl -s --max-time 2 https://speed.cloudflare.com/meta | awk -F\" '{print $26"-"$18}' | sed -e 's/ /_/g' || echo "vps")
if [ -f "${work_dir}/argo.log" ]; then
for i in {1..5}; do
purple "$i 次尝试获取ArgoDoamin中..."
argodomain=$(sed -n 's|.*https://\([^/]*trycloudflare\.com\).*|\1|p' "${work_dir}/argo.log")
[ -n "$argodomain" ] && break
sleep 2
done
else
restart_argo
sleep 6
argodomain=$(sed -n 's|.*https://\([^/]*trycloudflare\.com\).*|\1|p' "${work_dir}/argo.log")
fi
green "\nArgoDomain${purple}$argodomain${re}\n"
VMESS="{ \"v\": \"2\", \"ps\": \"${isp}\", \"add\": \"${CFIP}\", \"port\": \"${CFPORT}\", \"id\": \"${uuid}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"${argodomain}\", \"path\": \"/vmess?ed=2048\", \"tls\": \"tls\", \"sni\": \"${argodomain}\", \"alpn\": \"\", \"fp\": \"randomized\", \"allowlnsecure\": \"flase\"}"
cat > ${work_dir}/url.txt <<EOF
vless://${uuid}@${server_ip}:${vless_port}?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.iij.ad.jp&fp=chrome&pbk=${public_key}&type=tcp&headerType=none#${isp}
vmess://$(echo "$VMESS" | base64 -w0)
hysteria2://${uuid}@${server_ip}:${hy2_port}/?sni=www.bing.com&insecure=1&alpn=h3&obfs=none#${isp}
tuic://${uuid}:${password}@${server_ip}:${tuic_port}?sni=www.bing.com&congestion_control=bbr&udp_relay_mode=native&alpn=h3&allow_insecure=1#${isp}
EOF
echo ""
while IFS= read -r line; do echo -e "${purple}$line"; done < ${work_dir}/url.txt
base64 -w0 ${work_dir}/url.txt > ${work_dir}/sub.txt
yellow "\n温馨提醒:需打开V2rayN或其他软件里的 “跳过证书验证”,或将节点的Insecure或TLS里设置为“true”\n"
green "节点订阅链接:http://${server_ip}:${nginx_port}/${password}\n\n订阅链接适用于V2rayN,Nekbox,Sterisand,Loon,小火箭,圈X等\n"
green "订阅二维码"
$work_dir/qrencode "http://${server_ip}:${nginx_port}/${password}"
echo ""
}
# 修复nginx因host无法安装的问题
fix_nginx() {
HOSTNAME=$(hostname)
NGINX_CONFIG_FILE="/etc/nginx/nginx.conf"
grep -q "127.0.1.1 $HOSTNAME" /etc/hosts || echo "127.0.1.1 $HOSTNAME" | tee -a /etc/hosts >/dev/null
id -u nginx >/dev/null 2>&1 || useradd -r -d /var/www -s /sbin/nologin nginx >/dev/null 2>&1
grep -q "^user nginx;" $NGINX_CONFIG_FILE || sed -i "s/^user .*/user nginx;/" $NGINX_CONFIG_FILE >/dev/null 2>&1
}
# nginx订阅配置
add_nginx_conf() {
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
cat > /etc/nginx/nginx.conf << EOF
# nginx_conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen $nginx_port;
listen [::]:$nginx_port;
location /$password {
alias /etc/sing-box/sub.txt;
default_type 'text/plain; charset=utf-8';
}
}
}
EOF
nginx -t > /dev/null
if [ $? -eq 0 ]; then
if [ -f /etc/alpine-release ]; then
pkill -f '[n]ginx'
touch /run/nginx.pid
nginx -s reload
rc-service nginx restart
else
rm /run/nginx.pid
systemctl daemon-reload
systemctl restart nginx
fi
fi
}
# 启动 sing-box
start_singbox() {
if [ ${check_singbox} -eq 1 ]; then
yellow "正在启动 ${server_name} 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service sing-box start
else
systemctl daemon-reload
systemctl start "${server_name}"
fi
if [ $? -eq 0 ]; then
green "${server_name} 服务已成功启动\n"
else
red "${server_name} 服务启动失败\n"
fi
elif [ ${check_singbox} -eq 0 ]; then
yellow "sing-box 正在运行\n"
sleep 1
menu
else
yellow "sing-box 尚未安装!\n"
sleep 1
menu
fi
}
# 停止 sing-box
stop_singbox() {
if [ ${check_singbox} -eq 0 ]; then
yellow "正在停止 ${server_name} 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service sing-box stop
else
systemctl stop "${server_name}"
fi
if [ $? -eq 0 ]; then
green "${server_name} 服务已成功停止\n"
else
red "${server_name} 服务停止失败\n"
fi
elif [ ${check_singbox} -eq 1 ]; then
yellow "sing-box 未运行\n"
sleep 1
menu
else
yellow "sing-box 尚未安装!\n"
sleep 1
menu
fi
}
# 重启 sing-box
restart_singbox() {
if [ ${check_singbox} -eq 0 ]; then
yellow "正在重启 ${server_name} 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service ${server_name} restart
else
systemctl daemon-reload
systemctl restart "${server_name}"
fi
if [ $? -eq 0 ]; then
green "${server_name} 服务已成功重启\n"
else
red "${server_name} 服务重启失败\n"
fi
elif [ ${check_singbox} -eq 1 ]; then
yellow "sing-box 未运行\n"
sleep 1
menu
else
yellow "sing-box 尚未安装!\n"
sleep 1
menu
fi
}
# 启动 argo
start_argo() {
if [ ${check_argo} -eq 1 ]; then
yellow "正在启动 Argo 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service argo start
else
systemctl daemon-reload
systemctl start argo
fi
if [ $? -eq 0 ]; then
green "Argo 服务已成功重启\n"
else
red "Argo 服务重启失败\n"
fi
elif [ ${check_argo} -eq 0 ]; then
green "Argo 服务正在运行\n"
sleep 1
menu
else
yellow "Argo 尚未安装!\n"
sleep 1
menu
fi
}
# 停止 argo
stop_argo() {
if [ ${check_argo} -eq 0 ]; then
yellow "正在停止 Argo 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service stop start
else
systemctl daemon-reload
systemctl stop argo
fi
if [ $? -eq 0 ]; then
green "Argo 服务已成功停止\n"
else
red "Argo 服务停止失败\n"
fi
elif [ ${check_argo} -eq 1 ]; then
yellow "Argo 服务未运行\n"
sleep 1
menu
else
yellow "Argo 尚未安装!\n"
sleep 1
menu
fi
}
# 重启 argo
restart_argo() {
if [ ${check_argo} -eq 0 ]; then
yellow "正在重启 Argo 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service argo restart
else
systemctl daemon-reload
systemctl restart argo
fi
if [ $? -eq 0 ]; then
green "Argo 服务已成功重启\n"
else
red "Argo 服务重启失败\n"
fi
elif [ ${check_argo} -eq 1 ]; then
yellow "Argo 服务未运行\n"
sleep 1
menu
else
yellow "Argo 尚未安装!\n"
sleep 1
menu
fi
}
# 启动 nginx
start_nginx() {
if command -v nginx &>/dev/null; then
yellow "正在启动 nginx 服务\n"
if [ -f /etc/alpine-release ]; then
rc-service nginx start
else
systemctl daemon-reload
systemctl start nginx
fi
if [ $? -eq 0 ]; then
green "Nginx 服务已成功启动\n"
else
red "Nginx 启动失败\n"
fi
else
yellow "Nginx 尚未安装!\n"
sleep 1
menu
fi
}
# 重启 nginx
restart_nginx() {
if command -v nginx &>/dev/null; then
yellow "正在重启 nginx 服务\n"
if [ -f /etc/alpine-release ]; then
pkill -f '[n]ginx'
touch /run/nginx.pid
nginx -s reload
rc-service nginx restart
else
systemctl restart nginx
fi
if [ $? -eq 0 ]; then
green "Nginx 服务已成功重启\n"
else
red "Nginx 重启失败\n"
fi
else
yellow "Nginx 尚未安装!\n"
sleep 1
menu
fi
}
# 卸载 sing-box
uninstall_singbox() {
reading "确定要卸载 sing-box 吗? (y/n): " choice
case "${choice}" in
y|Y)
yellow "正在卸载 sing-box"
if [ -f /etc/alpine-release ]; then
rc-service sing-box stop
rc-service argo stop
rm /etc/init.d/sing-box /etc/init.d/argo
rc-update del sing-box default
rc-update del argo default
else
# 停止 sing-box和 argo 服务
systemctl stop "${server_name}"
systemctl stop argo
# 禁用 sing-box 服务
systemctl disable "${server_name}"
systemctl disable argo
# 重新加载 systemd
systemctl daemon-reload || true
fi
# 删除配置文件和日志
rm -rf "${work_dir}" || true
rm -f "${log_dir}" || true
rm -rf /etc/systemd/system/sing-box.service /etc/systemd/system/argo.service > /dev/null 2>&1
# 卸载Nginx
reading "\n是否卸载 Nginx${green}(卸载请输入 ${yellow}y${re} ${green}回车将跳过卸载Nginx) (y/n): ${re}" choice
case "${choice}" in
y|Y)
manage_packages uninstall nginx
;;
*)
yellow "取消卸载Nginx\n\n"
;;
esac
green "\nsing-box 卸载成功\n\n" && exit 0
;;
*)
purple "已取消卸载操作\n\n"
;;
esac
}
# 创建快捷指令
create_shortcut() {
cat > "$work_dir/sb.sh" << EOF
#!/usr/bin/env bash
bash <(curl -Ls https://raw.githubusercontent.com/eooce/sing-box/main/sing-box.sh) \$1
EOF
chmod +x "$work_dir/sb.sh"
ln -sf "$work_dir/sb.sh" /usr/bin/sb
if [ -s /usr/bin/sb ]; then
green "\n快捷指令 sb 创建成功\n"
else
red "\n快捷指令创建失败\n"
fi
}
# 适配alpine运行argo报错用户组和dns的问题
change_hosts() {
sh -c 'echo "0 0" > /proc/sys/net/ipv4/ping_group_range'
sed -i '1s/.*/127.0.0.1 localhost/' /etc/hosts
sed -i '2s/.*/::1 localhost/' /etc/hosts
}
# 变更配置
change_config() {
if [ ${check_singbox} -eq 0 ]; then
clear
echo ""
green "1. 修改端口"
skyblue "------------"
green "2. 修改UUID"
skyblue "------------"
green "3. 修改Reality伪装域名"
skyblue "------------"
green "4. 添加hysteria2端口跳跃"
skyblue "------------"
green "5. 删除hysteria2端口跳跃"
skyblue "------------"
purple "${purple}6. 返回主菜单"
skyblue "------------"
reading "请输入选择: " choice
case "${choice}" in
1)
echo ""
green "1. 修改vless-reality端口"
skyblue "------------"
green "2. 修改hysteria2端口"
skyblue "------------"
green "3. 修改tuic端口"
skyblue "------------"
purple "4. 返回上一级菜单"
skyblue "------------"
reading "请输入选择: " choice
case "${choice}" in
1)
reading "\n请输入vless-reality端口 (回车跳过将使用随机端口): " new_port
[ -z "$new_port" ] && new_port=$(shuf -i 2000-65000 -n 1)
sed -i '/"type": "vless"/,/listen_port/ s/"listen_port": [0-9]\+/"listen_port": '"$new_port"'/' $config_dir
restart_singbox
sed -i 's/\(vless:\/\/[^@]*@[^:]*:\)[0-9]\{1,\}/\1'"$new_port"'/' $client_dir
base64 -w0 /etc/sing-box/url.txt > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
green "\nvless-reality端口已修改成:${purple}$new_port${re} ${green}请更新订阅或手动更改vless-reality端口${re}\n"
;;
2)
reading "\n请输入hysteria2端口 (回车跳过将使用随机端口): " new_port
[ -z "$new_port" ] && new_port=$(shuf -i 2000-65000 -n 1)
sed -i '/"type": "hysteria2"/,/listen_port/ s/"listen_port": [0-9]\+/"listen_port": '"$new_port"'/' $config_dir
restart_singbox
sed -i 's/\(hysteria2:\/\/[^@]*@[^:]*:\)[0-9]\{1,\}/\1'"$new_port"'/' $client_dir
base64 -w0 $client_dir > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
green "\nhysteria2端口已修改为:${purple}${new_port}${re} ${green}请更新订阅或手动更改hysteria2端口${re}\n"
;;
3)
reading "\n请输入tuic端口 (回车跳过将使用随机端口): " new_port
[ -z "$new_port" ] && new_port=$(shuf -i 2000-65000 -n 1)
sed -i '/"type": "tuic"/,/listen_port/ s/"listen_port": [0-9]\+/"listen_port": '"$new_port"'/' $config_dir
restart_singbox
sed -i 's/\(tuic:\/\/[^@]*@[^:]*:\)[0-9]\{1,\}/\1'"$new_port"'/' $client_dir
base64 -w0 $client_dir > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
green "\ntuic端口已修改为:${purple}${new_port}${re} ${green}请更新订阅或手动更改tuic端口${re}\n"
;;
4) change_config ;;
*) red "无效的选项,请输入 1 到 4" ;;
esac
;;
2)
reading "\n请输入新的UUID: " new_uuid
[ -z "$new_uuid" ] && new_uuid=$(cat /proc/sys/kernel/random/uuid)
sed -i -E '
s/"uuid": "([a-f0-9-]+)"/"uuid": "'"$new_uuid"'"/g;
s/"uuid": "([a-f0-9-]+)"$/\"uuid\": \"'$new_uuid'\"/g;
s/"password": "([a-f0-9-]+)"/"password": "'"$new_uuid"'"/g
' $config_dir
restart_singbox
sed -i -E 's/(vless:\/\/|hysteria2:\/\/)[^@]*(@.*)/\1'"$new_uuid"'\2/' $client_dir
sed -i "s/tuic:\/\/[0-9a-f\-]\{36\}/tuic:\/\/$new_uuid/" /etc/sing-box/url.txt
isp=$(curl -s https://speed.cloudflare.com/meta | awk -F\" '{print $26"-"$18}' | sed -e 's/ /_/g')
argodomain=$(grep -oE 'https://[[:alnum:]+\.-]+\.trycloudflare\.com' "${work_dir}/argo.log" | sed 's@https://@@')
VMESS="{ \"v\": \"2\", \"ps\": \"${isp}\", \"add\": \"www.visa.com.sg\", \"port\": \"443\", \"id\": \"${new_uuid}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"${argodomain}\", \"path\": \"/vmess?ed=2048\", \"tls\": \"tls\", \"sni\": \"${argodomain}\", \"alpn\": \"\", \"fp\": \"randomized\", \"allowlnsecure\": \"flase\"}"
encoded_vmess=$(echo "$VMESS" | base64 -w0)
sed -i -E '/vmess:\/\//{s@vmess://.*@vmess://'"$encoded_vmess"'@}' $client_dir
base64 -w0 $client_dir > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
green "\nUUID已修改为:${purple}${new_uuid}${re} ${green}请更新订阅或手动更改所有节点的UUID${re}\n"
;;
3)
clear
green "\n1. www.joom.com\n\n2. www.stengg.com\n\n3. www.wedgehr.com\n\n4. www.cerebrium.ai\n\n5. www.nazhumi.com\n"
reading "\n请输入新的Reality伪装域名(可自定义输入,回车留空将使用默认1): " new_sni
if [ -z "$new_sni" ]; then
new_sni="www.joom.com"
elif [[ "$new_sni" == "1" ]]; then
new_sni="www.joom.com"
elif [[ "$new_sni" == "2" ]]; then
new_sni="www.stengg.com"
elif [[ "$new_sni" == "3" ]]; then
new_sni="www.wedgehr.com"
elif [[ "$new_sni" == "4" ]]; then
new_sni="www.cerebrium.ai"
elif [[ "$new_sni" == "5" ]]; then
new_sni="www.cerebrium.ai"
else
new_sni="$new_sni"
fi
jq --arg new_sni "$new_sni" '
(.inbounds[] | select(.type == "vless") | .tls.server_name) = $new_sni |
(.inbounds[] | select(.type == "vless") | .tls.reality.handshake.server) = $new_sni
' "$config_dir" > "$config_file.tmp" && mv "$config_file.tmp" "$config_dir"
restart_singbox
sed -i "s/\(vless:\/\/[^\?]*\?\([^\&]*\&\)*sni=\)[^&]*/\1$new_sni/" $client_dir
base64 -w0 $client_dir > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
echo ""
green "\nReality sni已修改为:${purple}${new_sni}${re} ${green}请更新订阅或手动更改reality节点的sni域名${re}\n"
;;
4)
purple "端口跳跃需确保跳跃区间的端口没有被占用,nat鸡请注意可用端口范围,否则可能造成节点不通\n"
reading "请输入跳跃起始端口 (回车跳过将使用随机端口): " min_port
[ -z "$min_port" ] && min_port=$(shuf -i 50000-65000 -n 1)
yellow "你的起始端口为:$min_port"
reading "\n请输入跳跃结束端口 (需大于起始端口): " max_port
[ -z "$max_port" ] && max_port=$(($min_port + 100))
yellow "你的结束端口为:$max_port\n"
purple "正在安装依赖,并设置端口跳跃规则中,请稍等...\n"
listen_port=$(sed -n '/"tag": "hysteria2"/,/}/s/.*"listen_port": \([0-9]*\).*/\1/p' $config_dir)
iptables -t nat -A PREROUTING -p udp --dport $min_port:$max_port -j DNAT --to-destination :$listen_port > /dev/null
command -v ip6tables &> /dev/null && ip6tables -t nat -A PREROUTING -p udp --dport $min_port:$max_port -j DNAT --to-destination :$listen_port > /dev/null
if [ -f /etc/alpine-release ]; then
iptables-save > /etc/iptables/rules.v4
command -v ip6tables &> /dev/null && ip6tables-save > /etc/iptables/rules.v6
cat << 'EOF' > /etc/init.d/iptables
#!/sbin/openrc-run
depend() {
need net
}
start() {
[ -f /etc/iptables/rules.v4 ] && iptables-restore < /etc/iptables/rules.v4
command -v ip6tables &> /dev/null && [ -f /etc/iptables/rules.v6 ] && ip6tables-restore < /etc/iptables/rules.v6
}
EOF
chmod +x /etc/init.d/iptables && rc-update add iptables default && /etc/init.d/iptables start
elif [ -f /etc/debian_version ]; then
DEBIAN_FRONTEND=noninteractive apt install -y iptables-persistent > /dev/null 2>&1 && netfilter-persistent save > /dev/null 2>&1
systemctl enable netfilter-persistent > /dev/null 2>&1 && systemctl start netfilter-persistent > /dev/null 2>&1
elif [ -f /etc/redhat-release ]; then
manage_packages install iptables-services > /dev/null 2>&1 && service iptables save > /dev/null 2>&1
systemctl enable iptables > /dev/null 2>&1 && systemctl start iptables > /dev/null 2>&1
command -v ip6tables &> /dev/null && service ip6tables save > /dev/null 2>&1
systemctl enable ip6tables > /dev/null 2>&1 && systemctl start ip6tables > /dev/null 2>&1
else
red "未知系统,请自行将跳跃端口转发到主端口" && exit 1
fi
restart_singbox
ip=$(get_realip)
uuid=$(sed -n 's/.*hysteria2:\/\/\([^@]*\)@.*/\1/p' $client_dir)
line_number=$(grep -n 'hysteria2://' $client_dir | cut -d':' -f1)
isp=$(curl -s --max-time 2 https://speed.cloudflare.com/meta | awk -F\" '{print $26"-"$18}' | sed -e 's/ /_/g' || echo "vps")
sed -i.bak "/hysteria2:/d" $client_dir
sed -i "${line_number}i hysteria2://$uuid@$ip:$listen_port?peer=www.bing.com&insecure=1&alpn=h3&obfs=none&mport=$listen_port,$min_port-$max_port#$isp" $client_dir
base64 -w0 $client_dir > /etc/sing-box/sub.txt
while IFS= read -r line; do yellow "$line"; done < ${work_dir}/url.txt
green "\nhysteria2端口跳跃已开启,跳跃端口为:${purple}$min_port-$max_port${re} ${green}请更新订阅或手动复制以上hysteria2节点${re}\n"
;;
5)
iptables -t nat -F PREROUTING > /dev/null 2>&1
command -v ip6tables &> /dev/null && ip6tables -t nat -F PREROUTING > /dev/null 2>&1
if [ -f /etc/alpine-release ]; then
rc-update del iptables default && rm -rf /etc/init.d/iptables
elif [ -f /etc/redhat-release ]; then
netfilter-persistent save > /dev/null 2>&1
elif [ -f /etc/redhat-release ]; then
service iptables save > /dev/null 2>&1
command -v ip6tables &> /dev/null && service ip6tables save > /dev/null 2>&1
else
manage_packages uninstall iptables ip6tables iptables-persistent iptables-service > /dev/null 2>&1
fi
sed -i '/hysteria2/s/&mport=[^#&]*//g' /etc/sing-box/url.txt
base64 -w0 $client_dir > /etc/sing-box/sub.txt
green "\n端口跳跃已删除\n"
;;
6) menu ;;
*) read "无效的选项!" ;;
esac
else
yellow "sing-box 尚未安装!"
sleep 1
menu
fi
}
disable_open_sub() {
if [ ${check_singbox} -eq 0 ]; then
clear
echo ""
green "1. 关闭节点订阅"
skyblue "------------"
green "2. 开启节点订阅"
skyblue "------------"
green "3. 更换订阅端口"
skyblue "------------"
purple "4. 返回主菜单"
skyblue "------------"
reading "请输入选择: " choice
case "${choice}" in
1)
if command -v nginx &>/dev/null; then
if [ -f /etc/alpine-release ]; then
rc-service nginx status | grep -q "started" && rc-service nginx stop || red "nginx not running"
else
[ "$(systemctl is-active nginx)" = "active" ] && systemctl stop nginx || red "ngixn not running"
fi
else
yellow "Nginx is not installed"
fi
green "\n已关闭节点订阅\n"
;;
2)
green "\n已开启节点订阅\n"
server_ip=$(get_realip)
password=$(tr -dc A-Za-z < /dev/urandom | head -c 32)
sed -i -E "s/(location \/)[^ ]+/\1${password//\//\\/}/" /etc/nginx/nginx.conf
sub_port=$(port=$(grep -E 'listen [0-9]+;' /etc/nginx/nginx.conf | awk '{print $2}' | sed 's/;//'); if [ "$port" -eq 80 ]; then echo ""; else echo "$port"; fi)
start_nginx
(port=$(grep -E 'listen [0-9]+;' /etc/nginx/nginx.conf | awk '{print $2}' | sed 's/;//'); if [ "$port" -eq 80 ]; then echo ""; else green "订阅端口:$port"; fi); link=$(if [ -z "$sub_port" ]; then echo "http://$server_ip/$password"; else echo "http://$server_ip:$sub_port/$password"; fi); green "\n新的节点订阅链接:$link\n"
;;
3)
reading "请输入新的订阅端口(1-65535):" sub_port
[ -z "$sub_port" ] && sub_port=$(shuf -i 2000-65000 -n 1)
until [[ -z $(netstat -tuln | grep -w tcp | awk '{print $4}' | sed 's/.*://g' | grep -w "$sub_port") ]]; do
if [[ -n $(netstat -tuln | grep -w tcp | awk '{print $4}' | sed 's/.*://g' | grep -w "$sub_port") ]]; then
echo -e "${red}${new_port}端口已经被其他程序占用,请更换端口重试${re}"
reading "请输入新的订阅端口(1-65535):" sub_port
[[ -z $sub_port ]] && sub_port=$(shuf -i 2000-65000 -n 1)
fi
done
sed -i 's/listen [0-9]\+;/listen '$sub_port';/g' /etc/nginx/nginx.conf
path=$(sed -n 's/.*location \/\([^ ]*\).*/\1/p' /etc/nginx/nginx.conf)
server_ip=$(get_realip)
restart_nginx
green "\n订阅端口更换成功\n"
green "新的订阅链接为:http://$server_ip:$sub_port/$path\n"
;;
4) menu ;;
*) red "无效的选项!" ;;
esac
else
yellow "sing-box 尚未安装!"
sleep 1
menu
fi
}
# singbox 管理
manage_singbox() {
green "1. 启动sing-box服务"
skyblue "-------------------"
green "2. 停止sing-box服务"
skyblue "-------------------"
green "3. 重启sing-box服务"
skyblue "-------------------"
purple "4. 返回主菜单"
skyblue "------------"
reading "\n请输入选择: " choice
case "${choice}" in
1) start_singbox ;;
2) stop_singbox ;;
3) restart_singbox ;;
4) menu ;;
*) red "无效的选项!" ;;
esac
}
# Argo 管理
manage_argo() {
if [ ${check_argo} -eq 2 ]; then
yellow "Argo 尚未安装!"
sleep 1
menu
else
clear
echo ""
green "1. 启动Argo服务"
skyblue "------------"
green "2. 停止Argo服务"
skyblue "------------"
green "3. 重启Argo服务"
skyblue "------------"
green "4. 添加Argo固定隧道"
skyblue "----------------"
green "5. 切换回Argo临时隧道"
skyblue "------------------"
green "6. 重新获取Argo临时域名"
skyblue "-------------------"
purple "7. 返回主菜单"
skyblue "-----------"
reading "\n请输入选择: " choice
case "${choice}" in
1) start_argo ;;
2) stop_argo ;;
3) clear
if [ -f /etc/alpine-release ]; then
grep -Fq -- '--url http://localhost:8001' /etc/init.d/argo && get_quick_tunnel && change_argo_domain || { green "\n当前使用固定隧道,无需获取临时域名"; sleep 2; menu; }
else
grep -q 'ExecStart=.*--url http://localhost:8001' /etc/systemd/system/argo.service && get_quick_tunnel && change_argo_domain || { green "\n当前使用固定隧道,无需获取临时域名"; sleep 2; menu; }
fi
;;
4)
clear
yellow "\n固定隧道可为json或token,固定隧道端口为8001,自行在cf后台设置\n\njson在f佬维护的站点里获取,获取地址:${purple}https://fscarmen.cloudflare.now.cc${re}\n"
reading "\n请输入你的argo域名: " argo_domain
ArgoDomain=$argo_domain
reading "\n请输入你的argo密钥(token或json): " argo_auth
if [[ $argo_auth =~ TunnelSecret ]]; then
echo $argo_auth > ${work_dir}/tunnel.json
cat > ${work_dir}/tunnel.yml << EOF
tunnel: $(cut -d\" -f12 <<< "$argo_auth")
credentials-file: ${work_dir}/tunnel.json
protocol: http2
ingress:
- hostname: $ArgoDomain
service: http://localhost:8001
originRequest:
noTLSVerify: true
- service: http_status:404
EOF
if [ -f /etc/alpine-release ]; then
sed -i '/^command_args=/c\command_args="-c '\''/etc/sing-box/argo tunnel --edge-ip-version auto --config /etc/sing-box/tunnel.yml run 2>&1'\''"' /etc/init.d/argo
else
sed -i '/^ExecStart=/c ExecStart=/bin/sh -c "/etc/sing-box/argo tunnel --edge-ip-version auto --config /etc/sing-box/tunnel.yml run 2>&1"' /etc/systemd/system/argo.service
fi
restart_argo
sleep 1
change_argo_domain
elif [[ $argo_auth =~ ^[A-Z0-9a-z=]{120,250}$ ]]; then
if [ -f /etc/alpine-release ]; then
sed -i "/^command_args=/c\command_args=\"-c '/etc/sing-box/argo tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token $argo_auth 2>&1'\"" /etc/init.d/argo
else
sed -i '/^ExecStart=/c ExecStart=/bin/sh -c "/etc/sing-box/argo tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token '$argo_auth' 2>&1"' /etc/systemd/system/argo.service
fi
restart_argo
sleep 1
change_argo_domain
else
yellow "你输入的argo域名或token不匹配,请重新输入"
manage_argo
fi
;;
5)
clear
if [ -f /etc/alpine-release ]; then
alpine_openrc_services
else
main_systemd_services
fi
get_quick_tunnel
change_argo_domain
;;
6)
if [ -f /etc/alpine-release ]; then
if grep -Fq -- '--url http://localhost:8001' /etc/init.d/argo; then
get_quick_tunnel
change_argo_domain
else
yellow "当前使用固定隧道,无法获取临时隧道"
sleep 2
menu
fi
else
if grep -q 'ExecStart=.*--url http://localhost:8001' /etc/systemd/system/argo.service; then
get_quick_tunnel
change_argo_domain
else
yellow "当前使用固定隧道,无法获取临时隧道"
sleep 2
menu
fi
fi
;;
7) menu ;;
*) red "无效的选项!" ;;
esac
fi
}
# 获取argo临时隧道
get_quick_tunnel() {
restart_argo
yellow "获取临时argo域名中,请稍等...\n"
sleep 3
if [ -f /etc/sing-box/argo.log ]; then
for i in {1..5}; do
purple "$i 次尝试获取ArgoDoamin中..."
get_argodomain=$(sed -n 's|.*https://\([^/]*trycloudflare\.com\).*|\1|p' /etc/sing-box/argo.log)
[ -n "$get_argodomain" ] && break
sleep 2
done
else
restart_argo
sleep 6
get_argodomain=$(sed -n 's|.*https://\([^/]*trycloudflare\.com\).*|\1|p' /etc/sing-box/argo.log)
fi
green "ArgoDomain${purple}$get_argodomain${re}\n"
ArgoDomain=$get_argodomain
}
# 更新Argo域名到订阅
change_argo_domain() {
content=$(cat "$client_dir")
vmess_url=$(grep -o 'vmess://[^ ]*' "$client_dir")
vmess_prefix="vmess://"
encoded_vmess="${vmess_url#"$vmess_prefix"}"
decoded_vmess=$(echo "$encoded_vmess" | base64 --decode)
updated_vmess=$(echo "$decoded_vmess" | jq --arg new_domain "$ArgoDomain" '.host = $new_domain | .sni = $new_domain')
encoded_updated_vmess=$(echo "$updated_vmess" | base64 | tr -d '\n')
new_vmess_url="$vmess_prefix$encoded_updated_vmess"
new_content=$(echo "$content" | sed "s|$vmess_url|$new_vmess_url|")
echo "$new_content" > "$client_dir"
base64 -w0 ${work_dir}/url.txt > ${work_dir}/sub.txt
green "vmess节点已更新,更新订阅或手动复制以下vmess-argo节点\n"
purple "$new_vmess_url\n"
}
# 查看节点信息和订阅链接
check_nodes() {
if [ ${check_singbox} -eq 0 ]; then
while IFS= read -r line; do purple "${purple}$line"; done < ${work_dir}/url.txt
server_ip=$(get_realip)
lujing=$(sed -n 's|.*location /||p' /etc/nginx/nginx.conf | awk '{print $1}')
sub_port=$(sed -n 's/^\s*listen \([0-9]\+\);/\1/p' /etc/nginx/nginx.conf)
green "\n节点订阅链接:http://${server_ip}:${sub_port}/${lujing}\n"
else
yellow "sing-box 尚未安装或未运行,请先安装或启动sing-box"
sleep 1
menu
fi
}
# 主菜单
menu() {
check_singbox &>/dev/null; check_singbox=$?
check_nginx &>/dev/null; check_nginx=$?
check_argo &>/dev/null; check_argo=$?
check_singbox_status=$(check_singbox) > /dev/null 2>&1
check_nginx_status=$(check_nginx) > /dev/null 2>&1
check_argo_status=$(check_argo) > /dev/null 2>&1
clear
echo ""
purple "=== 老王sing-box一键安装脚本 ===\n"
purple "---Argo 状态: ${check_argo_status}"
purple "--Nginx 状态: ${check_nginx_status}"
purple "singbox 状态: ${check_singbox_status}\n"
green "1. 安装sing-box"
red "2. 卸载sing-box"
echo "==============="
green "3. sing-box管理"
green "4. Argo隧道管理"
echo "==============="
green "5. 查看节点信息"
green "6. 修改节点配置"
green "7. 管理节点订阅"
echo "==============="
purple "8. ssh综合工具箱"
echo "==============="
red "0. 退出脚本"
echo "==========="
reading "请输入选择(0-8): " choice
echo ""
}
# 捕获 Ctrl+C 信号
trap 'red "已取消操作"; exit' INT
# 主循环
while true; do
menu
case "${choice}" in
1)
if [ ${check_singbox} -eq 0 ]; then
yellow "sing-box 已经安装!"
else
fix_nginx
manage_packages install nginx jq tar openssl iptables coreutils
[ -n "$(curl -s --max-time 2 ipv6.ip.sb)" ] && manage_packages install ip6tables
install_singbox
if [ -x "$(command -v systemctl)" ]; then
main_systemd_services
elif [ -x "$(command -v rc-update)" ]; then
alpine_openrc_services
change_hosts
rc-service sing-box restart
rc-service argo restart
else
echo "Unsupported init system"
exit 1
fi
sleep 5
get_info
add_nginx_conf
create_shortcut
fi
;;
2) uninstall_singbox ;;
3) manage_singbox ;;
4) manage_argo ;;
5) check_nodes ;;
6) change_config ;;
7) disable_open_sub ;;
8)
clear
curl -fsSL https://raw.githubusercontent.com/eooce/ssh_tool/main/ssh_tool.sh -o ssh_tool.sh && chmod +x ssh_tool.sh && ./ssh_tool.sh
;;
0) exit 0 ;;
*) red "无效的选项,请输入 0 到 8" ;;
esac
read -n 1 -s -r -p $'\033[1;91m按任意键继续...\033[0m'
done