portfolio-page/backend/deploy/nginx-api.sascha-bach.de.conf

90 lines
3.3 KiB
Plaintext

# Reverse proxy for the audit request endpoint.
#
# Deliberately a separate server block on its own hostname, so it does not
# touch the existing git / nextcloud vhosts on this machine. Reload nginx
# rather than restarting it, and the other services never drop a connection.
#
# Install to /etc/nginx/sites-available/api.sascha-bach.de and symlink into
# sites-enabled. certbot --nginx rewrites the TLS parts of this file.
server {
listen 80;
listen [::]:80;
server_name api.sascha-bach.de;
# Needed once so certbot can answer the HTTP-01 challenge.
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
# Rate-limits /deploy-webhook specifically. It is now reachable over the
# public hostname (see the location block below), so unlike
# /api/audit-request it has no per-request Node-side limiter of its own -
# the HMAC signature check is the real gate, this is just a cheap first
# filter against noise.
limit_req_zone $binary_remote_addr zone=deploy_webhook:1m rate=10r/m;
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name api.sascha-bach.de;
# certbot inserts ssl_certificate / ssl_certificate_key here.
# The Node process caps bodies at 10 kB. Reject oversized ones at the edge
# so they never reach it.
client_max_body_size 16k;
# Only these paths exist. Everything else is refused rather than
# forwarded, which keeps the attack surface to what the form needs.
location = /api/audit-request {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Required: server.js runs with `trust proxy 1` and derives the
# rate-limit key from this header. Without it every visitor shares
# one bucket and three submissions lock out everyone for 15 minutes.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 30s;
}
location = /health {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
access_log off;
}
# Only needed if Gitea/Forgejo runs in a Docker container: 127.0.0.1
# inside that container is the container's own loopback, not this host,
# so the webhook target can't be http://127.0.0.1:3002 in that case.
# Routing it here instead works regardless of the container network
# setup - the request simply goes out over HTTPS to this hostname and
# back in, the same way any other webhook call would.
#
# If Gitea runs natively on this same machine (not in Docker), this
# block is not needed - point the Gitea webhook straight at
# http://127.0.0.1:3002/deploy-webhook instead and skip this location.
location = /deploy-webhook {
limit_req zone=deploy_webhook burst=5 nodelay;
proxy_pass http://127.0.0.1:3002;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 30s;
}
location / {
return 404;
}
}