Recon of the live box (Ubuntu 24.04 x86_64, nginx 1.24, certbot 2.9) showed established conventions from the existing fenja / bifrost-customer services. Match them so the portal looks like a first-class citizen: - service runs as the existing `fenja` user, journald logging + full hardening block (ProtectKernelModules, LockPersonality), ExecStart on /usr/bin/node (box upgraded globally to Node 22) - code in /opt/bifrost-portal, in-dir .env (EnvironmentFile), data under the shared /opt/fenja/data/bifrost-portal (ReadWritePaths) - nginx: 1.24 `listen ... ssl http2` syntax, certbot options-ssl-nginx + dhparam includes, server_tokens off, sites-available/bifrost-portal (no .conf) symlinked; 12m body size for photo uploads; port 4322 (free) - deploy.sh / backup.sh point at the new paths - DEPLOY.md rewritten as a server-specific runbook incl. the global Node 22 upgrade + retest of the existing apps, and pnpm via corepack Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
63 lines
2.4 KiB
Text
63 lines
2.4 KiB
Text
# nginx site for bifrost-portal.fenja.ai
|
|
# Reverse-proxies to the Bifrost portal Node server on 127.0.0.1:4322.
|
|
# Coexists with the existing project-bifrost.fenja.ai site; only claims this
|
|
# hostname. Matches that site's conventions (nginx 1.24, certbot TLS includes).
|
|
#
|
|
# Install (no .conf extension, to match the existing sites-available layout):
|
|
# sudo cp deploy/nginx/bifrost-portal.fenja.ai.conf /etc/nginx/sites-available/bifrost-portal
|
|
# sudo ln -s /etc/nginx/sites-available/bifrost-portal /etc/nginx/sites-enabled/bifrost-portal
|
|
# sudo nginx -t && sudo systemctl reload nginx
|
|
#
|
|
# TLS: the :80 block must be live first so certbot's ACME challenge succeeds.
|
|
# Then: sudo certbot --nginx -d bifrost-portal.fenja.ai (edits this file).
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name bifrost-portal.fenja.ai;
|
|
|
|
# Let certbot's renewals reach .well-known/acme-challenge on port 80
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/html;
|
|
}
|
|
|
|
# Everything else goes to HTTPS
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name bifrost-portal.fenja.ai;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/bifrost-portal.fenja.ai/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/bifrost-portal.fenja.ai/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
|
|
# ─── Security headers ───
|
|
# HSTS — confirm the cert + redirect loop is solid before relying on it.
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Content-Type-Options nosniff always;
|
|
add_header X-Frame-Options SAMEORIGIN always;
|
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
|
|
|
# Event photo uploads can be a few MB (the existing site uses 32k — this
|
|
# site accepts image uploads, so it needs headroom).
|
|
client_max_body_size 12m;
|
|
|
|
# Don't leak nginx version
|
|
server_tokens off;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:4322;
|
|
proxy_http_version 1.1;
|
|
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_read_timeout 60s;
|
|
}
|
|
}
|