Files
Sing-box/sing-box.sh
T
2025-08-19 01:39:59 +08:00

1523 lines
56 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
# =========================
# 老王sing-box四合一安装脚本
# vless-version-reality|vmess-ws-tls(tunnel)|hysteria2|tuic5
# 最后更新时间: 2025.8.18
# =========================
export LANG=en_US.UTF-8
# 定义颜色
re="\033[0m"
red="\033[1;91m"
green="\e[1;32m"
yellow="\e[1;33m"
purple="\e[1;35m"
skyblue="\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:-'cf.877774.xyz'}
export CFPORT=${CFPORT:-'443'}
# 检查是否为root下运行
[[ $EUID -ne 0 ]] && red "请在root用户下运行脚本" && exit 1
# 检查命令是否存在函数
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# 检查服务状态通用函数
check_service() {
local service_name=$1
local service_file=$2
[[ ! -f "${service_file}" ]] && { red "not installed"; return 2; }
if command_exists apk; then
rc-service "${service_name}" status | grep -q "started" && green "running" || yellow "not running"
else
systemctl is-active "${service_name}" | grep -q "^active$" && green "running" || yellow "not running"
fi
return $?
}
# 检查sing-box状态
check_singbox() {
check_service "sing-box" "${work_dir}/${server_name}"
}
# 检查argo状态
check_argo() {
check_service "argo" "${work_dir}/argo"
}
# 检查nginx状态
check_nginx() {
command_exists nginx || { red "not installed"; return 2; }
check_service "nginx" "$(command -v nginx)"
}
#根据系统类型安装、卸载依赖
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_exists "$package"; then
green "${package} already installed"
continue
fi
yellow "正在安装 ${package}..."
if command_exists apt; then
DEBIAN_FRONTEND=noninteractive apt install -y "$package"
elif command_exists dnf; then
dnf install -y "$package"
elif command_exists yum; then
yum install -y "$package"
elif command_exists apk; then
apk update
apk add "$package"
else
red "Unknown system!"
return 1
fi
elif [ "$action" == "uninstall" ]; then
if ! command_exists "$package"; then
yellow "${package} is not installed"
continue
fi
yellow "正在卸载 ${package}..."
if command_exists apt; then
apt remove -y "$package" && apt autoremove -y
elif command_exists dnf; then
dnf remove -y "$package" && dnf autoremove -y
elif command_exists yum; then
yum remove -y "$package" && yum autoremove -y
elif command_exists apk; 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 -4 -sm 2 ip.sb)
ipv6() { curl -6 -sm 2 ip.sb; }
if [ -z "$ip" ]; then
echo "[$(ipv6)]"
elif curl -4 -sm 2 http://ipinfo.io/org | grep -qE 'Cloudflare|UnReal|AEZA|Andrei'; then
echo "[$(ipv6)]"
else
resp=$(curl -sm 8 "https://status.eooce.com/api/$ip" | jq -r '.status')
if [ "$resp" = "Available" ]; then
echo "$ip"
else
v6=$(ipv6)
[ -n "$v6" ] && echo "[$v6]" || echo "$ip"
fi
fi
}
# 处理防火墙
allow_port() {
has_ufw=0
has_firewalld=0
has_iptables=0
has_ip6tables=0
command_exists ufw && has_ufw=1
command_exists firewall-cmd && systemctl is-active firewalld >/dev/null 2>&1 && has_firewalld=1
command_exists iptables && has_iptables=1
command_exists ip6tables && has_ip6tables=1
# 出站
[ "$has_ufw" -eq 1 ] && ufw --force default allow outgoing
[ "$has_firewalld" -eq 1 ] && firewall-cmd --permanent --zone=public --set-target=ACCEPT
[ "$has_iptables" -eq 1 ] && iptables -P OUTPUT ACCEPT
[ "$has_ip6tables" -eq 1 ] && ip6tables -P OUTPUT ACCEPT
# 入站
for rule in "$@"; do
port=${rule%/*}
proto=${rule#*/}
[ "$has_ufw" -eq 1 ] && ufw allow in ${port}/${proto}
[ "$has_firewalld" -eq 1 ] && firewall-cmd --permanent --add-port=${port}/${proto}
[ "$has_iptables" -eq 1 ] && (iptables -C INPUT -p ${proto} --dport ${port} -j ACCEPT 2>/dev/null || iptables -A INPUT -p ${proto} --dport ${port} -j ACCEPT)
[ "$has_ip6tables" -eq 1 ] && (ip6tables -C INPUT -p ${proto} --dport ${port} -j ACCEPT 2>/dev/null || ip6tables -A INPUT -p ${proto} --dport ${port} -j ACCEPT)
done
[ "$has_firewalld" -eq 1 ] && firewall-cmd --reload
# 规则持久化
if command_exists rc-service 2>/dev/null; then
[ "$has_iptables" -eq 1 ] && iptables-save > /etc/iptables/rules.v4
[ "$has_ip6tables" -eq 1 ] && ip6tables-save > /etc/iptables/rules.v6
else
if ! command_exists netfilter-persistent; then
manage_packages install iptables-persistent || yellow "请手动安装netfilter-persistent或保存iptables规则"
netfilter-persistent save >/dev/null 2>&1
elif command_exists service; then
service iptables save 2>/dev/null
service ip6tables save 2>/dev/null
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}/qrencode" "https://$ARCH.ssss.nyc.mn/qrencode"
curl -sLo "${work_dir}/sing-box" "https://$ARCH.ssss.nyc.mn/sbx"
curl -sLo "${work_dir}/argo" "https://$ARCH.ssss.nyc.mn/bot"
# 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}')
# 放行端口
allow_port $vless_port/tcp $nginx_port/tcp $tuic_port/udp $hy2_port/udp > /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": "error",
"output": "$work_dir/sb.log",
"timestamp": true
},
"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-argo",
"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": [
{
"tag": "direct",
"type": "direct"
},
{
"tag": "block",
"type": "block"
},
{
"tag": "wireguard-out",
"type": "wireguard",
"local_address": [
"172.16.0.2/32",
"2606:4700:110:8f77:1ca9:f086:846c:5f9e/128"
],
"server": "162.159.192.200",
"server_port": 4500,
"peer_public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
"private_key": "wIxszdR2nMdA7a2Ul3XQcniSfSZqdqjPb6w6opvf5AU=",
"reserved": [
126,
246,
173
]
}
],
"route": {
"final": "direct",
"rule_set": [
{
"tag": "netflix",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geosite/netflix.srs",
"download_detour": "direct"
},
{
"tag": "openai",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geosite/openai.srs",
"download_detour": "direct"
}
],
"rules": [
{
"outbound": "wireguard-out",
"rule_set": [
"netflix",
"openai"
]
}
]
}
}
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 > /dev/null 2>&1
rc-update add argo default > /dev/null 2>&1
}
# 生成节点和订阅链接
get_info() {
yellow "\nip检测中,请稍等...\n"
server_ip=$(get_realip)
clear
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-argo?ed=2560\", \"tls\": \"tls\", \"sni\": \"${argodomain}\", \"alpn\": \"\", \"fp\": \"chrome\", \"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
chmod 644 ${work_dir}/sub.txt
yellow "\n温馨提醒:需打开V2rayN或其他软件里的 "跳过证书验证",或将节点的Insecure或TLS里设置为"true"\n"
green "V2rayN,Shadowrocket,Nekobox,Loon,Karing,Sterisand订阅链接:http://${server_ip}:${nginx_port}/${password}\n"
$work_dir/qrencode "http://${server_ip}:${nginx_port}/${password}"
yellow "\n=========================================================================================="
green "\n\nClash,Mihomo系列订阅链接:https://sublink.eooce.com/clash?config=http://${server_ip}:${nginx_port}/${password}\n"
$work_dir/qrencode "https://sublink.eooce.com/clash?config=http://${server_ip}:${nginx_port}/${password}"
yellow "\n=========================================================================================="
green "\n\nSing-box订阅链接:https://sublink.eooce.com/singbox?config=http://${server_ip}:${nginx_port}/${password}\n"
$work_dir/qrencode "https://sublink.eooce.com/singbox?config=http://${server_ip}:${nginx_port}/${password}"
yellow "\n=========================================================================================="
green "\n\nSurge订阅链接:https://sublink.eooce.com/surge?config=http://${server_ip}:${nginx_port}/${password}\n"
$work_dir/qrencode "https://sublink.eooce.com/surge?config=http://${server_ip}:${nginx_port}/${password}"
yellow "\n==========================================================================================\n"
}
# nginx订阅配置
add_nginx_conf() {
if ! command_exists nginx; then
red "nginx未安装,无法配置订阅服务"
return 1
else
manage_service "nginx" "stop" > /dev/null 2>&1
pkill nginx > /dev/null 2>&1
fi
mkdir -p /etc/nginx/conf.d
[[ -f "/etc/nginx/conf.d/sing-box.conf" ]] && cp /etc/nginx/conf.d/sing-box.conf /etc/nginx/conf.d/sing-box.conf.bak.sb
cat > /etc/nginx/conf.d/sing-box.conf << EOF
# sing-box 订阅配置
server {
listen $nginx_port;
listen [::]:$nginx_port;
server_name _;
# 安全设置
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
location = /$password {
alias /etc/sing-box/sub.txt;
default_type 'text/plain; charset=utf-8';
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
location / {
return 404;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
EOF
# 检查主配置文件是否存在
if [ -f "/etc/nginx/nginx.conf" ]; then
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.sb > /dev/null 2>&1
sed -i -e '15{/include \/etc\/nginx\/modules\/\*\.conf/d;}' -e '18{/include \/etc\/nginx\/conf\.d\/\*\.conf/d;}' /etc/nginx/nginx.conf > /dev/null 2>&1
# 检查是否已包含配置目录
if ! grep -q "include.*conf.d" /etc/nginx/nginx.conf; then
http_end_line=$(grep -n "^}" /etc/nginx/nginx.conf | tail -1 | cut -d: -f1)
if [ -n "$http_end_line" ]; then
sed -i "${http_end_line}i \ include /etc/nginx/conf.d/*.conf;" /etc/nginx/nginx.conf > /dev/null 2>&1
fi
fi
else
cat > /etc/nginx/nginx.conf << EOF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
EOF
fi
# 检查nginx配置语法
if nginx -t > /dev/null 2>&1; then
if nginx -s reload > /dev/null 2>&1; then
green "nginx订阅配置已加载"
else
start_nginx > /dev/null 2>&1
fi
else
yellow "nginx配置失败,订阅不可应,但不影响节点使用, issues反馈: https://github.com/eooce/Sing-box/issues"
restart_nginx > /dev/null 2>&1
if [ $? -eq 0 ]; then
green "nginx订阅配置已生效"
else
[[ -f "/etc/nginx/nginx.conf.bak.sb" ]] && cp "/etc/nginx/nginx.conf.bak.sb" /etc/nginx/nginx.conf > /dev/null 2>&1
restart_nginx > /dev/null 2>&1
fi
fi
}
# 通用服务管理函数
manage_service() {
local service_name="$1"
local action="$2"
if [ -z "$service_name" ] || [ -z "$action" ]; then
red "缺少服务名或操作参数\n"
return 1
fi
local status=$(check_service "$service_name" 2>/dev/null)
case "$action" in
"start")
if [ "$status" == "running" ]; then
yellow "${service_name} 正在运行\n"
return 0
elif [ "$status" == "not installed" ]; then
yellow "${service_name} 尚未安装!\n"
return 1
else
yellow "正在启动 ${service_name} 服务\n"
if command_exists rc-service; then
rc-service "$service_name" start
elif command_exists systemctl; then
systemctl daemon-reload
systemctl start "$service_name"
fi
if [ $? -eq 0 ]; then
green "${service_name} 服务已成功启动\n"
return 0
else
red "${service_name} 服务启动失败\n"
return 1
fi
fi
;;
"stop")
if [ "$status" == "not installed" ]; then
yellow "${service_name} 尚未安装!\n"
return 2
elif [ "$status" == "not running" ]; then
yellow "${service_name} 未运行\n"
return 1
else
yellow "正在停止 ${service_name} 服务\n"
if command_exists rc-service; then
rc-service "$service_name" stop
elif command_exists systemctl; then
systemctl stop "$service_name"
fi
if [ $? -eq 0 ]; then
green "${service_name} 服务已成功停止\n"
return 0
else
red "${service_name} 服务停止失败\n"
return 1
fi
fi
;;
"restart")
if [ "$status" == "not installed" ]; then
yellow "${service_name} 尚未安装!\n"
return 1
else
yellow "正在重启 ${service_name} 服务\n"
if command_exists rc-service; then
rc-service "$service_name" restart
elif command_exists systemctl; then
systemctl daemon-reload
systemctl restart "$service_name"
fi
if [ $? -eq 0 ]; then
green "${service_name} 服务已成功重启\n"
return 0
else
red "${service_name} 服务重启失败\n"
return 1
fi
fi
;;
*)
red "无效的操作: $action\n"
red "可用操作: start, stop, restart\n"
return 1
;;
esac
}
# 启动 sing-box
start_singbox() {
manage_service "sing-box" "start"
}
# 停止 sing-box
stop_singbox() {
manage_service "sing-box" "stop"
}
# 重启 sing-box
restart_singbox() {
manage_service "sing-box" "restart"
}
# 启动 argo
start_argo() {
manage_service "argo" "start"
}
# 停止 argo
stop_argo() {
manage_service "argo" "stop"
}
# 重启 argo
restart_argo() {
manage_service "argo" "restart"
}
# 启动 nginx
start_nginx() {
manage_service "nginx" "start"
}
# 重启 nginx
restart_nginx() {
manage_service "nginx" "restart"
}
# 卸载 sing-box
uninstall_singbox() {
reading "确定要卸载 sing-box 吗? (y/n): " choice
case "${choice}" in
y|Y)
yellow "正在卸载 sing-box"
if command_exists rc-service; 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 -rf "${log_dir}" || true
rm -rf /etc/systemd/system/sing-box.service /etc/systemd/system/argo.service > /dev/null 2>&1
rm -rf /etc/nginx/conf.d/sing-box.conf > /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() {
# 检查sing-box状态
local singbox_status=$(check_singbox 2>/dev/null)
local singbox_installed=$?
if [ $singbox_installed -eq 2 ]; then
yellow "sing-box 尚未安装!"
sleep 1
menu
return
fi
clear
echo ""
green "=== 修改节点配置 ===\n"
green "sing-box当前状态: $singbox_status\n"
green "1. 修改端口"
skyblue "------------"
green "2. 修改UUID"
skyblue "------------"
green "3. 修改Reality伪装域名"
skyblue "------------"
green "4. 添加hysteria2端口跳跃"
skyblue "------------"
green "5. 删除hysteria2端口跳跃"
skyblue "------------"
green "6. 修改vmess-argo优选域名"
skyblue "------------"
purple "0. 返回主菜单"
skyblue "------------"
reading "请输入选择: " choice
case "${choice}" in
1)
echo ""
green "1. 修改vless-reality端口"
skyblue "------------"
green "2. 修改hysteria2端口"
skyblue "------------"
green "3. 修改tuic端口"
skyblue "------------"
green "4. 修改vmess-argo端口"
skyblue "------------"
purple "0. 返回上一级菜单"
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
allow_port $new_port/tcp > /dev/null 2>&1
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
allow_port $new_port/udp > /dev/null 2>&1
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
allow_port $new_port/udp > /dev/null 2>&1
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)
reading "\n请输入vmess-argo端口 (回车跳过将使用随机端口): " new_port
[ -z "$new_port" ] && new_port=$(shuf -i 2000-65000 -n 1)
sed -i '/"type": "vmess"/,/listen_port/ s/"listen_port": [0-9]\+/"listen_port": '"$new_port"'/' $config_dir
allow_port $new_port/tcp > /dev/null 2>&1
if command_exists rc-service; then
if grep -q "localhost:" /etc/init.d/argo; then
sed -i 's/localhost:[0-9]\{1,\}/localhost:'"$new_port"'/' /etc/init.d/argo
get_quick_tunnel
change_argo_domain
fi
else
if grep -q "localhost:" /etc/systemd/system/argo.service; then
sed -i 's/localhost:[0-9]\{1,\}/localhost:'"$new_port"'/' /etc/systemd/system/argo.service
get_quick_tunnel
change_argo_domain
fi
fi
if [ -f /etc/sing-box/tunnel.yml ]; then
sed -i 's/localhost:[0-9]\{1,\}/localhost:'"$new_port"'/' /etc/sing-box/tunnel.yml
restart_argo
fi
if ([ -f /etc/systemd/system/argo.service ] && grep -q -- "--token" /etc/systemd/system/argo.service) || \
([ -f /etc/init.d/argo ] && grep -q -- "--token" /etc/init.d/argo); then
yellow "请在cloudflared里也对应修改端口为:${purple}${new_port}${re}\n"
fi
restart_singbox
green "\nvmess-argo端口已修改为:${purple}${new_port}${re}\n"
;;
0) 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.tw\", \"port\": \"443\", \"id\": \"${new_uuid}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"${argodomain}\", \"path\": \"/vmess-argo?ed=2560\", \"tls\": \"tls\", \"sni\": \"${argodomain}\", \"alpn\": \"\", \"fp\": \"\", \"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.nazhumi.com"
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 command_exists rc-service 2>/dev/null; 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 command_exists rc-service 2>/dev/null; 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) change_cfip ;;
0) menu ;;
*) read "无效的选项!" ;;
esac
}
disable_open_sub() {
local singbox_status=$(check_singbox 2>/dev/null)
local singbox_installed=$?
if [ $singbox_installed -eq 2 ]; then
yellow "sing-box 尚未安装!"
sleep 1
menu
return
fi
clear
echo ""
green "=== 管理节点订阅 ===\n"
skyblue "------------"
green "1. 关闭节点订阅"
skyblue "------------"
green "2. 开启节点订阅"
skyblue "------------"
green "3. 更换订阅端口"
skyblue "------------"
purple "0. 返回主菜单"
skyblue "------------"
reading "请输入选择: " choice
case "${choice}" in
1)
if command -v nginx &>/dev/null; then
if command_exists rc-service 2>/dev/null; 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 "s|\(location = /\)[^ ]*|\1$password|" /etc/nginx/conf.d/sing-box.conf
sub_port=$(port=$(grep -E 'listen [0-9]+;' "/etc/nginx/conf.d/sing-box.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/conf.d/sing-box.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 $(lsof -iTCP:"$sub_port" -sTCP:LISTEN -t) ]]; do
if [[ -n $(lsof -iTCP:"$sub_port" -sTCP:LISTEN -t) ]]; then
echo -e "${red}端口 $sub_port 已经被其他程序占用,请更换端口重试${re}"
reading "请输入新的订阅端口(1-65535):" sub_port
[[ -z $sub_port ]] && sub_port=$(shuf -i 2000-65000 -n 1)
fi
done
# 备份当前配置
if [ -f "/etc/nginx/conf.d/sing-box.conf" ]; then
cp "/etc/nginx/conf.d/sing-box.conf" "/etc/nginx/conf.d/sing-box.conf.bak.$(date +%Y%m%d)"
fi
# 更新端口配置
sed -i 's/listen [0-9]\+;/listen '$sub_port';/g' "/etc/nginx/conf.d/sing-box.conf"
sed -i 's/listen \[::\]:[0-9]\+;/listen [::]:'$sub_port';/g' "/etc/nginx/conf.d/sing-box.conf"
path=$(sed -n 's|.*location = /\([^ ]*\).*|\1|p' "/etc/nginx/conf.d/sing-box.conf")
server_ip=$(get_realip)
# 放行新端口
allow_port $sub_port/tcp > /dev/null 2>&1
# 测试nginx配置
if nginx -t > /dev/null 2>&1; then
# 尝试重新加载配置
if nginx -s reload > /dev/null 2>&1; then
green "nginx配置已重新加载,端口更换成功"
else
yellow "配置重新加载失败,尝试重启nginx服务..."
restart_nginx
fi
green "\n订阅端口更换成功\n"
green "新的订阅链接为:http://$server_ip:$sub_port/$path\n"
else
red "nginx配置测试失败,正在恢复原有配置..."
if [ -f "/etc/nginx/conf.d/sing-box.conf.bak."* ]; then
latest_backup=$(ls -t /etc/nginx/conf.d/sing-box.conf.bak.* | head -1)
cp "$latest_backup" "/etc/nginx/conf.d/sing-box.conf"
yellow "已恢复原有nginx配置"
fi
return 1
fi
;;
0) menu ;;
*) red "无效的选项!" ;;
esac
}
# singbox 管理
manage_singbox() {
# 检查sing-box状态
local singbox_status=$(check_singbox 2>/dev/null)
local singbox_installed=$?
clear
echo ""
green "=== sing-box 管理 ===\n"
green "sing-box当前状态: $singbox_status\n"
green "1. 启动sing-box服务"
skyblue "-------------------"
green "2. 停止sing-box服务"
skyblue "-------------------"
green "3. 重启sing-box服务"
skyblue "-------------------"
purple "0. 返回主菜单"
skyblue "------------"
reading "\n请输入选择: " choice
case "${choice}" in
1) start_singbox ;;
2) stop_singbox ;;
3) restart_singbox ;;
0) menu ;;
*) red "无效的选项!" && sleep 1 && manage_singbox;;
esac
}
# Argo 管理
manage_argo() {
# 检查Argo状态
local argo_status=$(check_argo 2>/dev/null)
local argo_installed=$?
clear
echo ""
green "=== Argo 隧道管理 ===\n"
green "Argo当前状态: $argo_status\n"
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 "0. 返回主菜单"
skyblue "-----------"
reading "\n请输入选择: " choice
case "${choice}" in
1) start_argo ;;
2) stop_argo ;;
3) clear
if command_exists rc-service 2>/dev/null; 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 command_exists rc-service 2>/dev/null; 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 command_exists rc-service 2>/dev/null; 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 command_exists rc-service 2>/dev/null; then
alpine_openrc_services
else
main_systemd_services
fi
get_quick_tunnel
change_argo_domain
;;
6)
if command_exists rc-service 2>/dev/null; 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
;;
0) menu ;;
*) red "无效的选项!" ;;
esac
}
# 获取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() {
while IFS= read -r line; do purple "${purple}$line"; done < ${work_dir}/url.txt
server_ip=$(get_realip)
lujing=$(sed -n 's|.*location = /\([^ ]*\).*|\1|p' "/etc/nginx/conf.d/sing-box.conf")
sub_port=$(sed -n 's/^\s*listen \([0-9]\+\);/\1/p' "/etc/nginx/conf.d/sing-box.conf")
base64_url="http://${server_ip}:${sub_port}/${lujing}"
green "\n\nSurge订阅链接: ${purple}https://sublink.eooce.com/surge?config=${base64_url}${re}\n"
green "sing-box订阅链接: ${purple}https://sublink.eooce.com/singbox?config=${base64_url}${purple}\n"
green "Mihomo/Clash系列订阅链接: ${purple}https://sublink.eooce.com/clash?config=${base64_url}${re}\n"
green "V2rayN,Shadowrocket,Nekobox,Loon,Karing,Sterisand订阅链接: ${purple}${base64_url}${re}\n"
}
change_cfip() {
clear
yellow "修改vmess-argo优选域名\n"
green "1: cf.090227.xyz 2: cf.877774.xyz 3: cf.877771.xyz 4: cdns.doon.eu.org 5: cf.zhetengsha.eu.org 6: time.is\n"
reading "请输入你的优选域名或优选IP\n(请输入1至6选项,可输入域名:端口 或 IP:端口,直接回车默认使用1): " cfip_input
if [ -z "$cfip_input" ]; then
cfip="cf.090227.xyz"
cfport="443"
else
case "$cfip_input" in
"1")
cfip="cf.090227.xyz"
cfport="443"
;;
"2")
cfip="cf.877774.xyz"
cfport="443"
;;
"3")
cfip="cf.877771.xyz"
cfport="443"
;;
"4")
cfip="cdns.doon.eu.org"
cfport="443"
;;
"5")
cfip="cf.zhetengsha.eu.org"
cfport="443"
;;
"6")
cfip="time.is"
cfport="443"
;;
*)
if [[ "$cfip_input" =~ : ]]; then
cfip=$(echo "$cfip_input" | cut -d':' -f1)
cfport=$(echo "$cfip_input" | cut -d':' -f2)
else
cfip="$cfip_input"
cfport="443"
fi
;;
esac
fi
content=$(cat "$client_dir")
vmess_url=$(grep -o 'vmess://[^ ]*' "$client_dir")
encoded_part="${vmess_url#vmess://}"
decoded_json=$(echo "$encoded_part" | base64 --decode 2>/dev/null)
updated_json=$(echo "$decoded_json" | jq --arg cfip "$cfip" --argjson cfport "$cfport" \
'.add = $cfip | .port = $cfport')
new_encoded_part=$(echo "$updated_json" | base64 -w0)
new_vmess_url="vmess://$new_encoded_part"
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 "\nvmess节点优选域名已更新为:${purple}${cfip}:${cfport},${green}更新订阅或手动复制以下vmess-argo节点${re}\n"
purple "$new_vmess_url\n"
}
# 主菜单
menu() {
singbox_status=$(check_singbox 2>/dev/null)
nginx_status=$(check_nginx 2>/dev/null)
argo_status=$(check_argo 2>/dev/null)
clear
echo ""
purple "=== 老王sing-box四合一安装脚本 ===\n"
purple "---Argo 状态: ${argo_status}"
purple "--Nginx 状态: ${nginx_status}"
purple "singbox 状态: ${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-9): " choice
echo ""
}
# 捕获 Ctrl+C 退出信号
trap 'red "已取消操作"; exit' INT
# 主循环
while true; do
menu
case "${choice}" in
1)
check_singbox &>/dev/null; check_singbox=$?
if [ ${check_singbox} -eq 0 ]; then
yellow "sing-box 已经安装!\n"
else
# fix_nginx
manage_packages install nginx jq tar openssl lsof coreutils
install_singbox
if command_exists systemctl; then
main_systemd_services
elif command_exists 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
bash <(curl -Ls ssh_tool.eooce.com)
;;
0) exit 0 ;;
*) red "无效的选项,请输入 0 到 8" ;;
esac
read -n 1 -s -r -p $'\033[1;91m按任意键返回...\033[0m'
done