feat(proxy): plain-HTTP LAN entry for waiter (:80) and manager (:8081), no certs required

Phones can now open the waiter app at http://<LAN IP> with no domain, DNS
record or certificate (works around DNS-rebinding failures, KI-001). The
manager gets http://<LAN IP>:8081, with http://<LAN IP>/manager redirecting
there. waiter.*/manager.* hostnames on :80 still redirect to https, so
legacy domain sites behave as before.

- Both plain-HTTP servers are LAN-only (allow RFC1918/loopback/ULA/link-local,
  deny all -> 403), so a router port-forward can't expose an unencrypted POS
- nginx-proxy/nginx.conf and the install.sh heredoc are now byte-identical
  (one canonical config, routing map in its header)
- install.sh generates a 10-year self-signed cert when certs/ is empty (nginx
  won't start its TLS listeners without one), detects HOST_IP via
  'ip route get', prompts for it on fresh installs and backfills it into an
  existing .env, always starts the stack, prints the LAN URLs
- docker-compose publishes 8081; .env.example documents HOST_IP
- pack README: ports/request path updated, CS-5 byte-identical check

Verified: nginx -t; install.sh in Debian (fresh / upgrade without HOST_IP /
re-run - no duplicate HOST_IP, cert SAN includes HOST_IP, key 600); full
stack from freshly built images: every entry point returns the expected
200/301/302, and removing the gateway's range from the allow list yields 403
on :80 and :8081.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-28 11:50:29 +03:00
co-authored by Claude Opus 5.5
parent 18012c2c95
commit 9fcb4df30e
5 changed files with 330 additions and 54 deletions
+150 -6
View File
@@ -1,19 +1,101 @@
# Repo/dev proxy config. install.sh writes its own copy on client sites — keep both in sync.
# Every proxied location must forward WebSocket upgrades (/api/ws/connect) and
# must not buffer (SSE), otherwise live events never reach waiters / KDS.
# Xenia POS — on-site proxy config.
# install.sh writes this exact file on client sites; nginx-proxy/nginx.conf in the
# repo is a byte-identical copy. Change both together (global Working Rule 9).
#
# :80 waiter.* / manager.* hostnames → redirect to https (legacy domain setup)
# :80 anything else (bare LAN IP) → waiter app over plain HTTP, LAN only
# /manager → redirect to :8081
# :8081 → manager dashboard over plain HTTP, LAN only
# :443 waiter.* / manager.* / bare IP → https (needs certs/cert.pem + key.pem)
# :4443 → manager over https
#
# Every proxied location forwards WebSocket upgrades (/api/ws/connect) and
# disables buffering (SSE), otherwise live events never reach phones / KDS.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ── Plain HTTP ────────────────────────────────────────────────────────────────
server {
listen 80;
server_name waiter.* manager.*;
return 301 https://$host$request_uri;
}
server {
listen 80 default_server;
server_name _;
# LAN only: plain HTTP must never be reachable from the internet, even if the
# client forwards ports on their router. Relies on Docker preserving the real
# client IP (default iptables port publishing on Linux).
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
allow 127.0.0.0/8;
allow fc00::/7;
allow fe80::/10;
allow ::1;
deny all;
location ~ ^/manager/?$ {
return 302 http://$host:8081/;
}
location / {
proxy_pass http://waiter_pwa:80;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 8081 default_server;
server_name _;
# LAN only: plain HTTP must never be reachable from the internet, even if the
# client forwards ports on their router. Relies on Docker preserving the real
# client IP (default iptables port publishing on Linux).
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
allow 127.0.0.0/8;
allow fc00::/7;
allow fe80::/10;
allow ::1;
deny all;
location / {
proxy_pass http://manager_dashboard:80;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
# ── HTTPS ─────────────────────────────────────────────────────────────────────
server {
listen 443 ssl;
server_name waiter.* _;
server_name waiter.*;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
@@ -36,8 +118,70 @@ server {
}
server {
listen 4443 ssl;
server_name manager.* _;
listen 443 ssl;
server_name manager.*;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://manager_dashboard:80;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /api/ {
proxy_pass http://backend:8000;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
location / {
proxy_pass http://waiter_pwa:80;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 4443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;