22# (/downloading via sendfile, /upload) run here so the agent saturates 10 GbE on
33# modest hardware; result posts + the results link proxy to the agent's loopback
44# relay (127.0.0.1:3001), which forwards them to the central server with the site
5- # slug and the real client IP. Same-origin, so no CORS is needed.
5+ # slug and the real client IP. LAN tests are same-origin; the /wan/ flow's
6+ # post-back from the external test page is cross-origin, so the relay answers
7+ # with CORS headers.
68server {
79 server_name _;
8- listen 3000 reuseport;
9- listen [::]:3000 reuseport;
10+ listen 3000 ssl reuseport;
11+ listen [::]:3000 ssl reuseport;
12+ # Self-signed TLS by default so the OpenSpeedTest page is a secure context - the
13+ # browser Geolocation API (GPS-tagged results) requires https, and it works over
14+ # the agent's LAN IP without a per-site reverse proxy. The cert is generated once
15+ # and persisted (entrypoint.sh for Docker, install-native.sh for bare-metal) with
16+ # the LAN IP + hostname as SANs, so a client's browser trust exception survives
17+ # restarts. Cert paths are filled in at build/install time (like __PIDFILE__).
18+ ssl_certificate __CERTFILE__;
19+ ssl_certificate_key __KEYFILE__;
20+ ssl_protocols TLSv1.2 TLSv1.3;
21+ ssl_session_cache shared:ost:2m ;
1022
1123 root /usr/share/nginx/html;
1224 index index .html;
@@ -39,20 +51,43 @@ server {
3951 proxy_set_header Host $host ;
4052 }
4153
54+ # Who-am-I for the central Client Performance page: a browser on this site's LAN
55+ # learns its own address here (the central server only ever sees the site's WAN
56+ # IP). Open CORS is deliberate - the response is the caller's own address, nothing
57+ # else, and the page fetching it runs on the central server's origin.
58+ location = /netopt/whoami {
59+ default_type application/json;
60+ add_header Access-Control-Allow-Origin "*" always;
61+ return 200 '{"ip":"$remote_addr"}' ;
62+ }
63+
64+ # WAN speed test router: the agent serves /wan/ (default server) or
65+ # /wan/<server-ID>/ as an interstitial that navigates to the mapped external
66+ # WAN test server, making this agent the page's referrer so results post
67+ # back to this origin with the real client LAN IP.
68+ location /wan/ {
69+ proxy_pass http ://127.0.0.1:3001 ;
70+ proxy_set_header Host $host ;
71+ proxy_set_header X-Forwarded-For $remote_addr ;
72+ }
73+
74+ location = /wan {
75+ return 302 /wan/$is_args$args ;
76+ }
77+
4278 # Upload endpoint - reads the entire POST body before responding (the
4379 # error_page 405 hack alone would respond before the body is received).
4480 location = /upload {
4581 add_header Cache-Control 'no-store, no-cache, max-age=0, no-transform' ;
4682 client_body_buffer_size 35m ;
4783 client_max_body_size 50m ;
48- proxy_pass http ://127.0.0.1:3000 /upload-sink;
84+ # The public :3000 listener is now TLS; the sink that consumes the upload
85+ # body lives on a plaintext loopback listener (server block below) so the
86+ # self-proxy stays cheap HTTP rather than looping back through TLS.
87+ proxy_pass http ://127.0.0.1:3002 /upload-sink;
4988 proxy_set_header Host $host ;
5089 }
5190
52- location = /upload-sink {
53- return 200 ;
54- }
55-
5691 location / {
5792 add_header Cache-Control 'no-store, no-cache, max-age=0, no-transform' ;
5893 add_header Last-Modified $date_gmt ;
@@ -76,3 +111,15 @@ server {
76111 gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
77112 }
78113}
114+
115+ # Plaintext loopback sink for the upload leg. The /upload location proxies the POST
116+ # body here purely to read it (measuring upload throughput) then returns 200 - it must
117+ # be plaintext HTTP since the public :3000 listener is now TLS. Loopback-only, so it is
118+ # never reachable from the network. client_max_body_size 0 lifts the body cap for the
119+ # discard (the client-facing /upload keeps its own 50m cap).
120+ server {
121+ listen 127.0.0.1:3002 ;
122+ client_max_body_size 0;
123+ access_log off;
124+ location = /upload-sink { return 200 ; }
125+ }
0 commit comments