mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-07 20:47:14 +03:00
3x-ui server mask
This commit is contained in:
@@ -44,12 +44,14 @@
|
|||||||
# ./trilium.nix
|
# ./trilium.nix
|
||||||
# ./zerotier.nix
|
# ./zerotier.nix
|
||||||
];
|
];
|
||||||
# Server's 3x-ui is the controller panel at x.zeroq.su (nginx HTTP
|
# Server's 3x-ui is the controller panel at x.zeroq.su, wired the same
|
||||||
# terminates TLS upstream, no SNI-routing on 443 needed here because
|
# way as the VDS node: nginx stream SNI-routes x.zeroq.su → container:2049
|
||||||
# there are other vhosts on the same port). Cert is still mounted in
|
# (3x-ui terminates TLS itself with the mounted LE cert), and the default
|
||||||
# case 3x-ui is later reconfigured to terminate TLS itself (e.g. for
|
# SNI → Xray REALITY via host:15380 → container:443 (see nginx.nix).
|
||||||
# direct node-API access); nginx doesn't have to use it.
|
xlib.services."3x-ui" = {
|
||||||
xlib.services."3x-ui".certDomain = "x.zeroq.su";
|
certDomain = "x.zeroq.su";
|
||||||
|
reality443Forwarding = true;
|
||||||
|
};
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
(xlib.helpers.mkTmpfile "d" "/mnt" "0755" "root" "root")
|
(xlib.helpers.mkTmpfile "d" "/mnt" "0755" "root" "root")
|
||||||
(xlib.helpers.mkTmpfile "d" xlib.dirs.services-mnt-folder "0755" "root" "root")
|
(xlib.helpers.mkTmpfile "d" xlib.dirs.services-mnt-folder "0755" "root" "root")
|
||||||
|
|||||||
+94
-23
@@ -5,12 +5,44 @@
|
|||||||
xlib,
|
xlib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Standard reverse-proxy: HTTP/S termination upstream, backend on the LAN.
|
# SNI-fronted reverse proxy (vds/nginx.nix style): port 443 is owned by an
|
||||||
# x.zeroq.su is the 3x-ui controller panel — see 3x-ui.nix for the
|
# nginx stream block that reads the ClientHello SNI and forwards the raw
|
||||||
# /subs/, /subsjs/, /clash/ routing logic.
|
# TLS stream. The HTTP vhosts no longer bind 443 — they listen on an
|
||||||
|
# internal HTTPS listener (127.0.0.1:8443) that the stream forwards onto,
|
||||||
|
# so all existing services behave exactly as before.
|
||||||
|
#
|
||||||
|
# Routing:
|
||||||
|
# x.zeroq.su → 127.0.0.1:2049 (3x-ui controller panel; TLS is
|
||||||
|
# terminated inside the container by the mounted LE
|
||||||
|
# cert, exactly like pubray1.zeroq.su on the VDS)
|
||||||
|
# immich/git/... → 127.0.0.1:8443 (nginx's own http {} listener:
|
||||||
|
# TLS termination + proxyPass unchanged)
|
||||||
|
# default → 127.0.0.1:15380 (Xray REALITY; podman DNATs
|
||||||
|
# host:15380 → container:443 so Xray sees its real
|
||||||
|
# configured port 443)
|
||||||
|
#
|
||||||
|
# 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,
|
||||||
|
# and anything else (REALITY fronting domains, direct-IP) goes to Xray.
|
||||||
let
|
let
|
||||||
server = "192.168.1.20";
|
server = "192.168.1.20";
|
||||||
|
|
||||||
|
panelDomain = "x.zeroq.su";
|
||||||
|
# Replaces the default 443 binding for every public vhost: 443 now
|
||||||
|
# belongs to the stream block, the internal https listener hosts the
|
||||||
|
# real server blocks. Port 80 stays public for ACME http-01 + redirects.
|
||||||
|
webListen = [
|
||||||
|
{
|
||||||
|
addr = "0.0.0.0";
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
addr = "127.0.0.1";
|
||||||
|
port = 8443;
|
||||||
|
ssl = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
mkProxy =
|
mkProxy =
|
||||||
{
|
{
|
||||||
domain,
|
domain,
|
||||||
@@ -22,6 +54,7 @@ let
|
|||||||
name = domain;
|
name = domain;
|
||||||
value = {
|
value = {
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://${server}:${toString port}";
|
proxyPass = "http://${server}:${toString port}";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
@@ -82,6 +115,49 @@ let
|
|||||||
extraConfig = bigUploads;
|
extraConfig = bigUploads;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Hardcoded vhosts served by the internal web listener (next to `sites`).
|
||||||
|
# Every one of them must appear in the SNI map → web.
|
||||||
|
extraWebDomains = [
|
||||||
|
"office.zeroq.su"
|
||||||
|
"zeroq.su"
|
||||||
|
"vetymae.opencodes.zeroq.su"
|
||||||
|
"lamet.opencodes.zeroq.su"
|
||||||
|
"nextcloud.zeroq.su"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Goes inside the auto-generated `stream {}` block (services.nginx.streamConfig).
|
||||||
|
# x.zeroq.su → 3x-ui panel; known vhosts → nginx's own https listener;
|
||||||
|
# everything else (any SNI a REALITY client uses, e.g.
|
||||||
|
# media.mediavitrina.ru, or direct-IP) → Xray.
|
||||||
|
streamConfig = ''
|
||||||
|
ssl_preread on;
|
||||||
|
|
||||||
|
map $ssl_preread_server_name $sni_backend {
|
||||||
|
default xray;
|
||||||
|
${panelDomain} panel;
|
||||||
|
${"\n" + lib.concatMapStringsSep "\n" (d: " ${d} web;") (extraWebDomains ++ (map (s: s.domain) sites))}
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream panel {
|
||||||
|
server 127.0.0.1:2049;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream web {
|
||||||
|
server 127.0.0.1:8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream xray {
|
||||||
|
server 127.0.0.1:15380;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443;
|
||||||
|
proxy_pass $sni_backend;
|
||||||
|
proxy_timeout 600s;
|
||||||
|
proxy_connect_timeout 5s;
|
||||||
|
}
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
services.nginx = {
|
services.nginx = {
|
||||||
@@ -90,6 +166,8 @@ in
|
|||||||
recommendedOptimisation = true;
|
recommendedOptimisation = true;
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
|
# Lands inside the auto-generated `stream {}` block.
|
||||||
|
streamConfig = streamConfig;
|
||||||
virtualHosts = (builtins.listToAttrs (map mkProxy sites)) // {
|
virtualHosts = (builtins.listToAttrs (map mkProxy sites)) // {
|
||||||
"nextcloud.private" = {
|
"nextcloud.private" = {
|
||||||
forceSSL = false;
|
forceSSL = false;
|
||||||
@@ -112,6 +190,7 @@ in
|
|||||||
"office.zeroq.su" = {
|
"office.zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
};
|
};
|
||||||
"pdf.private" = {
|
"pdf.private" = {
|
||||||
forceSSL = false;
|
forceSSL = false;
|
||||||
@@ -137,30 +216,19 @@ in
|
|||||||
extraConfig = bigUploads;
|
extraConfig = bigUploads;
|
||||||
};
|
};
|
||||||
"x.zeroq.su" = {
|
"x.zeroq.su" = {
|
||||||
forceSSL = true;
|
# ACME only — no https listener here: 443 is owned by the stream
|
||||||
|
# block, which routes the x.zeroq.su SNI to the panel inside the
|
||||||
|
# container (127.0.0.1:2049). 3x-ui terminates TLS itself using
|
||||||
|
# the cert this vhost renews (mounted at /root/cert/), and serves
|
||||||
|
# /subs/, /subsjs/, /clash/ straight from the panel, like the VDS.
|
||||||
|
# Don't add forceSSL: it would generate an HTTPS server block that
|
||||||
|
# conflicts with the stream listener.
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
locations = {
|
|
||||||
"/" = {
|
|
||||||
proxyPass = "http://${server}:2049";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
"/subs/" = {
|
|
||||||
proxyPass = "http://${server}:2096";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
"/subsjs/" = {
|
|
||||||
proxyPass = "http://${server}:2096";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
"/clash/" = {
|
|
||||||
proxyPass = "http://${server}:2096";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
"zeroq.su" = {
|
"zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
root = pkgs.writeTextDir "index.html" ''
|
root = pkgs.writeTextDir "index.html" ''
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
@@ -177,6 +245,7 @@ in
|
|||||||
"vetymae.opencodes.zeroq.su" = {
|
"vetymae.opencodes.zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://100.86.62.4:4096";
|
proxyPass = "http://100.86.62.4:4096";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
@@ -185,6 +254,7 @@ in
|
|||||||
"lamet.opencodes.zeroq.su" = {
|
"lamet.opencodes.zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://100.106.21.39:6061";
|
proxyPass = "http://100.106.21.39:6061";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
@@ -193,6 +263,7 @@ in
|
|||||||
"nextcloud.zeroq.su" = {
|
"nextcloud.zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
listen = webListen;
|
||||||
locations = {
|
locations = {
|
||||||
"/" = {
|
"/" = {
|
||||||
proxyPass = "http://${server}:10000";
|
proxyPass = "http://${server}:10000";
|
||||||
@@ -211,4 +282,4 @@ in
|
|||||||
80
|
80
|
||||||
443
|
443
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user