fix(deploy): gate proxied runtime routes

This commit is contained in:
Mitchell R 2026-05-11 08:57:55 +02:00
parent e38c92f753
commit b3c17a9d53
No known key found for this signature in database
5 changed files with 145 additions and 5 deletions

View file

@ -235,7 +235,7 @@ Everything else is a shared module (plain TS, no BSB lifecycle).
5. **Rust kiosk polish** — multi-camera compositor, H264/H265 auto-detect, web cells via WebKit 5. **Rust kiosk polish** — multi-camera compositor, H264/H265 auto-detect, web cells via WebKit
6. **Node-RED bridge** — outbound HTTP forwarder + inbound callbacks 6. **Node-RED bridge** — outbound HTTP forwarder + inbound callbacks
7. **Display power relay** — kiosk handles CEC first, then monitor DPMS fallback (`wlr-randr`, then `xset`). Server/admin should keep using generic wake/standby commands, not CEC-only naming. 7. **Display power relay** — kiosk handles CEC first, then monitor DPMS fallback (`wlr-randr`, then `xset`). Server/admin should keep using generic wake/standby commands, not CEC-only naming.
8. **Angie config** + systemd units + Dockerfile 8. **Angie config** + systemd units + Dockerfile — ✅ baseline native + Docker deployment files exist; Angie now uses auth_request for admin/kiosk-gated Node-RED routes, and Docker uses container upstreams.
9. **Auth-check endpoints** — ✅ admin session/API-key, kiosk key, and API-key checks added for proxy `auth_request` 9. **Auth-check endpoints** — ✅ admin session/API-key, kiosk key, and API-key checks added for proxy `auth_request`
## conventions (additions discovered while building) ## conventions (additions discovered while building)

View file

@ -18,6 +18,7 @@ sudo chown betterframe:betterframe /var/lib/betterframe /var/log/betterframe
sudo git clone https://github.com/BetterCorp/BetterFrame.git /opt/betterframe sudo git clone https://github.com/BetterCorp/BetterFrame.git /opt/betterframe
cd /opt/betterframe cd /opt/betterframe
sudo -u betterframe npm install sudo -u betterframe npm install
sudo -u betterframe npm run build
sudo cp sec-config.yaml /opt/betterframe/server/sec-config.yaml sudo cp sec-config.yaml /opt/betterframe/server/sec-config.yaml
# Install systemd unit # Install systemd unit
@ -59,6 +60,9 @@ sudo cp deploy/angie/betterframe.conf /etc/angie/conf.d/
sudo systemctl reload angie sudo systemctl reload angie
``` ```
The Angie config gates `/nrdp/*` with the admin session/API-key auth-check
endpoint and `/in/kiosk/*` with the kiosk Bearer-key auth-check endpoint.
## Docker ## Docker
```bash ```bash
@ -66,6 +70,8 @@ docker compose -f deploy/docker/docker-compose.yml up -d
``` ```
Kiosk still runs natively on the Pi (needs Wayland/HDMI), not in Docker. Kiosk still runs natively on the Pi (needs Wayland/HDMI), not in Docker.
The Compose stack uses `deploy/angie/betterframe.docker.conf` because service
names, not `127.0.0.1`, are the correct upstreams inside the Docker network.
Access: `http://<pi-ip>/setup` for first-run. Access: `http://<pi-ip>/setup` for first-run.

View file

