feat(proxy): https://<LAN IP>:8443 for the native app (backend-managed cert, LAN only)
- new :8443 server (both copies byte-identical): TLS with the backend's
data/tls key+cert, LAN-only allow list, proxies the whole waiter
origin incl. /api/ws/ upgrades
- compose: backend healthcheck; proxy waits for backend healthy (TLS files
exist) and mounts ${DATA_PATH}/tls read-only; publishes 8443;
TLS_PORT passed to the backend so it advertises the published port
Verified on an isolated stack: served key == advertised pin, identity and
WebSocket over TLS, proxy starts only after the backend is healthy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+17
-2
@@ -11,6 +11,7 @@ services:
|
|||||||
- DATABASE_URL=sqlite:////app/data/pos.db
|
- DATABASE_URL=sqlite:////app/data/pos.db
|
||||||
- VERSION=${VERSION:-0.0.0}
|
- VERSION=${VERSION:-0.0.0}
|
||||||
- HOST_IP=${HOST_IP:-}
|
- HOST_IP=${HOST_IP:-}
|
||||||
|
- TLS_PORT=${TLS_PORT:-8443} # published port of the proxy's TLS entry (advertised to the app)
|
||||||
- MASTER_USERNAME=${MASTER_USERNAME:-}
|
- MASTER_USERNAME=${MASTER_USERNAME:-}
|
||||||
- MASTER_PASSWORD=${MASTER_PASSWORD:-}
|
- MASTER_PASSWORD=${MASTER_PASSWORD:-}
|
||||||
volumes:
|
volumes:
|
||||||
@@ -18,6 +19,14 @@ services:
|
|||||||
- ${LOGO_PATH}:/app/logo.png:ro
|
- ${LOGO_PATH}:/app/logo.png:ro
|
||||||
- ${FISCAL_PATH}:/mnt/fiscal
|
- ${FISCAL_PATH}:/mnt/fiscal
|
||||||
- netinfo:/netinfo:ro
|
- netinfo:/netinfo:ro
|
||||||
|
# "Healthy" = app started, which also means the TLS key/cert for the
|
||||||
|
# proxy's :8443 exist (created at startup by services/tls_identity.py)
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/system/health', timeout=3)"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
# Finds the server's LAN IP on the PHYSICAL network (never a VPN) every minute
|
# Finds the server's LAN IP on the PHYSICAL network (never a VPN) every minute
|
||||||
# and shares it with the backend (pairing QR, manager, heartbeat). Needs the
|
# and shares it with the backend (pairing QR, manager, heartbeat). Needs the
|
||||||
@@ -50,12 +59,18 @@ services:
|
|||||||
- "443:443"
|
- "443:443"
|
||||||
- "4443:4443"
|
- "4443:4443"
|
||||||
- "8081:8081" # manager over plain HTTP (LAN only)
|
- "8081:8081" # manager over plain HTTP (LAN only)
|
||||||
|
- "8443:8443" # native app over TLS with a pinned key (LAN only)
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx-proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
- ./nginx-proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
- ./certs:/etc/nginx/certs:ro
|
- ./certs:/etc/nginx/certs:ro
|
||||||
|
- ${DATA_PATH}/tls:/etc/nginx/xenia-tls:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- waiter_pwa
|
backend:
|
||||||
- manager_dashboard
|
condition: service_healthy
|
||||||
|
waiter_pwa:
|
||||||
|
condition: service_started
|
||||||
|
manager_dashboard:
|
||||||
|
condition: service_started
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+39
@@ -108,6 +108,7 @@ cat > "$SCRIPT_DIR/nginx-proxy/nginx.conf" << 'EOF'
|
|||||||
# :8081 → manager dashboard over plain HTTP, LAN only
|
# :8081 → manager dashboard over plain HTTP, LAN only
|
||||||
# :443 waiter.* / manager.* / bare IP → https (needs certs/cert.pem + key.pem)
|
# :443 waiter.* / manager.* / bare IP → https (needs certs/cert.pem + key.pem)
|
||||||
# :4443 → manager over https
|
# :4443 → manager over https
|
||||||
|
# :8443 → native app over TLS (backend-managed key, pinned), LAN only
|
||||||
#
|
#
|
||||||
# Every proxied location forwards WebSocket upgrades (/api/ws/connect) and
|
# Every proxied location forwards WebSocket upgrades (/api/ws/connect) and
|
||||||
# disables buffering (SSE), otherwise live events never reach phones / KDS.
|
# disables buffering (SSE), otherwise live events never reach phones / KDS.
|
||||||
@@ -279,6 +280,44 @@ server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8443 ssl default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Encrypted LAN entry for the native app (plan step 7). Key + self-signed cert
|
||||||
|
# are created and renewed by the backend (services/tls_identity.py) under
|
||||||
|
# ${DATA_PATH}/tls; phones pin the public key, so renewals need no action.
|
||||||
|
ssl_certificate /etc/nginx/xenia-tls/cert.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/xenia-tls/key.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
# 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://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 {
|
server {
|
||||||
listen 4443 ssl default_server;
|
listen 4443 ssl default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
# :8081 → manager dashboard over plain HTTP, LAN only
|
# :8081 → manager dashboard over plain HTTP, LAN only
|
||||||
# :443 waiter.* / manager.* / bare IP → https (needs certs/cert.pem + key.pem)
|
# :443 waiter.* / manager.* / bare IP → https (needs certs/cert.pem + key.pem)
|
||||||
# :4443 → manager over https
|
# :4443 → manager over https
|
||||||
|
# :8443 → native app over TLS (backend-managed key, pinned), LAN only
|
||||||
#
|
#
|
||||||
# Every proxied location forwards WebSocket upgrades (/api/ws/connect) and
|
# Every proxied location forwards WebSocket upgrades (/api/ws/connect) and
|
||||||
# disables buffering (SSE), otherwise live events never reach phones / KDS.
|
# disables buffering (SSE), otherwise live events never reach phones / KDS.
|
||||||
@@ -179,6 +180,44 @@ server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8443 ssl default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Encrypted LAN entry for the native app (plan step 7). Key + self-signed cert
|
||||||
|
# are created and renewed by the backend (services/tls_identity.py) under
|
||||||
|
# ${DATA_PATH}/tls; phones pin the public key, so renewals need no action.
|
||||||
|
ssl_certificate /etc/nginx/xenia-tls/cert.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/xenia-tls/key.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
# 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://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 {
|
server {
|
||||||
listen 4443 ssl default_server;
|
listen 4443 ssl default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|||||||
Reference in New Issue
Block a user