docs: align deployment/setup docs with current code

- nginx/docs/PRODUCTION_DEPLOYMENT.md: drop stale htpasswd/Basic-auth model,
  manual server-side deploy step, /var/www/posterg paths and hardcoded IP; describe
  the PHP-layer AdminAuth, automated 📋 Deploying nginx configuration…
xamxam.conf

sent 145 bytes  received 125 bytes  540.00 bytes/sec
total size is 10,012  speedup is 37.08
deploy-server.sh

sent 143 bytes  received 113 bytes  512.00 bytes/sec
total size is 8,519  speedup is 33.28, and what deploy-server.sh
  actually does.
- nginx/docs/QUICK_REFERENCE.md: replace htpasswd user management with AdminAuth
  guidance; fix php8.2->php8.4, log paths, admin rate limit (300r/m), socket path.
- nginx/README.md, nginx/SETUP.md: note 📋 Deploying nginx configuration…
xamxam.conf

sent 145 bytes  received 125 bytes  540.00 bytes/sec
total size is 10,012  speedup is 37.08
deploy-server.sh

sent 143 bytes  received 113 bytes  170.67 bytes/sec
total size is 8,519  speedup is 33.28 automates
  deploy-server.sh (no manual ssh/sudo step).
- docs/deployment.md: correct deploy-code scope (code-only, no --chown), fix the
  /var/log/xamxam provisioning step (deploy-server.sh via deploy-nginx, not
  deploy-code), document the setup-server.sh prerequisite and fresh-box caveat.
- README.md: replace the incomplete manual first-time setup snippet with the
  real setup-server.sh + provision-server flow and a fresh-box caveat.
