feat: add optional UDP relay support

This commit is contained in:
OpenClaw
2026-07-20 08:35:22 +08:00
parent a89108cf52
commit bf2390c6ba
2 changed files with 69 additions and 19 deletions

View File

@@ -2,6 +2,8 @@
一键安装 shadowsocks-rust自动配置 ss2022 双节点,生成订阅链接 + Surge/Clash 配置。
安装时可选择是否启用 UDP默认启用。普通节点会使用 TCP+UDPHTTP obfs 节点保留 obfs TCP 前端,并在相同公网端口额外提供原生 Shadowsocks UDP。
## 一键安装
```bash
@@ -15,6 +17,7 @@ curl -sL https://mjjtop.com/ss -o ss-rust.sh && chmod +x ss-rust.sh && ./ss-rust
- 🎲 随机端口 + 随机密钥
- ⏰ 自动 NTP 时间同步
- 📋 自动生成 SS 订阅链接 / Surge 配置 / Clash 配置
- 📡 可控 UDP 转发,生成的 Clash 节点会同步标记 UDP 能力
- 🔄 systemd 管理,开机自启
## 输出文件

View File

@@ -29,6 +29,7 @@ METHOD_OBFS=""
OBFS_BACKEND_PORT=""
OBFS_HOST=""
NODE_MODE=""
ENABLE_UDP="1"
# ============ 基础检测 ============
check_root() {
@@ -228,6 +229,7 @@ select_and_configure() {
PORT_2022="" ; KEY_2022="" ; METHOD_2022=""
PORT_RAW="" ; KEY_RAW="" ; METHOD_RAW=""
PORT_OBFS="" ; KEY_OBFS="" ; METHOD_OBFS="" ; OBFS_BACKEND_PORT="" ; OBFS_HOST=""
ENABLE_UDP="1"
echo ""
echo -e "${CYAN}════════════════════════════════════════${NC}"
@@ -242,6 +244,14 @@ select_and_configure() {
read -rp "请选择 [1-4] (默认3): " node_choice
node_choice=${node_choice:-3}
read -rp "启用 UDP 转发? [Y/n]: " udp_choice
udp_choice=${udp_choice:-Y}
if [[ "$udp_choice" =~ ^[Yy]$ ]]; then
ENABLE_UDP="1"
else
ENABLE_UDP="0"
fi
if [[ "$node_choice" == "1" || "$node_choice" == "3" ]]; then
local dp=$((RANDOM % 10000 + 20000))
local dk=$(openssl rand -base64 16)
@@ -288,6 +298,8 @@ select_and_configure() {
python3 -c "
import json
servers = []
udp_enabled = '${ENABLE_UDP}' == '1'
direct_mode = 'tcp_and_udp' if udp_enabled else 'tcp_only'
if '${PORT_2022}':
servers.append({
'server': '0.0.0.0',
@@ -295,7 +307,8 @@ if '${PORT_2022}':
'method': '${METHOD_2022}',
'password': '${KEY_2022}',
'timeout': 300,
'fast_open': True
'fast_open': True,
'mode': direct_mode
})
if '${PORT_RAW}':
servers.append({
@@ -304,16 +317,29 @@ if '${PORT_RAW}':
'method': '${METHOD_RAW}',
'password': '${KEY_RAW}',
'timeout': 300,
'fast_open': True
'fast_open': True,
'mode': direct_mode
})
if '${PORT_OBFS}':
# simple-obfs only fronts TCP. Keep the obfuscated TCP backend on
# localhost and expose native Shadowsocks UDP on the public port.
servers.append({
'server': '127.0.0.1',
'server_port': int('${OBFS_BACKEND_PORT}'),
'method': '${METHOD_OBFS}',
'password': '${KEY_OBFS}',
'timeout': 300,
'fast_open': True
'fast_open': True,
'mode': 'tcp_only'
})
if udp_enabled:
servers.append({
'server': '0.0.0.0',
'server_port': int('${PORT_OBFS}'),
'method': '${METHOD_OBFS}',
'password': '${KEY_OBFS}',
'timeout': 300,
'mode': 'udp_only'
})
with open('/etc/shadowsocks-rust/config.json', 'w') as f:
json.dump({'servers': servers}, f, indent=4)
@@ -327,6 +353,7 @@ KEY_OBFS=${KEY_OBFS}
METHOD_OBFS=${METHOD_OBFS}
OBFS_BACKEND_PORT=${OBFS_BACKEND_PORT}
OBFS_HOST=${OBFS_HOST}
ENABLE_UDP=${ENABLE_UDP}
EOF
else
rm -f /etc/shadowsocks-rust/obfs.env
@@ -415,12 +442,28 @@ load_config() {
PORT_2022="" ; KEY_2022="" ; METHOD_2022=""
PORT_RAW="" ; KEY_RAW="" ; METHOD_RAW=""
PORT_OBFS="" ; KEY_OBFS="" ; METHOD_OBFS="" ; OBFS_BACKEND_PORT="" ; OBFS_HOST=""
ENABLE_UDP="0"
eval $(python3 -c "
import json
if [[ -f /etc/shadowsocks-rust/obfs.env ]]; then
# shellcheck disable=SC1091
source /etc/shadowsocks-rust/obfs.env
fi
eval $(OBFS_BACKEND_PORT="${OBFS_BACKEND_PORT}" PORT_OBFS="${PORT_OBFS}" python3 -c "
import json, os
with open('/etc/shadowsocks-rust/config.json') as f:
c = json.load(f)
skip_ports = {
int(p) for p in (os.environ.get('OBFS_BACKEND_PORT'), os.environ.get('PORT_OBFS'))
if p and p.isdigit()
}
udp_enabled = False
for s in c['servers']:
mode = s.get('mode', 'tcp_only')
if mode in ('tcp_and_udp', 'udp_only'):
udp_enabled = True
if s.get('server_port') in skip_ports or mode == 'udp_only':
continue
m = s['method']
if '2022' in m:
print(f'PORT_2022={s[\"server_port\"]}')
@@ -430,14 +473,8 @@ for s in c['servers']:
print(f'PORT_RAW={s[\"server_port\"]}')
print(f'KEY_RAW={s[\"password\"]}')
print(f'METHOD_RAW={m}')
print(f'ENABLE_UDP={1 if udp_enabled else 0}')
" 2>/dev/null)
if [[ -f /etc/shadowsocks-rust/obfs.env ]]; then
# shellcheck disable=SC1091
source /etc/shadowsocks-rust/obfs.env
if [[ -n "$OBFS_BACKEND_PORT" && "$PORT_2022" == "$OBFS_BACKEND_PORT" ]]; then
PORT_2022="" ; KEY_2022="" ; METHOD_2022=""
fi
fi
return 0
}
@@ -448,14 +485,19 @@ gen_subscribe() {
mkdir -p "$sub_dir"
local uris="" surge="" clash="" info_txt=""
local udp_clash="false" udp_label="关闭"
if [[ "${ENABLE_UDP:-0}" == "1" ]]; then
udp_clash="true"
udp_label="开启"
fi
if [[ -n "$PORT_2022" ]]; then
local uri="ss://$(echo -n "${METHOD_2022}:${KEY_2022}" | base64 -w0)@${SERVER_IP}:${PORT_2022}#SS2022-128"
URI_2022="$uri"
uris="${uris}${uri}\n"
surge="${surge}SS2022-128 = ss, ${SERVER_IP}, ${PORT_2022}, encrypt-method=${METHOD_2022}, password=${KEY_2022}\n"
clash="${clash} - name: SS2022-128\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_2022}\n cipher: ${METHOD_2022}\n password: \"${KEY_2022}\"\n\n"
info_txt="${info_txt}【SS2022-AES-128】新协议\n 地址: ${SERVER_IP}\n 端口: ${PORT_2022}\n 加密: ${METHOD_2022}\n 密码: ${KEY_2022}\n\n"
clash="${clash} - name: SS2022-128\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_2022}\n cipher: ${METHOD_2022}\n password: \"${KEY_2022}\"\n udp: ${udp_clash}\n\n"
info_txt="${info_txt}【SS2022-AES-128】新协议\n 地址: ${SERVER_IP}\n 端口: ${PORT_2022}\n 加密: ${METHOD_2022}\n 密码: ${KEY_2022}\n UDP: ${udp_label}\n\n"
fi
if [[ -n "$PORT_RAW" ]]; then
@@ -463,8 +505,8 @@ gen_subscribe() {
URI_RAW="$uri"
uris="${uris}${uri}\n"
surge="${surge}SS-AES-128 = ss, ${SERVER_IP}, ${PORT_RAW}, encrypt-method=${METHOD_RAW}, password=${KEY_RAW}\n"
clash="${clash} - name: SS-AES-128\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_RAW}\n cipher: ${METHOD_RAW}\n password: \"${KEY_RAW}\"\n\n"
info_txt="${info_txt}【SS-AES-128】传统协议\n 地址: ${SERVER_IP}\n 端口: ${PORT_RAW}\n 加密: ${METHOD_RAW}\n 密码: ${KEY_RAW}\n\n"
clash="${clash} - name: SS-AES-128\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_RAW}\n cipher: ${METHOD_RAW}\n password: \"${KEY_RAW}\"\n udp: ${udp_clash}\n\n"
info_txt="${info_txt}【SS-AES-128】传统协议\n 地址: ${SERVER_IP}\n 端口: ${PORT_RAW}\n 加密: ${METHOD_RAW}\n 密码: ${KEY_RAW}\n UDP: ${udp_label}\n\n"
fi
if [[ -n "$PORT_OBFS" ]]; then
@@ -475,8 +517,8 @@ gen_subscribe() {
URI_OBFS="$uri"
uris="${uris}${uri}\n"
surge="${surge}SS2022-OBFS-HTTP = ss, ${SERVER_IP}, ${PORT_OBFS}, encrypt-method=${METHOD_OBFS}, password=${KEY_OBFS}, obfs=http, obfs-host=${OBFS_HOST}\n"
clash="${clash} - name: SS2022-OBFS-HTTP\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_OBFS}\n cipher: ${METHOD_OBFS}\n password: \"${KEY_OBFS}\"\n udp: false\n plugin: obfs\n plugin-opts:\n mode: http\n host: ${OBFS_HOST}\n\n"
info_txt="${info_txt}【SS2022 + HTTP obfs】simple-obfs 伪装\n 地址: ${SERVER_IP}\n 端口: ${PORT_OBFS}\n 加密: ${METHOD_OBFS}\n 密码: ${KEY_OBFS}\n 插件: obfs/http\n Host: ${OBFS_HOST}\n 后端: 127.0.0.1:${OBFS_BACKEND_PORT}\n\n"
clash="${clash} - name: SS2022-OBFS-HTTP\n type: ss\n server: ${SERVER_IP}\n port: ${PORT_OBFS}\n cipher: ${METHOD_OBFS}\n password: \"${KEY_OBFS}\"\n udp: ${udp_clash}\n plugin: obfs\n plugin-opts:\n mode: http\n host: ${OBFS_HOST}\n\n"
info_txt="${info_txt}【SS2022 + HTTP obfs】simple-obfs 伪装\n 地址: ${SERVER_IP}\n 端口: ${PORT_OBFS}\n 加密: ${METHOD_OBFS}\n 密码: ${KEY_OBFS}\n 插件: obfs/http\n Host: ${OBFS_HOST}\n 后端: 127.0.0.1:${OBFS_BACKEND_PORT}\n UDP: ${udp_label}(公网原生 SS UDP\n\n"
fi
echo -e "$uris" | base64 -w0 > "$sub_dir/subscribe.txt"
@@ -489,6 +531,8 @@ gen_subscribe() {
# ============ 显示结果 ============
show_result() {
load_config || return
local udp_label="关闭"
[[ "${ENABLE_UDP:-0}" == "1" ]] && udp_label="开启"
echo ""
echo -e "${CYAN}════════════════════════════════════════${NC}"
echo -e "${CYAN} 🚀 Shadowsocks-Rust 安装完成${NC}"
@@ -501,6 +545,7 @@ show_result() {
echo -e " 端口: ${YELLOW}${PORT_2022}${NC}"
echo -e " 加密: ${METHOD_2022}"
echo -e " 密码: ${YELLOW}${KEY_2022}${NC}"
echo -e " UDP: ${udp_label}"
echo ""
fi
@@ -510,6 +555,7 @@ show_result() {
echo -e " 端口: ${YELLOW}${PORT_RAW}${NC}"
echo -e " 加密: ${METHOD_RAW}"
echo -e " 密码: ${YELLOW}${KEY_RAW}${NC}"
echo -e " UDP: ${udp_label}"
echo ""
fi
@@ -521,6 +567,7 @@ show_result() {
echo -e " 密码: ${YELLOW}${KEY_OBFS}${NC}"
echo -e " 插件: obfs / http"
echo -e " Host: ${OBFS_HOST}"
echo -e " UDP: ${udp_label}"
echo ""
fi
@@ -607,7 +654,7 @@ import json
with open('/etc/shadowsocks-rust/config.json') as f:
c = json.load(f)
for s in c.get('servers', []):
if str(s.get('server_port')) == '${OBFS_BACKEND_PORT}':
if str(s.get('server_port')) in ('${OBFS_BACKEND_PORT}', '${PORT_OBFS}'):
s['password'] = '${KEY_OBFS}'
with open('/etc/shadowsocks-rust/config.json','w') as f:
json.dump(c, f, indent=4)