fix(security): sovereign-audit hardening pass — RCE, multi-tenant, reliability Reasoning-based audit fixes (all verified by typecheck, attack paths re-traced): - build-time RCE: validate spec.dependencies to npm-registry semver only (no git/url/file specifiers) + --ignore-scripts in runner Dockerfile. - container hardening fail-CLOSED: harden unless RUNNER_DISABLE_HARDENING=1, no longer gated on a fragile NODE_ENV string compare. - secret env keys validated (UPPER_SNAKE, reject NODE_*/PATH/LD_*). - cross-org image-tag collision: qualify tag with serverId. - /iterate now enforces suspension + daily-build limits like /servers. - preview SSE: clear keepalive in finally + on client close (timer/FD leak). - SMS OTP: atomic attempt counter (lt(attempts,MAX) in UPDATE) — brute-force race. - getSession orders membership by createdAt (deterministic primary org). - template scopes aggregated from real tool scopes (was hardcoded mcp:read). - template category filter pushed into WHERE (was applied after LIMIT). - support admin reply/status: 404 on unknown ticket; status change now audited. - build worker: queue defaultJobOptions, docker build/run/stop timeouts, old-container teardown in finally (no orphan on post-deploy DB failure). - nginx: HSTS, X-Frame-Options DENY, nosniff, Referrer-Policy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
95 lines
3.7 KiB
Plaintext
95 lines
3.7 KiB
Plaintext
# nginx vhost for buildmymcpserver.com — install on the host nginx:
|
|
# cp this to /etc/nginx/sites-available/buildmymcpserver
|
|
# ln -s /etc/nginx/sites-available/buildmymcpserver /etc/nginx/sites-enabled/
|
|
# nginx -t && systemctl reload nginx
|
|
#
|
|
# Serves both :80 and :443. The :443 listener uses a self-signed origin cert
|
|
# (see DEPLOY.md) so Cloudflare can run in "Full" mode — TLS all the way to the
|
|
# origin — instead of "Flexible" (plaintext origin hop). For "Full (strict)",
|
|
# swap the self-signed cert for a Cloudflare Origin Certificate.
|
|
|
|
# --- Web app: buildmymcpserver.com ---
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name buildmymcpserver.com www.buildmymcpserver.com;
|
|
|
|
ssl_certificate /etc/ssl/buildmymcpserver/origin.crt;
|
|
ssl_certificate_key /etc/ssl/buildmymcpserver/origin.key;
|
|
|
|
client_max_body_size 12M;
|
|
|
|
# Security headers (INF-002). Cloudflare sits in front — if it also injects
|
|
# HSTS, drop that line here to avoid duplication. CSP intentionally omitted
|
|
# for now (a wrong policy breaks Next/Tailwind inline) — track separately.
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:4001;
|
|
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_cache_bypass $http_upgrade;
|
|
proxy_read_timeout 120s;
|
|
}
|
|
}
|
|
|
|
# --- Control plane API: api.buildmymcpserver.com ---
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name api.buildmymcpserver.com;
|
|
|
|
ssl_certificate /etc/ssl/buildmymcpserver/origin.crt;
|
|
ssl_certificate_key /etc/ssl/buildmymcpserver/origin.key;
|
|
|
|
client_max_body_size 12M;
|
|
|
|
# Security headers (INF-002). nosniff matters for the JSON API; XFO/HSTS are
|
|
# belt-and-suspenders for any HTML the API might ever serve.
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
# Build-log WebSocket stream (/v1/builds/:id/stream) — needs the upgrade
|
|
# headers and a long read timeout; buffering off so frames are not held.
|
|
location /v1/builds/ {
|
|
proxy_pass http://127.0.0.1:4000;
|
|
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_buffering off;
|
|
proxy_cache off;
|
|
proxy_read_timeout 600s;
|
|
}
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:4000;
|
|
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_cache_bypass $http_upgrade;
|
|
proxy_read_timeout 120s;
|
|
}
|
|
}
|