feat: add optional UDP relay support
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
一键安装 shadowsocks-rust,自动配置 ss2022 双节点,生成订阅链接 + Surge/Clash 配置。
|
||||
|
||||
安装时可选择是否启用 UDP(默认启用)。普通节点会使用 TCP+UDP;HTTP 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 管理,开机自启
|
||||
|
||||
## 输出文件
|
||||
|
||||
85
ss-rust.sh
85
ss-rust.sh
@@ -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,17 +317,30 @@ 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)
|
||||
print('OK')
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user