Server hardening: nginx origin locked to ArvanCloud + CSP, demo logins removed, docs

- ops/nginx/: the two nginx sites as they now run on 249. Only ArvanCloud
  edges reach the app (direct hits on the IP get 403, so Arvan's DDoS/WAF
  can't be skipped); the frontend gets CSP (own sources only, the one inline
  script by hash), X-Frame-Options, nosniff, Referrer-Policy,
  Permissions-Policy, HSTS; API body limit 100M -> 20M plus HSTS. Checked from
  outside and in Chrome (all tabs and an island quiz, logged in): no CSP errors.
- Demo accounts (users 11-15, 25) and TEST_LOGIN_PHONES removed from the server;
  the health warning for fixed-code logins now has a test and says how to
  test instead (an OTPCode made in the admin).
- SMS daily cap on the server is 2000 (launch of ~600 users).
- CLAUDE.md: never run tests with --parallel on macOS, always --noinput
  (both hang forever without an error).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 19:17:11 +03:30
parent 2a03dc81b5
commit ba4ecff29e
9 changed files with 196 additions and 117 deletions

31
ops/nginx/mathapp-front Normal file
View File

@@ -0,0 +1,31 @@
# روی ۲۴۹: /etc/nginx/sites-available/mathapp-front (sites-enabled یک symlink به آن است) — math.tadrisino2.ir.
server {
listen 80;
listen 443 ssl;
server_name math.tadrisino2.ir;
ssl_certificate /etc/ssl/certs/mathapp.crt;
ssl_certificate_key /etc/ssl/private/mathapp.key;
root /home/administrator/mathapp_frontend_new/dist;
include /etc/nginx/snippets/zareian-arvan-only.conf;
include /etc/nginx/snippets/zareian-front-headers.conf;
# index.html و config.js اسم ثابت دارند: مرورگر باید هر بار از سرور بپرسد (etag → ۳۰۴)،
# وگرنه بعد از دیپلوی نسخه‌ی قدیمی را نشان می‌دهد.
add_header Cache-Control "no-cache" always;
location / {
index index.html;
try_files $uri $uri/ @fallback;
}
# فایل‌های assets/ اسمشان هش دارد و هیچ‌وقت با همان اسم عوض نمی‌شوند.
location /assets/ {
include /etc/nginx/snippets/zareian-front-headers.conf;
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location @fallback {
rewrite ^ /index.html break;
}
}