mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-07 20:47:14 +03:00
Compare commits
2
Commits
dev
...
c87ba277c4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c87ba277c4 | ||
|
|
5f1496e980 |
@@ -79,8 +79,11 @@
|
||||
ct state established,related accept
|
||||
|
||||
# РЕЖЕМ SYN СРАЗУ
|
||||
tcp flags syn tcp dport {80,443} limit rate 20/second burst 40 packets accept
|
||||
tcp flags syn tcp dport {80,443} drop
|
||||
# 2096 — 3x-ui subscription endpoint (subPort): /subs/,
|
||||
# /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
|
||||
|
||||
# остальное по необходимости
|
||||
}
|
||||
|
||||
@@ -44,12 +44,14 @@
|
||||
# ./trilium.nix
|
||||
# ./zerotier.nix
|
||||
];
|
||||
# Server's 3x-ui is the controller panel at x.zeroq.su (nginx HTTP
|
||||
# terminates TLS upstream, no SNI-routing on 443 needed here because
|
||||
# there are other vhosts on the same port). Cert is still mounted in
|
||||
# case 3x-ui is later reconfigured to terminate TLS itself (e.g. for
|
||||
# direct node-API access); nginx doesn't have to use it.
|
||||
xlib.services."3x-ui".certDomain = "x.zeroq.su";
|
||||
# Server's 3x-ui is the controller panel at x.zeroq.su, wired the same
|
||||
# way as the VDS node: nginx stream SNI-routes x.zeroq.su → container:2049
|
||||
# (3x-ui terminates TLS itself with the mounted LE cert), and the default
|
||||
# SNI → Xray REALITY via host:15380 → container:443 (see nginx.nix).
|
||||
xlib.services."3x-ui" = {
|
||||
certDomain = "x.zeroq.su";
|
||||
reality443Forwarding = true;
|
||||
};
|
||||
systemd.tmpfiles.rules = [
|
||||
(xlib.helpers.mkTmpfile "d" "/mnt" "0755" "root" "root")
|
||||
(xlib.helpers.mkTmpfile "d" xlib.dirs.services-mnt-folder "0755" "root" "root")
|
||||
|
||||
+110
-13
@@ -5,12 +5,46 @@
|
||||
xlib,
|
||||
...
|
||||
}:
|
||||
# Standard reverse-proxy: HTTP/S termination upstream, backend on the LAN.
|
||||
# x.zeroq.su is the 3x-ui controller panel — see 3x-ui.nix for the
|
||||
# /subs/, /subsjs/, /clash/ routing logic.
|
||||
# SNI-fronted reverse proxy (vds/nginx.nix style): port 443 is owned by an
|
||||
# nginx stream block that reads the ClientHello SNI and forwards the raw
|
||||
# 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:8443 (nginx's own https listener:
|
||||
# TLS terminated by nginx, then path-routed:
|
||||
# "/" → panel web server 2049, "/subs/…" etc →
|
||||
# panel subscription server 2096, so subscription
|
||||
# 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
|
||||
# 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
|
||||
# (via the web listener so nginx can split /subs/ off the panel paths),
|
||||
# and anything else (REALITY fronting domains, direct-IP) goes to Xray.
|
||||
let
|
||||
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 =
|
||||
{
|
||||
domain,
|
||||
@@ -22,6 +56,7 @@ let
|
||||
name = domain;
|
||||
value = {
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${server}:${toString port}";
|
||||
proxyWebsockets = true;
|
||||
@@ -82,6 +117,46 @@ let
|
||||
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 → nginx's own https listener (8443), where nginx path-routes
|
||||
# /subs/... → panel subscription server 2096 and / → panel web 2049;
|
||||
# known vhosts → web 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} web;
|
||||
${"\n" + lib.concatMapStringsSep "\n" (d: " ${d} web;") (extraWebDomains ++ (map (s: s.domain) sites))}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
services.nginx = {
|
||||
@@ -90,6 +165,8 @@ in
|
||||
recommendedOptimisation = true;
|
||||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
# Lands inside the auto-generated `stream {}` block.
|
||||
streamConfig = streamConfig;
|
||||
virtualHosts = (builtins.listToAttrs (map mkProxy sites)) // {
|
||||
"nextcloud.private" = {
|
||||
forceSSL = false;
|
||||
@@ -112,6 +189,7 @@ in
|
||||
"office.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
};
|
||||
"pdf.private" = {
|
||||
forceSSL = false;
|
||||
@@ -137,30 +215,46 @@ in
|
||||
extraConfig = bigUploads;
|
||||
};
|
||||
"x.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
# Panel domain. TLS is terminated HERE by nginx (stream routes this
|
||||
# SNI to the web listener), and paths are split:
|
||||
# /subs/, /subsjs/, /clash/, /sub/ → panel subscription server
|
||||
# (127.0.0.1:2096, TLS inside the container) so links like
|
||||
# https://x.zeroq.su/subs/<uuid> work without :2096;
|
||||
# everything else → panel web server
|
||||
# (127.0.0.1:2049, TLS inside the container).
|
||||
# The panel serves both on TLS with the mounted LE cert.
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
listen = webListen;
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://${server}:2049";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
"/subs/" = {
|
||||
proxyPass = "http://${server}:2096";
|
||||
proxyPass = "https://127.0.0.1:2096";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = "proxy_ssl_verify off;";
|
||||
};
|
||||
"/subsjs/" = {
|
||||
proxyPass = "http://${server}:2096";
|
||||
proxyWebsockets = true;
|
||||
proxyPass = "https://127.0.0.1:2096";
|
||||
extraConfig = "proxy_ssl_verify off;";
|
||||
};
|
||||
"/clash/" = {
|
||||
proxyPass = "http://${server}:2096";
|
||||
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" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
root = pkgs.writeTextDir "index.html" ''
|
||||
<!doctype html>
|
||||
<html>
|
||||
@@ -177,6 +271,7 @@ in
|
||||
"vetymae.opencodes.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
locations."/" = {
|
||||
proxyPass = "http://100.86.62.4:4096";
|
||||
proxyWebsockets = true;
|
||||
@@ -185,6 +280,7 @@ in
|
||||
"lamet.opencodes.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
locations."/" = {
|
||||
proxyPass = "http://100.106.21.39:6061";
|
||||
proxyWebsockets = true;
|
||||
@@ -193,6 +289,7 @@ in
|
||||
"nextcloud.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
listen = webListen;
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://${server}:10000";
|
||||
@@ -211,4 +308,4 @@ in
|
||||
80
|
||||
443
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -71,5 +71,9 @@ in
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
# 3x-ui subscription endpoint (subPort): /subs/, /subsjs/, /clash/
|
||||
# раздаёт сама панель; порт должен быть открыт, пока ссылки подписки
|
||||
# содержат :2096 (см. также nftables-ruleset в configurations/vds.nix).
|
||||
2096
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user