Files
xamxam/nginx/SETUP.md
Théophile Gervreau-Mercier 467aced734 Restructure repository and implement secure search feature
Phase 1: Consolidate shared infrastructure
- Create shared/ directory for common code
- Consolidate Database.php from front-backend and formulaire into unified shared/Database.php
  - Smart path detection for test.db vs posterg.db
  - Secure search with wildcard escaping and input validation
  - Support both singleton and direct instantiation patterns
  - Full CRUD methods for admin functionality
- Move RateLimit.php to shared/ (30 requests/min)
- Update all require paths across apps to use shared/

Phase 2: Reorganize directory structure
- Rename front-backend/ → apps/public/
- Rename formulaire/ → apps/admin/
- Rename db/ → database/
- Update all file paths for new structure
- Create root .gitignore excluding databases, cache, logs

Implement secure search feature
- Add apps/public/search.php with full-text search across theses
- Search filters: query, year, orientation, AP program, keywords
- Security features:
  - SQL injection prevention (prepared statements)
  - Wildcard injection prevention (escape % and _)
  - Input validation (max 200 chars, year range 1900-2100)
  - Rate limiting (30 req/min per IP)
  - Pagination limited to 100 results/page
  - XSS protection (htmlspecialchars on output)

Add comprehensive test suite
- Create apps/public/tests/ with proper structure
  - tests/Integration/SearchTest.php - 12 search scenarios
  - tests/Security/SecurityTest.php - vulnerability testing
  - tests/Unit/RateLimitTest.php - rate limit behavior
- Create database/fixtures/CreateTestDatabase.php
- Add apps/public/run-tests.php test runner
- All tests passing (4/4 suites)

Update deployment configuration
- Rename justfile 'sync' recipe to 'deploy'
- Create deploy group with separate deploy-public and deploy-admin
- Add test-deploy recipe for test database
- Exclude *.db, tests/, cache/, *.md from production deploy
- Deploy shared/ to both public and admin locations

Stats: +4482 insertions, -654 deletions across 72 files
2026-02-02 18:53:58 +01:00

8.1 KiB

Nginx Setup for Post-ERG

This document explains how to set up nginx with security features and password protection for the admin panel.

Prerequisites

  • Ubuntu/Debian server with root access
  • Nginx installed
  • PHP-FPM installed (PHP 8.2 or later)
  • Domain name pointed to your server

Installation Steps

1. Install Required Packages

# Install nginx and apache2-utils (for htpasswd)
sudo apt update
sudo apt install nginx apache2-utils php8.2-fpm

# Install SSL certificate tool (optional, for HTTPS)
sudo apt install certbot python3-certbot-nginx

2. Create Password File for Admin Panel

Create a password-protected admin area:

# Create htpasswd file
sudo htpasswd -c /etc/nginx/.htpasswd-posterg admin

# You'll be prompted to enter a password
# Enter a strong password (e.g., generated with: openssl rand -base64 32)

# Add additional users (without -c flag)
sudo htpasswd /etc/nginx/.htpasswd-posterg supervisor

Important: Store the username and password securely!

3. Copy Nginx Configuration

# Copy the config file
sudo cp nginx/posterg.conf /etc/nginx/sites-available/posterg

# Update PHP-FPM socket path if needed (check your PHP version)
# Edit the file and change php8.2-fpm.sock to match your version
sudo nano /etc/nginx/sites-available/posterg

# Create symlink to enable the site
sudo ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/

# Remove default site (optional)
sudo rm /etc/nginx/sites-enabled/default

4. Update Domain Name

Edit the configuration to use your domain:

sudo nano /etc/nginx/sites-available/posterg

# Change these lines:
# server_name posterg.erg.be www.posterg.erg.be;
# to your actual domain name

5. Test and Reload Nginx

# Test configuration
sudo nginx -t

# If test passes, reload nginx
sudo systemctl reload nginx

# Check nginx status
sudo systemctl status nginx

6. Set Up SSL/HTTPS (Production)

# Get SSL certificate from Let's Encrypt
sudo certbot --nginx -d posterg.erg.be -d www.posterg.erg.be

# Follow the prompts
# Certbot will automatically update your nginx config

# Enable auto-renewal
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# Test renewal
sudo certbot renew --dry-run

7. Configure PHP-FPM

Optimize PHP-FPM for security and performance:

sudo nano /etc/php/8.2/fpm/pool.d/www.conf

Update these settings:

# Security
php_admin_value[open_basedir] = /var/www/html:/tmp
php_admin_flag[allow_url_fopen] = off

# Performance
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

# Uploads
php_value[upload_max_filesize] = 50M
php_value[post_max_size] = 100M
php_value[max_execution_time] = 120
php_value[max_input_time] = 120

Restart PHP-FPM:

sudo systemctl restart php8.2-fpm

8. Set Correct Permissions

# Set ownership
sudo chown -R www-data:www-data /var/www/html

