4.7 KiB
Nginx Proxy Manager proxy hosts
This file records the docker-1 compose setup. Once production and test run on their native VMs, the proxy hosts point at the VMs over the LAN instead: see docs/DEPLOY.md, "Native VM install (phase 10)", NPM section.
NPM runs on docker-1 as container npm_app_1 on network npm_default (compose project in
/home/spencerm/npm). It terminates HTTPS for both Packet Station stacks. The stacks publish
no port. Each app joins npm_default through deploy/compose.proxy.yaml with its own alias,
and NPM forwards to that alias.
Make one proxy host per stack in the NPM web UI.
| Production | Test | |
|---|---|---|
| Domain name | packetstation.pescoinc.biz |
packetstation-test.pescoinc.biz |
| Forward hostname | packetstation-app |
packetstation-test-app |
APP_ALIAS in the stack env file |
packetstation-app |
packetstation-test-app |
Details tab
- Scheme:
http - Forward hostname / IP: the alias from the table above. Never an IP address: container addresses change when a stack is recreated.
- Forward port:
8000 - Cache assets: off. The app sets its own cache headers.
- Block common exploits: on. Confirm it does not reject uploads when the test proxy host is made (plan open question 16).
- Websockets support: off. Live updates use server-sent events, not websockets.
- Access list: none. Sign-in is done by the app.
SSL tab
- SSL certificate: the PESCO certificate for the name.
- Force SSL: on.
- HTTP/2 support: on.
- HSTS enabled: off. The app sends
Strict-Transport-Securityitself, along with CSP and the other security headers. Two HSTS headers with different values would conflict. - HSTS subdomains: off.
Advanced tab
Paste this block as is for both hosts:
client_max_body_size 30m;
location /api/events {
proxy_pass $forward_scheme://$server:$port;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Why each part is there:
client_max_body_size 30m: drawings, MTRs and reports are uploaded through the app. NPM's default is 1 MB at the server level. The app enforces its own, smaller per-type limits.location /api/events: the server-sent events stream. Each open screen holds one request open for up to an hour. Buffering must be off or events arrive late in batches. The app also sendsX-Accel-Buffering: noon this response.proxy_http_version 1.1with an emptyConnectionheader keeps the upstream connection open for the stream.- The three
proxy_set_headerlines repeat what NPM's ownlocation /adds. A custom location does not inherit NPM's proxy include, so without them the app would see NPM's address as the client and the wrong scheme.
Why $server and not the alias by name
NPM writes set $forward_scheme http; set $server "<forward hostname>"; set $port 8000; into
each proxy host's server block, and its own location / uses proxy_pass with those variables.
When proxy_pass contains a variable, nginx resolves the name at request time through the
resolver NPM configures (Docker's DNS at 127.0.0.11). A literal
proxy_pass http://packetstation-app:8000; is resolved once when nginx loads its config. That
breaks in two ways: nginx refuses to load the config if the stack is down at that moment, which
takes every other proxy host down with it, and after the stack is recreated the app gets a new
address while nginx keeps sending to the old one. Using $forward_scheme://$server:$port keeps
the events location on the same name and the same live resolution as the rest of the host.
Trust of X-Forwarded-*
The app trusts X-Forwarded-For and X-Forwarded-Proto only from npm_app_1's own address
(TRUSTED_PROXY_HOST=npm_app_1), resolved at start and re-resolved every 60 seconds so an NPM
restart is followed. It does not trust the whole npm_default subnet (172.22.0.0/16): other
stacks share that network, and the per-address sign-in rate limit depends on the real client
address. A request from any other address has its forwarded headers ignored. uvicorn runs
without --proxy-headers; the app does this check itself.
Check after saving
From a PC on the LAN:
https://<name>/loads the sign-in page with a valid certificate.- The response headers show one
Strict-Transport-Securityheader, from the app. - Open a packet screen and leave it for five minutes. A change made in another browser shows up without a reload, so the events stream is not being buffered or cut.
- Upload a 20 MB PDF on the test stack. It is accepted (Block common exploits did not reject it).