@ -78,7 +78,7 @@ server {
# ---- Node-RED dashboard (admin-only) ---- # ---- Node-RED dashboard (admin-only) ----
location /nrdp/ { location /nrdp/ {
# auth_request /api/admin/_check; # enable when auth-check endpoint ready auth_request /api/admin/_check;
rewrite ^/nrdp/(.*) /$1 break; rewrite ^/nrdp/(.*) /$1 break;
proxy_pass http://betterframe_nodered; proxy_pass http://betterframe_nodered;
proxy_set_header Host $host; proxy_set_header Host $host;
@ -96,11 +96,31 @@ server {
# ---- Node-RED HTTP-in (kiosk-gated) ---- # ---- Node-RED HTTP-in (kiosk-gated) ----
location /in/kiosk/ { location /in/kiosk/ {
# Bearer kiosk-key validated by Node-RED flow auth_request /api/kiosk/_check;
rewrite ^/in/kiosk/(.*) /kiosk/$1 break; rewrite ^/in/kiosk/(.*) /kiosk/$1 break;
proxy_pass http://betterframe_nodered; proxy_pass http://betterframe_nodered;
} }
# ---- Proxy auth subrequests ----
location = /api/admin/_check {
internal;
proxy_pass http://betterframe_admin;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Cookie $http_cookie;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
}
location = /api/kiosk/_check {
internal;
proxy_pass http://betterframe_api;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
}
# ---- Health/readiness/version (public) ---- # ---- Health/readiness/version (public) ----
location ~ ^/(healthz|readyz|version)$ { location ~ ^/(healthz|readyz|version)$ {
proxy_pass http://betterframe_admin; proxy_pass http://betterframe_admin;

View file

@ -0,0 +1,114 @@
# BetterFrame Docker nginx config.
#
# Same routes as betterframe.conf, but upstreams use compose service names
# instead of localhost.
upstream betterframe_admin { server server:18080; keepalive 16; }
upstream betterframe_api { server server:18081; keepalive 16; }
upstream betterframe_ws { server server:18082; }
upstream betterframe_nodered { server nodered:1880; keepalive 8; }
limit_req_zone $binary_remote_addr zone=bf_public:10m rate=30r/s;
server {
listen 80;
server_name _;
client_max_body_size 16M;
location /admin/ {
proxy_pass http://betterframe_admin;
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_http_version 1.1;
proxy_set_header Connection "";
}
location = /admin { return 301 /admin/; }
location /setup { proxy_pass http://betterframe_admin; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
location /auth/ { proxy_pass http://betterframe_admin; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
location /static/ { proxy_pass http://betterframe_admin; }
location /api/kiosk/ {
proxy_pass http://betterframe_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /api/pair/ {
limit_req zone=bf_public burst=10 nodelay;
proxy_pass http://betterframe_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api/admin/ {
proxy_pass http://betterframe_admin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /ws/kiosk {
proxy_pass http://betterframe_ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
location /nrdp/ {
auth_request /api/admin/_check;
rewrite ^/nrdp/(.*) /$1 break;
proxy_pass http://betterframe_nodered;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /in/public/ {
limit_req zone=bf_public burst=20 nodelay;
rewrite ^/in/public/(.*) /public/$1 break;
proxy_pass http://betterframe_nodered;
}
location /in/kiosk/ {
auth_request /api/kiosk/_check;
rewrite ^/in/kiosk/(.*) /kiosk/$1 break;
proxy_pass http://betterframe_nodered;
}
location = /api/admin/_check {
internal;
proxy_pass http://betterframe_admin;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Cookie $http_cookie;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
}
location = /api/kiosk/_check {
internal;
proxy_pass http://betterframe_api;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
}
location ~ ^/(healthz|readyz|version)$ {
proxy_pass http://betterframe_admin;
}
location = / {
proxy_pass http://betterframe_admin;
}
}

View file

@ -21,7 +21,7 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- betterframe-data:/var/lib/betterframe - betterframe-data:/var/lib/betterframe
- ../../sec-config.yaml:/app/sec-config.yaml:ro - ../../sec-config.yaml:/app/server/sec-config.yaml:ro
expose: expose:
- "18080" - "18080"
- "18081" - "18081"
@ -39,7 +39,7 @@ services:
ports: ports:
- "80:80" - "80:80"
volumes: volumes:
- ../angie/betterframe.conf:/etc/nginx/conf.d/default.conf:ro - ../angie/betterframe.docker.conf:/etc/nginx/conf.d/default.conf:ro
networks: networks:
- betterframe - betterframe