This commit is contained in:
2026-09-17 02:03:25 +03:00
parent 5f1496e980
commit c87ba277c4
3 changed files with 56 additions and 23 deletions
+5 -2
View File
@@ -79,8 +79,11 @@
ct state established,related accept ct state established,related accept
# РЕЖЕМ SYN СРАЗУ # РЕЖЕМ SYN СРАЗУ
tcp flags syn tcp dport {80,443} limit rate 20/second burst 40 packets accept # 2096 — 3x-ui subscription endpoint (subPort): /subs/,
tcp flags syn tcp dport {80,443} drop # /subsjs/, /clash/ раздаёт сама панель, ссылки содержат
# этот порт, пока в x-ui.db не задан subURI без порта.
tcp flags syn tcp dport {80,443,2096} limit rate 20/second burst 40 packets accept
tcp flags syn tcp dport {80,443,2096} drop
# остальное по необходимости # остальное по необходимости
} }
+47 -21
View File
@@ -12,17 +12,19 @@
# so all existing services behave exactly as before. # so all existing services behave exactly as before.
# #
# Routing: # Routing:
# x.zeroq.su → 127.0.0.1:2049 (3x-ui controller panel; TLS is # x.zeroq.su → 127.0.0.1:8443 (nginx's own https listener:
# terminated inside the container by the mounted LE # TLS terminated by nginx, then path-routed:
# cert, exactly like pubray1.zeroq.su on the VDS) # "/" → panel web server 2049, "/subs/…" etc →
# immich/git/... → 127.0.0.1:8443 (nginx's own http {} listener: # panel subscription server 2096, so subscription
# TLS termination + proxyPass unchanged) # links can be plain https://x.zeroq.su/subs/<uuid>)
# immich/git/... → 127.0.0.1:8443 (nginx's own http {} listener)
# default → 127.0.0.1:15380 (Xray REALITY; podman DNATs # default → 127.0.0.1:15380 (Xray REALITY; podman DNATs
# host:15380 → container:443 so Xray sees its real # host:15380 → container:443 so Xray sees its real
# configured port 443) # configured port 443)
# #
# ssl_preread reads SNI from the ClientHello; every SNI matching a known # ssl_preread reads SNI from the ClientHello; every SNI matching a known
# vhost goes to the internal web listener, x.zeroq.su goes to the panel, # vhost goes to the internal web listener, x.zeroq.su goes to the panel
# (via the web listener so nginx can split /subs/ off the panel paths),
# and anything else (REALITY fronting domains, direct-IP) goes to Xray. # and anything else (REALITY fronting domains, direct-IP) goes to Xray.
let let
server = "192.168.1.20"; server = "192.168.1.20";
@@ -127,22 +129,19 @@ let
]; ];
# Goes inside the auto-generated `stream {}` block (services.nginx.streamConfig). # Goes inside the auto-generated `stream {}` block (services.nginx.streamConfig).
# x.zeroq.su → 3x-ui panel; known vhosts → nginx's own https listener; # x.zeroq.su → nginx's own https listener (8443), where nginx path-routes
# everything else (any SNI a REALITY client uses, e.g. # /subs/... → panel subscription server 2096 and / → panel web 2049;
# media.mediavitrina.ru, or direct-IP) → Xray. # known vhosts → web listener; everything else (any SNI a REALITY client
# uses, e.g. media.mediavitrina.ru, or direct-IP) → Xray.
streamConfig = '' streamConfig = ''
ssl_preread on; ssl_preread on;
map $ssl_preread_server_name $sni_backend { map $ssl_preread_server_name $sni_backend {
default xray; default xray;
${panelDomain} panel; ${panelDomain} web;
${"\n" + lib.concatMapStringsSep "\n" (d: " ${d} web;") (extraWebDomains ++ (map (s: s.domain) sites))} ${"\n" + lib.concatMapStringsSep "\n" (d: " ${d} web;") (extraWebDomains ++ (map (s: s.domain) sites))}
} }
upstream panel {
server 127.0.0.1:2049;
}
upstream web { upstream web {
server 127.0.0.1:8443; server 127.0.0.1:8443;
} }
@@ -216,14 +215,41 @@ in
extraConfig = bigUploads; extraConfig = bigUploads;
}; };
"x.zeroq.su" = { "x.zeroq.su" = {
# ACME only — no https listener here: 443 is owned by the stream # Panel domain. TLS is terminated HERE by nginx (stream routes this
# block, which routes the x.zeroq.su SNI to the panel inside the # SNI to the web listener), and paths are split:
# container (127.0.0.1:2049). 3x-ui terminates TLS itself using # /subs/, /subsjs/, /clash/, /sub/ → panel subscription server
# the cert this vhost renews (mounted at /root/cert/), and serves # (127.0.0.1:2096, TLS inside the container) so links like
# /subs/, /subsjs/, /clash/ straight from the panel, like the VDS. # https://x.zeroq.su/subs/<uuid> work without :2096;
# Don't add forceSSL: it would generate an HTTPS server block that # everything else → panel web server
# conflicts with the stream listener. # (127.0.0.1:2049, TLS inside the container).
# The panel serves both on TLS with the mounted LE cert.
enableACME = true; enableACME = true;
forceSSL = true;
listen = webListen;
locations = {
"/subs/" = {
proxyPass = "https://127.0.0.1:2096";
proxyWebsockets = true;
extraConfig = "proxy_ssl_verify off;";
};
"/subsjs/" = {
proxyPass = "https://127.0.0.1:2096";
extraConfig = "proxy_ssl_verify off;";
};
"/clash/" = {
proxyPass = "https://127.0.0.1:2096";
extraConfig = "proxy_ssl_verify off;";
};
"/sub/" = {
proxyPass = "https://127.0.0.1:2096";
extraConfig = "proxy_ssl_verify off;";
};
"/" = {
proxyPass = "https://127.0.0.1:2049";
proxyWebsockets = true;
extraConfig = "proxy_ssl_verify off;";
};
};
}; };
"zeroq.su" = { "zeroq.su" = {
forceSSL = true; forceSSL = true;
+4
View File
@@ -71,5 +71,9 @@ in
networking.firewall.allowedTCPPorts = [ networking.firewall.allowedTCPPorts = [
80 80
443 443
# 3x-ui subscription endpoint (subPort): /subs/, /subsjs/, /clash/
# раздаёт сама панель; порт должен быть открыт, пока ссылки подписки
# содержат :2096 (см. также nftables-ruleset в configurations/vds.nix).
2096
]; ];
} }