全节点(jp/us/ru/aq/hk2)已部署并实测通过(UoT DNS 端到端 ✅ + TCP 回归 ✅): - nginx 模板:map $request 识别 sing-box UoT v2 magic 地址 (sp.v2.udp-over-tcp.arpa)→ 127.0.0.1:15201;default_server 块加 proxy_connect_address;read/send timeout 10m - 新增 uot-relay/:Python 标准库 UoT v2 relay(connect/non-connect 双模式, 仅监听 127.0.0.1,空闲 9 分钟断开) - README 5.5 节:部署方法 + 客户端协议 + 四个实测坑 ($connect_host 未注册变量 / data_timeout 与 read_timeout 同槽 / CONNECT 恒落 default_server / 拒绝端口 0)
167 lines
6.3 KiB
Nginx Configuration File
167 lines
6.3 KiB
Nginx Configuration File
worker_processes auto;
|
||
worker_rlimit_nofile 65535;
|
||
events {
|
||
worker_connections 8192;
|
||
multi_accept on;
|
||
}
|
||
|
||
http {
|
||
server_tokens off;
|
||
resolver 8.8.8.8 ipv6=off valid=300s;
|
||
resolver_timeout 10s;
|
||
# 默认站点分流:本机/私网直连返回伪装页;其余 Host(正向代理目标)走 /__proxy 转发
|
||
# 公网 IP 无需手改:cron(sync-nginx-local-ips.sh)自动写入 local_ip_rules.conf;
|
||
# 另有 /__proxy 自代理环保护兜底,即使公网 IP 变更也不会死循环。
|
||
map $host $is_local {
|
||
default 0;
|
||
"~^(10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)" 1;
|
||
}
|
||
|
||
# UoT v2(UDP over TCP):CONNECT 目标为 sing-box UoT v2 magic 地址
|
||
# (sp.v2.udp-over-tcp.arpa)时转发到本机 UoT relay(127.0.0.1:15201),
|
||
# 其余 CONNECT 走默认解析($connect_addr)。
|
||
# 注意(实测结论,勿踩坑):
|
||
# 1. 必须用 $request 匹配——此模块版本未注册 $connect_host 变量;
|
||
# 2. proxy_connect_address 必须放在 default_server 块——nginx 按 Host 头
|
||
# 选虚拟主机,CONNECT 的 Host 是目标域名,永远落在 default_server;
|
||
# 3. 客户端 CONNECT magic 地址时端口必须非 0(模块拒绝 port<1,返回 400)。
|
||
map $request $rrpp_uot_connect_address {
|
||
default $connect_addr;
|
||
"~^CONNECT sp\.v2\.udp-over-tcp\.arpa" 127.0.0.1:15201;
|
||
}
|
||
include mime.types;
|
||
default_type application/octet-stream;
|
||
sendfile on;
|
||
tcp_nopush on;
|
||
tcp_nodelay on;
|
||
keepalive_timeout 65;
|
||
keepalive_requests 100;
|
||
|
||
# <nginx端口>:主域名 HTTPS 站点 + 认证 CONNECT 代理
|
||
server {
|
||
listen <nginx端口> ssl;
|
||
http2 on;
|
||
server_name <节点域名>;
|
||
root /var/www/html;
|
||
|
||
ssl_certificate /etc/nginx/ssl/<节点域名>/fullchain.pem;
|
||
ssl_certificate_key /etc/nginx/ssl/<节点域名>/privkey.pem;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||
ssl_session_cache shared:SSL:10m;
|
||
ssl_session_timeout 10m;
|
||
|
||
# 仅 CONNECT/正向代理需要认证,普通页面不需要
|
||
auth_basic "Restricted";
|
||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||
|
||
proxy_connect;
|
||
proxy_connect_allow all;
|
||
proxy_connect_address $rrpp_uot_connect_address;
|
||
proxy_connect_connect_timeout 60s;
|
||
# 注:此模块版本 proxy_connect_data_timeout 与 proxy_connect_read_timeout
|
||
# 写同一字段(同时配置报 duplicate),长连接超时用 read/send timeout 设置
|
||
proxy_connect_read_timeout 10m;
|
||
proxy_connect_send_timeout 10m;
|
||
|
||
location / {
|
||
auth_basic off;
|
||
if ($host != "<节点域名>") {
|
||
rewrite ^ /__proxy last;
|
||
}
|
||
index index.html;
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
|
||
location /__proxy {
|
||
internal;
|
||
proxy_intercept_errors on;
|
||
error_page 502 503 504 =444 @drop;
|
||
resolver 8.8.8.8 ipv6=off valid=300s;
|
||
proxy_pass $scheme://$host$request_uri;
|
||
proxy_set_header Host $host;
|
||
proxy_ssl_name $host;
|
||
proxy_ssl_server_name on;
|
||
}
|
||
|
||
location @drop {
|
||
return 444;
|
||
}
|
||
}
|
||
|
||
# <nginx端口> fallback:其他 SNI 也可用 CONNECT 代理;普通 GET 只返回伪装页
|
||
server {
|
||
listen <nginx端口> ssl default_server;
|
||
http2 on;
|
||
server_name _;
|
||
|
||
ssl_certificate /etc/nginx/ssl/<节点域名>/fullchain.pem;
|
||
ssl_certificate_key /etc/nginx/ssl/<节点域名>/privkey.pem;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||
ssl_session_cache shared:SSL:10m;
|
||
ssl_session_timeout 10m;
|
||
|
||
# 仅 CONNECT/正向代理需要认证,普通页面不需要
|
||
auth_basic "Restricted";
|
||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||
|
||
proxy_connect;
|
||
proxy_connect_allow all;
|
||
proxy_connect_address $rrpp_uot_connect_address;
|
||
proxy_connect_connect_timeout 60s;
|
||
# 注:此模块版本 proxy_connect_data_timeout 与 proxy_connect_read_timeout
|
||
# 写同一字段(同时配置报 duplicate),长连接超时用 read/send timeout 设置
|
||
proxy_connect_read_timeout 10m;
|
||
proxy_connect_send_timeout 10m;
|
||
|
||
location / {
|
||
auth_basic off;
|
||
# 自动判定本机:连接本地地址 / 无 Host / 私网回环 / cron 维护的公网 IP 规则
|
||
# 命中则 rewrite 到 /__local<原路径>(保留 $uri),其余走 /__proxy 转发
|
||
if ($host = $server_addr) { rewrite ^(.*)$ /__local$1 last; }
|
||
if ($host = "_") { rewrite ^(.*)$ /__local$1 last; }
|
||
if ($is_local) { rewrite ^(.*)$ /__local$1 last; }
|
||
include /etc/nginx/local_ip_rules.conf;
|
||
rewrite ^ /__proxy last;
|
||
}
|
||
|
||
# 本机 IP/域名直连:剥掉前缀后按原路径服务伪装页(保持原有行为)
|
||
location /__local {
|
||
internal;
|
||
auth_basic off;
|
||
rewrite ^/__local(.*)$ $1 break;
|
||
root /var/www/html;
|
||
index index.html;
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
|
||
# 明文 HTTP 正向代理:绝对形式请求(Host=目标域名)在此转发
|
||
location /__proxy {
|
||
internal;
|
||
# 自代理环保护:转发请求带标记头,二次进入说明代理目标指向本机(未知/新公网 IP),
|
||
# 返回 508 Loop Detected 而非死循环
|
||
if ($http_x_proxy_loop_guard = "1") {
|
||
return 508;
|
||
}
|
||
proxy_set_header X-Proxy-Loop-Guard "1";
|
||
resolver 8.8.8.8 ipv6=off valid=300s;
|
||
proxy_pass http://$http_host$request_uri;
|
||
proxy_set_header Host $http_host;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
proxy_connect_timeout 10s;
|
||
proxy_read_timeout 120s;
|
||
proxy_intercept_errors on;
|
||
# 目标不可达/无法解析:直接断开连接(444),不返回任何页面
|
||
error_page 502 503 504 =444 @drop;
|
||
}
|
||
|
||
location @drop {
|
||
return 444;
|
||
}
|
||
}
|
||
|
||
include /etc/nginx/conf.d/*.conf;
|
||
}
|