mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-07 20:47:14 +03:00
311 lines
8.3 KiB
Nix
311 lines
8.3 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
xlib,
|
|
...
|
|
}:
|
|
# 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,
|
|
port,
|
|
addSSL ? false,
|
|
extraConfig ? "",
|
|
}:
|
|
{
|
|
name = domain;
|
|
value = {
|
|
enableACME = true;
|
|
listen = webListen;
|
|
locations."/" = {
|
|
proxyPass = "http://${server}:${toString port}";
|
|
proxyWebsockets = true;
|
|
};
|
|
}
|
|
// lib.optionalAttrs (!addSSL) { forceSSL = true; }
|
|
// lib.optionalAttrs addSSL { addSSL = true; }
|
|
// lib.optionalAttrs (extraConfig != "") { inherit extraConfig; };
|
|
};
|
|
|
|
bigUploads = "client_max_body_size 5G;";
|
|
|
|
sites = [
|
|
{
|
|
domain = "immich.zeroq.su";
|
|
port = 2283;
|
|
addSSL = true;
|
|
extraConfig = bigUploads;
|
|
}
|
|
{
|
|
domain = "kuma.zeroq.su";
|
|
port = 4001;
|
|
}
|
|
{
|
|
domain = "health.zeroq.su";
|
|
port = 19999;
|
|
}
|
|
{
|
|
domain = "git.zeroq.su";
|
|
port = 3000;
|
|
}
|
|
{
|
|
domain = "homebox.zeroq.su";
|
|
port = 7745;
|
|
}
|
|
{
|
|
domain = "flux.zeroq.su";
|
|
port = 6061;
|
|
}
|
|
{
|
|
domain = "navidrome.zeroq.su";
|
|
port = 4533;
|
|
addSSL = true;
|
|
}
|
|
{
|
|
domain = "calibre.zeroq.su";
|
|
port = 8083;
|
|
extraConfig = bigUploads;
|
|
}
|
|
{
|
|
domain = "nix-cache.zeroq.su";
|
|
port = 5000;
|
|
extraConfig = bigUploads;
|
|
}
|
|
{
|
|
domain = "pdf.zeroq.su";
|
|
port = 8446;
|
|
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 = {
|
|
enable = true;
|
|
recommendedGzipSettings = true;
|
|
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;
|
|
enableACME = false;
|
|
listen = [
|
|
{
|
|
addr = "100.64.0.0";
|
|
port = 10000;
|
|
}
|
|
{
|
|
addr = "192.168.1.20";
|
|
port = 10000;
|
|
}
|
|
{
|
|
addr = "127.0.0.1";
|
|
port = 10000;
|
|
}
|
|
];
|
|
};
|
|
"office.zeroq.su" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
listen = webListen;
|
|
};
|
|
"pdf.private" = {
|
|
forceSSL = false;
|
|
enableACME = false;
|
|
listen = [
|
|
{
|
|
addr = "0.0.0.0";
|
|
port = 80;
|
|
}
|
|
{
|
|
addr = "100.64.0.0";
|
|
port = 8446;
|
|
}
|
|
{
|
|
addr = "192.168.1.20";
|
|
port = 8446;
|
|
}
|
|
{
|
|
addr = "127.0.0.1";
|
|
port = 8446;
|
|
}
|
|
];
|
|
extraConfig = bigUploads;
|
|
};
|
|
"x.zeroq.su" = {
|
|
# 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 = {
|
|
"/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" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
listen = webListen;
|
|
root = pkgs.writeTextDir "index.html" ''
|
|
<!doctype html>
|
|
<html>
|
|
<body>
|
|
<pre>What are you doing here?</pre>
|
|
</body>
|
|
</html>
|
|
'';
|
|
locations."/guest/" = {
|
|
proxyPass = "http://${server}:80";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
"vetymae.opencodes.zeroq.su" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
listen = webListen;
|
|
locations."/" = {
|
|
proxyPass = "http://100.86.62.4:4096";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
"lamet.opencodes.zeroq.su" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
listen = webListen;
|
|
locations."/" = {
|
|
proxyPass = "http://100.106.21.39:6061";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
"nextcloud.zeroq.su" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
listen = webListen;
|
|
locations = {
|
|
"/" = {
|
|
proxyPass = "http://${server}:10000";
|
|
proxyWebsockets = true;
|
|
};
|
|
"/whiteboard" = {
|
|
proxyPass = "http://${server}:3002";
|
|
proxyWebsockets = true;
|
|
};
|
|
};
|
|
extraConfig = bigUploads;
|
|
};
|
|
};
|
|
};
|
|
networking.firewall.allowedTCPPorts = [
|
|
80
|
|
443
|
|
];
|
|
} |