Files
Maven/deploy/ecosystem/nginx.conf
kami 0e83ddf3df deploy: stop mavweb becoming the default nginx server by file order
The maven block was first in nginx.conf, and nginx serves the first block for
a listen address when no server_name matches. Those two ports used to default
to nexus. After the maven block landed, a request with an unknown or absent
Host header reached mavweb instead, which is the one surface in the file that
can define and run argv. The ACL still held, so this was not an exposure, but
it is the wrong default to acquire by accident.

The nexus block is now marked default_server so the choice is explicit, and
the maven block moved last as a second guard. Also raises client_body_timeout
and proxy_send_timeout to match client_max_body_size 32m, since a slow
push-to-talk upload was cut at the 60s default on both while
proxy_read_timeout was already 300s.

Found in review of #52.
2026-08-01 13:56:44 +04:00

146 lines
5.9 KiB
Nginx Configuration File

# Reverse-proxy Maven's own web UI plus the three sibling admin UIs. Drop into
# your nginx sites (or the nginx-panel app) and reload. Assumes the compose
# publishes each service on 127.0.0.1:<port>. Add TLS (certbot / your existing
# cert block) per server.
#
# NOTE: hexis.<domain> previously pointed at the MCP tool — repoint that
# elsewhere first (the app now owns hexis.*).
#
# 10.42.0.1 and 192.168.1.104 below are THIS BOX's WireGuard and LAN
# addresses (homesrv) — these admin UIs have no auth of their own, so the
# explicit bind + allow/deny below is what keeps them off the open internet.
# On a different box, replace both addresses with that box's wg and LAN IPs.
# Do NOT "fix" a failed bind by reverting to `listen 80` (all interfaces) —
# that removes the only access control these containers have.
#
# BEFORE YOU RELOAD — two ways this file takes nginx down, both host-side.
# These blocks are for the HOST nginx, not for anything inside the compose;
# the containers publish on 127.0.0.1 and have no nginx of their own.
#
# 1. Binding an address that does not exist yet. `listen 10.42.0.1:80` fails
# with EADDRNOTAVAIL if wg0 is down, and nginx exits rather than starting
# without it — so a reboot that brings nginx up before WireGuard leaves the
# box with no web at all. Allow the bind to succeed regardless:
# sysctl -w net.ipv4.ip_nonlocal_bind=1
# echo 'net.ipv4.ip_nonlocal_bind = 1' > /etc/sysctl.d/99-nginx-bind.conf
# Ordering nginx after the wg interface works too, but only until the next
# time the tunnel restarts.
#
# 2. A duplicate $connection_upgrade map. That is a fatal config error, so the
# map now lives in nginx-upgrade-map.conf and is installed separately —
# read the note at the top of that file first.
#
# `nginx -t` catches the second and not the first. Run it anyway, every time.
#
# BLOCK ORDER IS LOAD-BEARING. nginx serves the first block for a given listen
# address when no server_name matches, so whichever block comes first here
# answers requests with an unknown or absent Host header. That must not be
# mavweb: it is the one surface in this file that can define and run argv. The
# nexus block is marked default_server so the choice is explicit rather than a
# consequence of file order, and the maven block sits last as a second guard.
# If you add a block, do not put it above nexus. If another site file already
# claims default_server on 10.42.0.1:80 or 192.168.1.104:80, nginx refuses to
# start with "a duplicate default server" — drop the two keywords here and rely
# on the block order instead.
#
# Every server_name below is a literal for kvmx.ru even though the comments
# write maven.<domain>. This file reads like a template and is not one.
server {
listen 10.42.0.1:80 default_server;
listen 192.168.1.104:80 default_server;
server_name nexus.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:9740;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name praxis.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:8989;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name hexis.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://127.0.0.1:9741;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# maven.<domain> → mavweb (docker-compose.yml publishes it on 127.0.0.1:9201).
# Same bind + ACL as the siblings, and for a stronger reason: mavweb serves
# POST /tools, which defines argv that internal/tool EXECUTES, plus POST
# /routines, /api/revert, /api/chat, /api/ptt and /ws (Vikunja #317). Without
# -webauthn-origin/-webauthn-rpid mavweb has no auth of its own, so this block
# is the auth. If you add TLS and a basic-auth/oauth2-proxy layer, keep the
# allow/deny anyway — belt and braces on an RCE surface.
#
# WebSocket upgrade matters here: /ws carries push-to-talk audio, so the
# Upgrade/Connection headers below are required, not decoration. They reference
# $connection_upgrade, which this file does NOT define — see
# nginx-upgrade-map.conf and point 2 above.
server {
listen 10.42.0.1:80;
listen 192.168.1.104:80;
server_name maven.kvmx.ru;
allow 10.42.0.0/24;
allow 192.168.1.0/24;
deny all;
# push-to-talk uploads raw PCM; the default 1m is enough for a short
# utterance but not for a long one.
client_max_body_size 32m;
# A 32m upload over a slow link outlives the 60s default on the two body
# timeouts, and nginx cuts it at exactly 60s with a 408 or a 504 that looks
# like the turn failed. Raise them with the size, not just proxy_read_timeout.
client_body_timeout 300s;
location / {
proxy_pass http://127.0.0.1:9201;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_send_timeout 300s; # pushing the PCM upstream, same reason
proxy_read_timeout 300s; # an LLM turn can take minutes on the iGPU
}
}