# nginx.service — InterGenOS-hardened systemd unit for nginx
#
# Applies the database-fleet §5e systemd hardening baseline (from the
# database-landing-plan, generalized to the web-server fleet). Bind
# default is 127.0.0.1 only (set in /etc/nginx/nginx.conf
# `listen 127.0.0.1:443` directives). server_tokens off. Status
# endpoint locked to 127.0.0.1 via allow/deny pair in the sample
# config. TLS-only sample server.
#
# Service ships disabled — operator runs `systemctl enable nginx`
# consciously after reviewing the sample config + replacing the
# self-signed cert with a real one if needed.

[Unit]
Description=nginx HTTP server
After=network.target nginx-tls-keygen.service
# Per-machine TLS cert must exist before nginx reads it (F43). The keygen unit
# is a no-op once a cert is present, so this coupling is safe on every start.
Requires=nginx-tls-keygen.service

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
KillMode=mixed
KillSignal=SIGQUIT
TimeoutStopSec=10

# nginx's master process starts as root to bind privileged ports +
# read TLS private keys, then fork-execs workers as the configured
# `user` (nginx). This is the unprivileged-worker pattern; the master
# is the only privileged surface and immediately drops to the unpriv
# user after socket bind. Capabilities below are scoped to the
# privileged-bind path only.

# --- database-fleet §5e hardening baseline ---

NoNewPrivileges=true

# Filesystem isolation
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadWritePaths=/var/log/nginx /var/cache/nginx /run

# Kernel surface reductions
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectHostname=true
ProtectClock=true
ProtectProc=invisible

# Address-family / namespace / realtime / SUID restrictions
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true

# Memory protection — nginx has no JIT, MDWE is safe
MemoryDenyWriteExecute=true
RemoveIPC=true

# Syscall surface
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @mount @swap @reboot

# Capabilities — nginx master needs CAP_NET_BIND_SERVICE (port 80/443)
# + CAP_SETUID/CAP_SETGID to drop to the nginx user after bind. Drop
# everything else.
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID
AmbientCapabilities=

[Install]
WantedBy=multi-user.target