# Set directory permissions
sudo find /var/www/html -type d -exec chmod 755 {} \;

# Set file permissions
sudo find /var/www/html -type f -exec chmod 644 {} \;

# Make upload directories writable
sudo chmod 775 /var/www/html/formulaire/data/theses
sudo chmod 775 /var/www/html/formulaire/data/covers

# Protect database
sudo chmod 600 /var/www/html/database/posterg.db
sudo chown www-data:www-data /var/www/html/database/posterg.db

Security Features Implemented

1. Admin Panel Password Protection

  • HTTP Basic Authentication on /formulaire/ path
  • Only authorized users can access admin panel

2. Rate Limiting

  • General requests: 30 requests/minute
  • Search endpoint: 30 requests/minute
  • Admin panel: 10 requests/minute

3. File Access Protection

  • .db files blocked
  • .md, .txt, .sql files blocked
  • shared/ directory blocked (PHP includes only)
  • tests/ directory blocked
  • cache/ directory blocked
  • Hidden files (.git, .env) blocked

4. Security Headers

  • X-Frame-Options: Prevent clickjacking
  • X-Content-Type-Options: Prevent MIME sniffing
  • X-XSS-Protection: Enable XSS filter
  • Strict-Transport-Security: Force HTTPS
  • Referrer-Policy: Control referrer information
  • Permissions-Policy: Disable unnecessary browser features

5. SSL/TLS Configuration

  • TLS 1.2 and 1.3 only
  • Strong cipher suites
  • OCSP stapling
  • HSTS enabled

6. PHP Security

  • open_basedir restriction
  • Upload size limits
  • Timeout limits
  • Server tokens disabled

Testing

Test Admin Password Protection

# Should prompt for password
curl -I https://posterg.erg.be/formulaire/

# With credentials
curl -u admin:your_password https://posterg.erg.be/formulaire/

Test Rate Limiting

# Make multiple rapid requests (should get 429 Too Many Requests after limit)
for i in {1..50}; do curl -I https://posterg.erg.be/ 2>&1 | grep HTTP; done

Test File Blocking

# Should return 403 Forbidden
curl -I https://posterg.erg.be/database/posterg.db
curl -I https://posterg.erg.be/shared/Database.php
curl -I https://posterg.erg.be/README.md

Test Security Headers

# Check security headers
curl -I https://posterg.erg.be/ | grep -E "X-Frame|X-Content|Strict-Transport"

Monitoring and Logs

# Watch access logs
sudo tail -f /var/log/nginx/posterg_access.log

# Watch error logs
sudo tail -f /var/log/nginx/posterg_error.log

# Watch SSL access logs
sudo tail -f /var/log/nginx/posterg_ssl_access.log

# Check PHP-FPM logs
sudo tail -f /var/log/php8.2-fpm.log

Troubleshooting

"403 Forbidden" on admin panel

  • Check htpasswd file exists: ls -l /etc/nginx/.htpasswd-posterg
  • Check file permissions: sudo chmod 644 /etc/nginx/.htpasswd-posterg
  • Check credentials are correct

"502 Bad Gateway"

  • Check PHP-FPM is running: sudo systemctl status php8.2-fpm
  • Check socket path in nginx config matches PHP-FPM config
  • Check PHP-FPM logs: sudo tail /var/log/php8.2-fpm.log

"File not found" errors

  • Check root path in nginx config
  • Check file permissions
  • Check PHP-FPM open_basedir setting

Rate limiting too strict

  • Adjust rate= values in nginx config
  • Adjust burst= values for each location

Maintenance

Change Admin Password

# Change password for existing user
sudo htpasswd /etc/nginx/.htpasswd-posterg admin

# Remove a user
sudo htpasswd -D /etc/nginx/.htpasswd-posterg old_user

Renew SSL Certificate

# Manual renewal
sudo certbot renew

# Check expiry
sudo certbot certificates

Update Configuration

# After modifying config file
sudo nginx -t
sudo systemctl reload nginx

Performance Tuning

Enable Gzip Compression

Add to nginx config:

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;

Enable FastCGI Cache

For high-traffic sites, add FastCGI caching:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=POSTERG:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

Security Checklist

  • Admin password set and secured
  • SSL/HTTPS enabled and working
  • Database files not accessible via web
  • Sensitive files (.md, .sql, .env) blocked
  • Rate limiting configured
  • Security headers enabled
  • PHP open_basedir configured
  • File permissions correct (644 for files, 755 for dirs)
  • Logs monitored regularly
  • Backups automated

Additional Hardening (Optional)

Install Fail2Ban

Protect against brute force attacks:

sudo apt install fail2ban

# Create jail for nginx
sudo nano /etc/fail2ban/jail.local

Add:

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/posterg_error.log
maxretry = 5
bantime = 3600

Enable UFW Firewall

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Database Encryption

Consider encrypting the SQLite database at rest using SQLCipher or dm-crypt/LUKS.