This commit is contained in:
Pontoporeia
2026-09-18 16:26:49 +02:00
parent 64fd92b913
commit 0e009c49d4
6 changed files with 220 additions and 393 deletions
+21 -7
View File
@@ -40,21 +40,35 @@ accidental overwrites of production data.
### First-time server setup ### First-time server setup
First bootstrap the role/user/dirs with `scripts/setup-server.sh`, then the one-shot
remote provisioning recipe, which deploys code, nginx, and cron jobs:
```bash ```bash
ssh xamxam # 1. role/user/dir bootstrap (creates xamxam group, /var/www/xamxam, perms):
sudo mkdir -p /var/www/xamxam # equivalent to: ssh -t xamxam "sudo DEPLOY_USER=\$USER bash /tmp/setup-server.sh"
sudo chown www-data:xamxam /var/www/xamxam rsync scripts/setup-server.sh xamxam:/tmp/setup-server.sh
sudo chmod 775 /var/www/xamxam ssh -t xamxam "sudo DEPLOY_USER=\$USER bash /tmp/setup-server.sh"
exit
# 2. full provisioning (env/APP_KEY + deploy + nginx + backup + cleanup cron + logrotate):
just provision-server
``` ```
Then deploy once and apply nginx config: > **Fresh box caveat:** `provision-server` assumes `setup-server.sh` already ran.
> On a truly empty `/var/www/xamxam`, run `setup-server.sh` (or re-apply
> `deploy-server.sh` via `just deploy-nginx`) first, otherwise the deploy's
> rsync hits `Permission denied` on `storage/`.
`just deploy` / `just deploy-nginx` apply code + nginx config:
```bash ```bash
just deploy just deploy
just deploy-nginx just deploy-nginx # included in `just deploy` and `just provision-server`
``` ```
See [docs/deployment.md](docs/deployment.md) for the full workflow and the
podman test environment in [test-env/README.md](test-env/README.md) to validate
any of this against a fresh throwaway box.
## Security notes ## Security notes
- Admin panel protected by PHP session (`AdminAuth`) — password-only, no username - Admin panel protected by PHP session (`AdminAuth`) — password-only, no username
+41 -11
View File
@@ -19,26 +19,44 @@ Deployment is orchestrated through the `justfile` (`deploy` group).
## One-time server setup ## One-time server setup
From a machine with `ssh` access to the `xamxam` host, run the full remote From a machine with `ssh` access to the `xamxam` host, **first** bootstrap the
provisioning once: role/user/dirs with `scripts/setup-server.sh` (creates the `xamxam` group, adds
`www-data` and the deploy user to it, creates `/var/www/xamxam` and the
cache/log/backup dirs), then run the full remote provisioning once:
```bash ```bash
# 1. role/user/dir bootstrap (as root on the host):
rsync scripts/setup-server.sh xamxam:/tmp/setup-server.sh
ssh -t xamxam "sudo DEPLOY_USER=\$USER bash /tmp/setup-server.sh"
# 2. full provisioning (env/APP_KEY + deploy + nginx + backup + cron + logrotate):
just provision-server just provision-server
``` ```
> **Fresh box caveat:** `provision-server` assumes `setup-server.sh` already ran.
> On a truly empty `/var/www/xamxam` the deploy's rsync fails with
> `Permission denied` on `storage/` until the setgid group-writable tree exists.
> `scripts/deploy-server.sh` (via `just deploy-nginx`) is what normalises
> ownership/perms on each deploy.
This chains (each step is also runnable individually): This chains (each step is also runnable individually):
1. `scripts/provision-server-env.sh` — ensure the server has an `APP_KEY` in 1. `scripts/provision-server-env.sh` — ensure the server has an `APP_KEY` in
`/var/www/xamxam/.env` (idempotent; see below). `/var/www/xamxam/.env` (idempotent; see below).
2. `just deploy` — code + Composer deps + migrations + permissions. 2. `just deploy` — code + Composer deps + migrations + `deploy-nginx`
3. `just deploy-nginx` — install + apply the nginx config and fix permissions. (install + apply the nginx config and fix permissions) + env + permissions check.
4. `just deploy-backup`, `just deploy-cleanup-cron`, `just deploy-tmp-cleanup-cron`, `just deploy-logrotate` — 3. `just deploy-backup` — install the backup script + cron jobs.
install backup + cleanup (drafts + abandoned uploads) cron jobs and log rotation. 4. `just deploy-cleanup-cron`, `just deploy-logrotate` — install the orphaned-
draft cleanup cron and log rotation.
It finishes by telling you what's left to do in `/admin/account` (set the admin It finishes by telling you what's left to do in `/admin/account` (set the admin
password — a fresh DB starts unauthenticated — and configure SMTP/PeerTube password — a fresh DB starts unauthenticated — and configure SMTP/PeerTube
credentials and Nextcloud sync). credentials and Nextcloud sync).
> The abandoned-upload GC cron (`just deploy-tmp-cleanup-cron`) is **not** part
> of `provision-server`; install it separately if you want hourly GC of
> abandoned FilePond uploads.
If you'd rather do it step by step (e.g. you already provisioned nginx): If you'd rather do it step by step (e.g. you already provisioned nginx):
```bash ```bash
@@ -81,7 +99,7 @@ If you ever rotate `APP_KEY`, re-encrypt the SMTP password with
| Command | Purpose | | Command | Purpose |
|---------|---------| |---------|---------|
| `just deploy` | Full deploy: build + code + Composer deps + migrations + env + permissions check | | `just deploy` | Full deploy: build + code + Composer deps + migrations + env + permissions check |
| `just deploy-code` | rsync app files + nginx config + permissions (no Composer, no migrations) | | `just deploy-code` | rsync app files only (no Composer, no nginx, no migrations) |
| `just deploy-deps` | Sync composer.{json,lock} → server, then `composer install`/`dump-autoload` | | `just deploy-deps` | Sync composer.{json,lock} → server, then `composer install`/`dump-autoload` |
| `just deploy-migrate` | Run pending DB migrations on the server | | `just deploy-migrate` | Run pending DB migrations on the server |
| `just deploy-env` | Upload `app/.env` (only if the remote `.env` is absent — never overwrites a key) | | `just deploy-env` | Upload `app/.env` (only if the remote `.env` is absent — never overwrites a key) |
@@ -95,9 +113,9 @@ If you ever rotate `APP_KEY`, re-encrypt the SMTP password with
> `just deploy-logrotate` to install log rotation. A single `just deploy-all-first` > `just deploy-logrotate` to install log rotation. A single `just deploy-all-first`
> chains all of these together. > chains all of these together.
> The app log directory `/var/log/xamxam/` is provisioned automatically by > The app log directory `/var/log/xamxam/` is provisioned automatically by
> `deploy-code` on every run (via `deploy-server.sh`), so no separate step is > `scripts/deploy-server.sh` on every `just deploy-nginx` (which `just deploy`
> needed for it. To migrate logs written by an older build, run > includes), so no separate step is needed for it. To migrate logs written by an
> `just migrate-log-names --apply` once. > older build, run `just migrate-log-names --apply` once.
### Environment file & re-encryption ### Environment file & re-encryption
@@ -153,7 +171,9 @@ are purged by the cron above.
## Permissions model ## Permissions model
Managed/simulated by the justfile and verified by `deploy-verify-permissions`: Ownership and permissions are applied by `scripts/deploy-server.sh` (run via
`just deploy-nginx`, which `just deploy` includes) and verified by
`deploy-verify-permissions`:
- Ownership: `www-data:xamxam` - Ownership: `www-data:xamxam`
- Directories: **2775** (setgid — new files inherit the `xamxam` group) - Directories: **2775** (setgid — new files inherit the `xamxam` group)
@@ -161,6 +181,16 @@ Managed/simulated by the justfile and verified by `deploy-verify-permissions`:
- `storage/xamxam.db` and other `*.db`: **660** - `storage/xamxam.db` and other `*.db`: **660**
- `app/.env`: **640** - `app/.env`: **640**
`just deploy-code` only rsyncs code (as the SSH/deploy user); it does **not**
set ownership. Ownership is normalised afterwards by `deploy-server.sh`
(`chown -R www-data:xamxam /var/www/xamxam`, setgid dirs, locked DBs). On a
`xamxam` group-writable tree the rsync succeeds, then `deploy-nginx` (within
`just deploy`) fixes ownership/perms. If `deploy-code` reports `Permission
denied` on `storage/` (e.g. a fresh box where the dirs are not yet
`www-data:xamxam` group-writable), re-apply `scripts/setup-server.sh` /
`deploy-server.sh` first, or add `--chown=www-data:xamxam` to the rsync so it
normalises ownership while transferring.
The nginx/`deploy-server.sh` step (`just deploy-nginx`, or The nginx/`deploy-server.sh` step (`just deploy-nginx`, or
`sudo DEPLOY_USER=$USER bash /tmp/deploy-server.sh` via `just deploy-script`) `sudo DEPLOY_USER=$USER bash /tmp/deploy-server.sh` via `just deploy-script`)
fixes permissions and installs the nginx config. fixes permissions and installs the nginx config.
+4 -7
View File
@@ -18,19 +18,16 @@ This directory contains nginx configuration and documentation for the XAMXAM TFE
### Deploy nginx configuration ### Deploy nginx configuration
```bash ```bash
# From your local machine # From your local machine — this uploads nginx/xamxam.conf + scripts/deploy-server.sh,
# runs the script as root, then cleans up the remote temp files. No server-side step needed.
just deploy-nginx just deploy-nginx
# Then on the server:
ssh xamxam
sudo bash /tmp/deploy-server.sh
``` ```
The deployment script will: The deployment script (`scripts/deploy-server.sh`) will:
- ✅ Fix file permissions (www-data:xamxam) - ✅ Fix file permissions (www-data:xamxam)
- ✅ Install nginx configuration - ✅ Install nginx configuration
- ✅ Test and reload nginx - ✅ Test and reload nginx
- ✅ Verify PHP-FPM is running - ✅ Reload php-fpm (session-GC tuning) and verify it is running
### Manage admin password ### Manage admin password
+6 -9
View File
@@ -1,4 +1,4 @@
# Nginx Setup for Post-ERG # Nginx Setup — XAMXAM
Complete setup guide for nginx with security features and password protection. Complete setup guide for nginx with security features and password protection.
@@ -13,18 +13,15 @@ Complete setup guide for nginx with security features and password protection.
### 1. Deploy from your local machine ### 1. Deploy from your local machine
`just deploy-nginx` uploads `nginx/xamxam.conf` and `scripts/deploy-server.sh`, runs the
script as root on the server (installing the config, fixing permissions, and reloading
nginx + php-fpm), then cleans up. No manual server-side step is required:
```bash ```bash
just deploy-nginx just deploy-nginx
``` ```
### 2. Apply on the server ### 2. Set admin password (first time only)
```bash
ssh xamxam
sudo bash /tmp/deploy-server.sh
```
### 3. Set admin password (first time only)
Visit `/admin/parametres` → Account tab and set the admin password there. Visit `/admin/parametres` → Account tab and set the admin password there.
+70 -178
View File
@@ -1,210 +1,102 @@
# Production Deployment Guide - Post-ERG # Production Deployment Guide
This guide covers deploying the production nginx configuration with proper security and permissions. Deploying the XAMXAM production nginx configuration and administering the site.
## 🎯 Overview ## Overview
- **Server**: xamxam.erg.be (internal IP: 192.168.6.125) - **Host**: `xamxam` (SSH alias), app root `/var/www/xamxam/`
- **PHP Version**: 8.4 - **PHP Version**: 8.4
- **SSL/TLS**: Handled by upstream reverse proxy - **SSL/TLS**: Terminated by an upstream reverse proxy (nginx itself listens on 80)
- **Document Root**: `/var/www/xamxam/public/` - **Document Root**: `/var/www/xamxam/public/` (deployed flat, not under `app/`)
- **Web / FPM user**: `www-data`, app group: `xamxam`
## 🚀 Quick Deployment ## Quick deployment (recommended)
From your local machine: Everything is orchestrated from your local machine through the justfile. `just deploy-nginx`
uploads both `nginx/xamxam.conf` and `scripts/deploy-server.sh` to the server, runs the
latter as root (installing the config, fixing permissions, testing and reloading nginx and
php-fpm), then cleans up the remote temp files:
```bash ```bash
# Deploy nginx config and upload deployment script
just deploy-nginx just deploy-nginx
# Then on the server:
ssh xamxam
sudo bash /tmp/deploy-server.sh
sudo systemctl reload nginx
``` ```
This uploads: No manual server-side step is needed — the recipe already does it. For a full
- `nginx/xamxam.conf` → `/tmp/xamxam.conf` code + dependencies + migrations deploy, run `just deploy` (which includes
- `scripts/deploy-server.sh` → `/tmp/deploy-server.sh` `deploy-nginx`). See [`../../docs/deployment.md`](../../docs/deployment.md).
## 📋 Step-by-Step Deployment What `scripts/deploy-server.sh` does (as root):
### 1. Set Up Admin Password (First Time Only) - Fixes ownership to `www-data:xamxam` across `/var/www/xamxam/`
- Sets directory permissions to **2775** (setgid) and files to **664**
- Locks SQLite databases (and WAL/SHM sidecars) to **660**; `.env` to **640**
- Creates writable cache / upload-tmp / `var/{cache,logs,tmp}` dirs for php-fpm
- Provisions `/var/log/xamxam/` (app log dir) and `/var/backups/xamxam/` (backups)
- Installs a php-fpm session-GC tuning file (`zz-xamxam-session.ini`)
- Installs `nginx/xamxam.conf` into `sites-available`, symlinks it, backs up old
uploads, prunes old backups, validates with `nginx -t`, and reloads nginx + php-fpm
## Admin authentication
The admin panel is protected by the application's **PHP session auth layer**
(`src/AdminAuth.php`), **not** by nginx `htpasswd`/Basic-auth. The user supplies a
single password on `/admin/login.php`; no username, no `/etc/nginx/.htpasswd`.
Configure the admin password in the admin panel at `/admin/parametres` → Account
tab (a fresh DB starts unauthenticated until a password is set). See
[`PHP_AUTH_LAYER.md`](PHP_AUTH_LAYER.md) for the full authentication details.
## Verification
After a successful deploy:
```bash ```bash
ssh xamxam curl -I https://xamxam.erg.be/ # expect 200
sudo htpasswd -c /etc/nginx/.htpasswd-xamxam admin curl -I https://xamxam.erg.be/admin/ # expect 200 (login page / 302 to it)
# Enter a strong password when prompted curl -I https://xamxam.erg.be/storage/xamxam.db # expect 404 / 403 (blocked)
curl -I https://xamxam.erg.be/src/Database.php # expect 404 / 403 (blocked)
just deploy-verify-permissions # expect "All permissions OK"
``` ```
**💡 Tip**: Generate a strong password: Security headers (`X-Frame-Options`, `X-Content-Type-Options`,
```bash `Strict-Transport-Security`, `Referrer-Policy`, `Permissions-Policy`,
openssl rand -base64 32 `Content-Security-Policy`) are emitted by the nginx config; see
``` [`SECURITY_HEADERS.md`](SECURITY_HEADERS.md).
### 2. Deploy Configuration ## Troubleshooting
```bash
# From your local machine
just deploy-nginx
# On the server
sudo bash /tmp/deploy-server.sh
sudo systemctl reload nginx
```
The script will:
- ✅ Fix file permissions (set to www-data:xamxam)
- ✅ Install nginx configuration
- ✅ Test nginx configuration
- ✅ Check PHP-FPM status
## 🔧 Manual Deployment (Alternative)
### Step 1: Fix Permissions
```bash
ssh xamxam
# Set correct ownership
sudo chown -R www-data:xamxam /var/www/xamxam/
# Set directory permissions
sudo find /var/www/posterg -type d -exec chmod 755 {} \;
# Set file permissions
sudo find /var/www/posterg -type f -exec chmod 644 {} \;
# Make storage writable
sudo chmod 775 /var/www/xamxam/storage
# Protect database
sudo chmod 660 /var/www/xamxam/storage/test.db
sudo chown www-data:xamxam /var/www/xamxam/storage/test.db
```
### Step 2: Deploy Nginx Config
```bash
# Copy config
sudo cp /tmp/xamxam.conf /etc/nginx/sites-available/xamxam
# Enable site and disable default
sudo ln -sf /etc/nginx/sites-available/xamxam /etc/nginx/sites-enabled/xamxam
sudo rm -f /etc/nginx/sites-enabled/default
# Test and reload
sudo nginx -t
sudo systemctl reload nginx
```
## 🧪 Testing
### Test Public Site
```bash
# Should return 200 OK
curl -I https://xamxam.erg.be/
```
### Test Admin Protection
```bash
# Should return 401 Unauthorized
curl -I https://xamxam.erg.be/admin/
# With credentials
curl -u admin:your_password https://xamxam.erg.be/admin/
```
### Test File Protection
```bash
# Should return 403 Forbidden
curl -I https://xamxam.erg.be/storage/test.db
curl -I https://xamxam.erg.be/src/Database.php
curl -I https://xamxam.erg.be/config/bootstrap.php
```
### Test Security Headers
```bash
curl -I https://xamxam.erg.be/ | grep -E "X-Frame|X-Content|Strict-Transport"
```
## 🔍 Troubleshooting
### Still Getting 403 Forbidden
**Check file permissions:**
```bash
ls -la /var/www/xamxam/public/index.php
groups www-data # Should include xamxam
```
### 502 Bad Gateway ### 502 Bad Gateway
**Check PHP-FPM:**
```bash ```bash
sudo systemctl status php8.4-fpm sudo systemctl status php8.4-fpm
sudo systemctl restart php8.4-fpm sudo systemctl restart php8.4-fpm
``` ```
### Admin Password Not Working ### Nginx config error
```bash ```bash
sudo htpasswd /etc/nginx/.htpasswd-xamxam admin
```
## 📊 Monitoring
```bash
# Watch logs
sudo tail -f /var/log/nginx/xamxam_access.log
sudo tail -f /var/log/nginx/xamxam_error.log
# Check status
sudo systemctl status nginx
```
## 🔒 Security Checklist
After deployment, verify:
- [ ] Public site accessible at https://xamxam.erg.be/
- [ ] Admin panel requires password
- [ ] Database files return 403 Forbidden
- [ ] Source files return 403 Forbidden
- [ ] Security headers present
- [ ] PHP-FPM running
## 🔄 Updating the Site
```bash
# Deploy code changes
just deploy
# Reload nginx if config changed
ssh xamxam "sudo systemctl reload nginx"
```
## 🆘 Emergency Recovery
```bash
# Restore default nginx config
ssh xamxam ssh xamxam
sudo rm /etc/nginx/sites-enabled/xamxam sudo nginx -t
sudo systemctl reload nginx
# Reset permissions
sudo chown -R www-data:xamxam /var/www/xamxam/
sudo find /var/www/posterg -type d -exec chmod 755 {} \;
sudo find /var/www/posterg -type f -exec chmod 644 {} \;
``` ```
--- ### Still getting 403 Forbidden
**See also:** Check ownership/group and that `www-data` is a member of the `xamxam` group:
- [QUICK_REFERENCE.md](QUICK_REFERENCE.md) - Command reference
- [ADMIN_USERS.md](ADMIN_USERS.md) - User management ```bash
- [SECURITY_HEADERS.md](SECURITY_HEADERS.md) - Security headers ls -la /var/www/xamxam/public/index.php
groups www-data # should include xamxam
```
If permissions are wrong, re-run the permission-fixing deploy script:
```bash
just deploy-nginx # re-applies deploy-server.sh (perms + config)
```
## See also
- [`QUICK_REFERENCE.md`](QUICK_REFERENCE.md) — command reference
- [`PHP_AUTH_LAYER.md`](PHP_AUTH_LAYER.md) — admin authentication
- [`SECURITY_HEADERS.md`](SECURITY_HEADERS.md) — security headers
- [`../../docs/deployment.md`](../../docs/deployment.md) — full deployment, backups, rollback
+78 -181
View File
@@ -1,41 +1,40 @@
# Nginx Quick Reference - Post-ERG # Nginx Quick Reference — XAMXAM
## Setup Commands Command reference for the XAMXAM nginx configuration.
## Deploy the config
```bash ```bash
# Copy nginx config # From your local machine: uploads nginx/xamxam.conf + scripts/deploy-server.sh,
sudo cp nginx/xamxam.conf /etc/nginx/sites-available/xamxam # installs the config, fixes permissions, validates (nginx -t) and reloads.
sudo ln -s /etc/nginx/sites-available/xamxam /etc/nginx/sites-enabled/ just deploy-nginx
```
Manual alternative (on the server, as root):
```bash
sudo cp /tmp/xamxam.conf /etc/nginx/sites-available/xamxam
sudo ln -sf /etc/nginx/sites-available/xamxam /etc/nginx/sites-enabled/xamxam
sudo rm -f /etc/nginx/sites-enabled/default sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
# Test and reload
sudo nginx -t
sudo systemctl reload nginx
``` ```
## Common Operations ## Admin authentication
### Password Management The admin panel is protected by the application's **PHP session auth**
(`src/AdminAuth.php`), **not** by nginx Basic-auth/htpasswd. There is no
`.htpasswd` file — manage the admin password in the admin panel at
`/admin/parametres` → Account tab. See [`PHP_AUTH_LAYER.md`](PHP_AUTH_LAYER.md).
To reset the password from the shell, store a bcrypt hash in the DB:
```bash ```bash
# Interactive menu (recommended) ssh xamxam
sudo bash /tmp/manage-admin-users.sh HASH=$(sudo -u www-data php -r "echo password_hash('NEWPASSWORD', PASSWORD_DEFAULT);")
# then insert into site_settings.admin_password_hash (see PHP_AUTH_LAYER.md)
# Or manual commands:
# Add new user
sudo htpasswd /etc/nginx/.htpasswd-xamxam username
# Change password for existing user
sudo htpasswd /etc/nginx/.htpasswd-xamxam username
# Remove user
sudo htpasswd -D /etc/nginx/.htpasswd-xamxam username
# List all users
sudo cut -d: -f1 /etc/nginx/.htpasswd-xamxam
``` ```
### Nginx Control ## Nginx control
```bash ```bash
# Test configuration # Test configuration
@@ -47,196 +46,94 @@ sudo systemctl reload nginx
# Restart nginx (brief downtime) # Restart nginx (brief downtime)
sudo systemctl restart nginx sudo systemctl restart nginx
# Stop nginx
sudo systemctl stop nginx
# Start nginx
sudo systemctl start nginx
# Check status # Check status
sudo systemctl status nginx sudo systemctl status nginx
``` ```
### View Logs ## Logs
The nginx config writes app-specific logs (paths set in the server block):
```bash ```bash
# Public site access log sudo tail -f /var/log/nginx/xamxam-nginx-access.log
sudo tail -f /var/log/nginx/xamxam_access.log sudo tail -f /var/log/nginx/xamxam-nginx-error.log
sudo tail -f /var/log/nginx/xamxam-ssl_access.log
# Public site errors
sudo tail -f /var/log/nginx/xamxam_error.log
# SSL access log
sudo tail -f /var/log/nginx/xamxam_ssl_access.log
# Search for specific pattern
sudo grep "404" /var/log/nginx/xamxam_access.log
# Count requests by IP
sudo awk '{print $1}' /var/log/nginx/xamxam_access.log | sort | uniq -c | sort -nr | head
``` ```
### SSL/HTTPS ## SSL / HTTPS
```bash SSL/TLS is terminated by an **upstream reverse proxy**; this nginx listens on
# Get SSL certificate (Let's Encrypt) HTTP (port 80) and `Strict-Transport-Security` is set by the config. No
sudo certbot --nginx -d xamxam.erg.be -d www.xamxam.erg.be Let's Encrypt/certbot step is needed here. (Only relevant if you later serve
TLS directly.)
# Renew certificates
sudo certbot renew
# Check certificate expiry
sudo certbot certificates
# Test auto-renewal
sudo certbot renew --dry-run
```
## Testing ## Testing
### Test Admin Authentication
```bash ```bash
# Should require password (returns 401) # Public site: expect 200
curl -I https://xamxam.erg.be/
# Admin: expect 200 (login page, PHP-layer auth) or 302 to /admin/login.php
curl -I https://xamxam.erg.be/admin/ curl -I https://xamxam.erg.be/admin/
# With authentication # File protection: expect 404/403
curl -u admin:password https://xamxam.erg.be/admin/
```
### Test Rate Limiting
```bash
# Should show increasing 429 responses after limit
for i in {1..50}; do
curl -s -o /dev/null -w "%{http_code}\n" https://xamxam.erg.be/
done
```
### Test File Protection
```bash
# Should return 403
curl -I https://xamxam.erg.be/storage/xamxam.db curl -I https://xamxam.erg.be/storage/xamxam.db
curl -I https://xamxam.erg.be/shared/Database.php curl -I https://xamxam.erg.be/src/Database.php
curl -I https://xamxam.erg.be/.env curl -I https://xamxam.erg.be/.env
``` ```
### Test Security Headers
```bash
# Check all security headers
curl -I https://xamxam.erg.be/ 2>&1 | grep -E "X-|Strict-Transport|Referrer|Permissions"
```
## Troubleshooting ## Troubleshooting
### Common Issues ### 502 Bad Gateway
**403 Forbidden on admin**
```bash ```bash
# Check htpasswd file exists # Check / restart PHP-FPM (8.4)
sudo ls -l /etc/nginx/.htpasswd-xamxam sudo systemctl status php8.4-fpm
sudo systemctl restart php8.4-fpm
# Check permissions sudo tail /var/log/php8.4-fpm.log
sudo chmod 644 /etc/nginx/.htpasswd-xamxam
``` ```
**502 Bad Gateway** ### Configuration errors
```bash ```bash
# Check PHP-FPM status
sudo systemctl status php8.2-fpm
# Restart PHP-FPM
sudo systemctl restart php8.2-fpm
# Check PHP-FPM logs
sudo tail /var/log/php8.2-fpm.log
```
**Configuration errors**
```bash
# Test config and show errors
sudo nginx -t sudo nginx -t
# Check nginx error log
sudo tail -50 /var/log/nginx/error.log sudo tail -50 /var/log/nginx/error.log
``` ```
### Emergency Recovery ### 403 Forbidden / permission issues
```bash The nginx config blocks sensitive paths by design (`.db`, `.env`, `src/`,
# Disable password protection temporarily `storage/`, `templates/`, etc.). If something legitimately 403s, check the file
sudo nano /etc/nginx/sites-available/xamxam ownership/group and that `www-data` is in the `xamxam` group, then re-apply
# Comment out these lines in /admin/ location: perms with `just deploy-nginx` (runs `deploy-server.sh`).
# auth_basic "Admin Access - Post-ERG";
# auth_basic_user_file /etc/nginx/.htpasswd-xamxam;
# Reload nginx ## Rate limits (current settings)
sudo nginx -t && sudo systemctl reload nginx
```
## Performance Monitoring | Zone | Rate |
|------|------|
| `general` | 30 r/min |
| `search` | 30 r/min |
| `admin` | 300 r/min (burst 30) |
```bash To adjust, edit the `limit_req_zone` / `limit_req` lines in `nginx/xamxam.conf`:
# Check active connections
sudo ss -tulpn | grep nginx
# Monitor nginx processes
watch -n 1 'ps aux | grep nginx'
# Check request rate
sudo tail -f /var/log/nginx/xamxam_access.log | pv -l -r > /dev/null
# Disk usage of logs
sudo du -sh /var/log/nginx/*
```
## Maintenance
```bash
# Rotate logs manually
sudo nginx -s reopen
# Clear old logs (keep last 7 days)
sudo find /var/log/nginx -name "*.log" -mtime +7 -delete
# Backup configuration
sudo cp /etc/nginx/sites-available/xamxam /etc/nginx/sites-available/xamxam.backup.$(date +%Y%m%d)
# Backup password file
sudo cp /etc/nginx/.htpasswd-xamxam /etc/nginx/.htpasswd-xamxam.backup.$(date +%Y%m%d)
```
## Security Checklist
- [ ] Admin password set: `sudo ls -l /etc/nginx/.htpasswd-xamxam`
- [ ] SSL enabled: `curl -I https://xamxam.erg.be/`
- [ ] Database blocked: `curl -I https://xamxam.erg.be/storage/xamxam.db`
- [ ] Shared directory blocked: `curl -I https://xamxam.erg.be/shared/Database.php`
- [ ] Rate limiting working: Test with curl loop
- [ ] Security headers present: `curl -I https://xamxam.erg.be/ | grep X-`
- [ ] Logs accessible: `sudo tail /var/log/nginx/xamxam_access.log`
## Configuration Paths
- **Nginx config**: `/etc/nginx/sites-available/xamxam`
- **Password file**: `/etc/nginx/.htpasswd-xamxam`
- **SSL certificates**: `/etc/letsencrypt/live/xamxam.erg.be/`
- **Access logs**: `/var/log/nginx/xamxam_access.log`
- **Error logs**: `/var/log/nginx/xamxam_error.log`
- **PHP-FPM config**: `/etc/php/8.2/fpm/pool.d/www.conf`
- **PHP-FPM socket**: `/var/run/php/php8.2-fpm.sock`
## Rate Limits (Current Settings)
- **General requests**: 30 requests/minute
- **Search endpoint**: 30 requests/minute (burst: 10)
- **Admin panel**: 10 requests/minute (burst: 5)
To adjust, edit these lines in nginx config:
```nginx ```nginx
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/m; limit_req_zone $binary_remote_addr zone=general:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=search:10m rate=30r/m; limit_req_zone $binary_remote_addr zone=search:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=admin:10m rate=10r/m; limit_req_zone $binary_remote_addr zone=admin:10m rate=300r/m;
``` ```
## Configuration paths
- **Nginx config**: `/etc/nginx/sites-available/xamxam` → `sites-enabled/xamxam`
- **PHP-FPM pool**: `/etc/php/8.4/fpm/pool.d/www.conf`
- **PHP-FPM socket**: `/var/run/php/php8.4-fpm.sock`
## Security checklist
- [ ] Admin password set (in `/admin/parametres`, by default set only once / fresh DB is open)
- [ ] Public site reachable: `curl -I https://xamxam.erg.be/`
- [ ] DB / source blocked: `curl -I https://xamxam.erg.be/storage/xamxam.db`
- [ ] Rate limiting working (curl loop yields 429 after limit)
- [ ] Security headers present: `curl -I https://xamxam.erg.be/ | grep X-`
- [ ] Logs exist: `sudo tail /var/log/nginx/xamxam-nginx-error.log`