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
This commit is contained in:
Théophile Gervreau-Mercier
2026-01-28 10:24:36 +01:00
parent 95f52d549e
commit 467aced734
81 changed files with 6304 additions and 785 deletions

247
nginx/QUICK_REFERENCE.md Normal file
View File

@@ -0,0 +1,247 @@
# Nginx Quick Reference - Post-ERG
## Setup Commands
```bash
# Make setup script executable
chmod +x nginx/setup-password.sh
# Run password setup (as root)
sudo ./nginx/setup-password.sh
# Copy nginx config
sudo cp nginx/posterg.conf /etc/nginx/sites-available/posterg
# Enable site
sudo ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload nginx
sudo systemctl reload nginx
```
## Common Operations
### Password Management
```bash
# Add new user
sudo htpasswd /etc/nginx/.htpasswd-posterg username
# Change password for existing user
sudo htpasswd /etc/nginx/.htpasswd-posterg username
# Remove user
sudo htpasswd -D /etc/nginx/.htpasswd-posterg username
# List all users
sudo cut -d: -f1 /etc/nginx/.htpasswd-posterg
```
### Nginx Control
```bash
# Test configuration
sudo nginx -t
# Reload configuration (no downtime)
sudo systemctl reload nginx
# Restart nginx (brief downtime)
sudo systemctl restart nginx
# Stop nginx
sudo systemctl stop nginx
# Start nginx
sudo systemctl start nginx
# Check status
sudo systemctl status nginx
```
### View Logs
```bash
# Public site access log
sudo tail -f /var/log/nginx/posterg_access.log
# Public site errors
sudo tail -f /var/log/nginx/posterg_error.log
# SSL access log
sudo tail -f /var/log/nginx/posterg_ssl_access.log
# Search for specific pattern
sudo grep "404" /var/log/nginx/posterg_access.log
# Count requests by IP
sudo awk '{print $1}' /var/log/nginx/posterg_access.log | sort | uniq -c | sort -nr | head
```
### SSL/HTTPS
```bash
# Get SSL certificate (Let's Encrypt)
sudo certbot --nginx -d posterg.erg.be -d www.posterg.erg.be
# Renew certificates
sudo certbot renew
# Check certificate expiry
sudo certbot certificates
# Test auto-renewal
sudo certbot renew --dry-run
```
## Testing
### Test Admin Authentication
```bash
# Should require password (returns 401)
curl -I https://posterg.erg.be/formulaire/
# With authentication
curl -u admin:password https://posterg.erg.be/formulaire/
```
### 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://posterg.erg.be/
done
```
### Test File Protection
```bash
# Should return 403
curl -I https://posterg.erg.be/database/posterg.db
curl -I https://posterg.erg.be/shared/Database.php
curl -I https://posterg.erg.be/.env
```
### Test Security Headers
```bash
# Check all security headers
curl -I https://posterg.erg.be/ 2>&1 | grep -E "X-|Strict-Transport|Referrer|Permissions"
```
## Troubleshooting
### Common Issues
**403 Forbidden on admin**
```bash
# Check htpasswd file exists
sudo ls -l /etc/nginx/.htpasswd-posterg
# Check permissions
sudo chmod 644 /etc/nginx/.htpasswd-posterg
```
**502 Bad Gateway**
```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
# Check nginx error log
sudo tail -50 /var/log/nginx/error.log
```
### Emergency Recovery
```bash
# Disable password protection temporarily
sudo nano /etc/nginx/sites-available/posterg
# Comment out these lines in /formulaire/ location:
# auth_basic "Admin Access - Post-ERG";
# auth_basic_user_file /etc/nginx/.htpasswd-posterg;
# Reload nginx
sudo nginx -t && sudo systemctl reload nginx
```
## Performance Monitoring
```bash
# 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/posterg_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/posterg /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d)
# Backup password file
sudo cp /etc/nginx/.htpasswd-posterg /etc/nginx/.htpasswd-posterg.backup.$(date +%Y%m%d)
```
## Security Checklist
- [ ] Admin password set: `sudo ls -l /etc/nginx/.htpasswd-posterg`
- [ ] SSL enabled: `curl -I https://posterg.erg.be/`
- [ ] Database blocked: `curl -I https://posterg.erg.be/database/posterg.db`
- [ ] Shared directory blocked: `curl -I https://posterg.erg.be/shared/Database.php`
- [ ] Rate limiting working: Test with curl loop
- [ ] Security headers present: `curl -I https://posterg.erg.be/ | grep X-`
- [ ] Logs accessible: `sudo tail /var/log/nginx/posterg_access.log`
## Configuration Paths
- **Nginx config**: `/etc/nginx/sites-available/posterg`
- **Password file**: `/etc/nginx/.htpasswd-posterg`
- **SSL certificates**: `/etc/letsencrypt/live/posterg.erg.be/`
- **Access logs**: `/var/log/nginx/posterg_access.log`
- **Error logs**: `/var/log/nginx/posterg_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
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=admin:10m rate=10r/m;
```

172
nginx/README.md Normal file
View File

@@ -0,0 +1,172 @@
# Nginx Configuration - Post-ERG
This directory contains nginx configuration and setup scripts for the Post-ERG thesis website.
## 📁 Files
- **`posterg.conf`** - Complete nginx configuration file
- **`setup-password.sh`** - Script to create admin passwords
- **`SETUP.md`** - Detailed setup instructions
- **`QUICK_REFERENCE.md`** - Command reference and troubleshooting
## 🚀 Quick Start
### 1. Set up admin password
```bash
# Make script executable
chmod +x nginx/setup-password.sh
# Run setup (as root on server)
sudo ./nginx/setup-password.sh
```
### 2. Deploy nginx configuration
```bash
# From your local machine
just deploy-nginx
# Then on the server:
sudo cp /tmp/posterg.conf /etc/nginx/sites-available/posterg
sudo ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### 3. Set up SSL (production)
```bash
# On server
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d posterg.erg.be -d www.posterg.erg.be
```
## 🔒 Security Features
### Admin Panel Protection
- **Password required** for `/formulaire/` (admin panel)
- HTTP Basic Authentication
- Rate limited: 10 requests/minute
### File Access Protection
- Database files (`.db`) - **BLOCKED**
- Sensitive files (`.md`, `.sql`, `.env`) - **BLOCKED**
- Shared directory - **BLOCKED**
- Tests directory - **BLOCKED**
- Cache directory - **BLOCKED**
- Hidden files (`.git`, etc.) - **BLOCKED**
### Rate Limiting
- General requests: 30/minute
- Search endpoint: 30/minute
- Admin panel: 10/minute
### Security Headers
- ✅ X-Frame-Options (clickjacking protection)
- ✅ X-Content-Type-Options (MIME sniffing protection)
- ✅ X-XSS-Protection (XSS filter)
- ✅ Strict-Transport-Security (force HTTPS)
- ✅ Referrer-Policy (referrer control)
- ✅ Permissions-Policy (disable browser features)
### SSL/TLS
- TLS 1.2 and 1.3 only
- Strong cipher suites
- OCSP stapling
- HSTS enabled
## 📚 Documentation
- **[SETUP.md](SETUP.md)** - Complete setup guide
- Installation steps
- Configuration details
- Testing procedures
- Troubleshooting
- Performance tuning
- Security checklist
- **[QUICK_REFERENCE.md](QUICK_REFERENCE.md)** - Command reference
- Common operations
- Password management
- Nginx control
- Log viewing
- Testing commands
- Troubleshooting
## 🧪 Testing
Test your configuration:
```bash
# Test admin authentication
curl -I https://posterg.erg.be/formulaire/
# Test file protection
curl -I https://posterg.erg.be/database/posterg.db
# Test security headers
curl -I https://posterg.erg.be/ | grep -E "X-|Strict-Transport"
```
## 🆘 Quick Help
### Admin can't log in
```bash
# Reset password
sudo htpasswd /etc/nginx/.htpasswd-posterg admin
```
### 502 Bad Gateway
```bash
# Check PHP-FPM
sudo systemctl status php8.2-fpm
sudo systemctl restart php8.2-fpm
```
### Configuration errors
```bash
# Test and show errors
sudo nginx -t
```
## 📊 Monitoring
```bash
# Watch access logs
sudo tail -f /var/log/nginx/posterg_access.log
# Watch error logs
sudo tail -f /var/log/nginx/posterg_error.log
# Check nginx status
sudo systemctl status nginx
```
## 🔄 Maintenance
### Change admin password
```bash
sudo htpasswd /etc/nginx/.htpasswd-posterg admin
```
### Reload after config changes
```bash
sudo nginx -t && sudo systemctl reload nginx
```
### Renew SSL certificate
```bash
sudo certbot renew
```
## 📞 Support
For detailed instructions, see:
- **SETUP.md** - Complete setup guide
- **QUICK_REFERENCE.md** - Command reference
For issues:
1. Check nginx error logs: `sudo tail /var/log/nginx/posterg_error.log`
2. Test configuration: `sudo nginx -t`
3. Check PHP-FPM: `sudo systemctl status php8.2-fpm`

369
nginx/SETUP.md Normal file
View File

@@ -0,0 +1,369 @@
# 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
```bash
# 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:
```bash
# 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
```bash
# 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:
```bash
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
```bash
# 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)
```bash
# 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:
```bash
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
```
Update these settings:
```ini
# 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:
```bash
sudo systemctl restart php8.2-fpm
```
### 8. Set Correct Permissions
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# Check security headers
curl -I https://posterg.erg.be/ | grep -E "X-Frame|X-Content|Strict-Transport"
```
## Monitoring and Logs
```bash
# 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
```bash
# 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
```bash
# Manual renewal
sudo certbot renew
# Check expiry
sudo certbot certificates
```
### Update Configuration
```bash
# After modifying config file
sudo nginx -t
sudo systemctl reload nginx
```
## Performance Tuning
### Enable Gzip Compression
Add to nginx config:
```nginx
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:
```nginx
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:
```bash
sudo apt install fail2ban
# Create jail for nginx
sudo nano /etc/fail2ban/jail.local
```
Add:
```ini
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/posterg_error.log
maxretry = 5
bantime = 3600
```
### Enable UFW Firewall
```bash
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.

283
nginx/posterg.conf Normal file
View File

@@ -0,0 +1,283 @@
# Nginx configuration for Post-ERG thesis website
# Place this in /etc/nginx/sites-available/posterg
# Then symlink: ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/
# Rate limiting zones
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=admin:10m rate=10r/m;
# Server block - HTTP (redirect to HTTPS in production)
server {
listen 80;
listen [::]:80;
server_name posterg.erg.be www.posterg.erg.be;
# Redirect all HTTP to HTTPS (uncomment in production)
# return 301 https://$server_name$request_uri;
# For development/testing, allow HTTP
root /var/www/html;
index index.php index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Disable server tokens
server_tokens off;
# Max upload size (for thesis files)
client_max_body_size 100M;
client_body_timeout 120s;
# Logging
access_log /var/log/nginx/posterg_access.log;
error_log /var/log/nginx/posterg_error.log warn;
# Block common attack patterns
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(git|env|db-journal)$ {
deny all;
access_log off;
log_not_found off;
}
# Deny access to sensitive files
location ~* \.(md|txt|sql|sh|json)$ {
deny all;
}
# Deny access to database files
location ~* \.db$ {
deny all;
}
# Deny access to shared/ directory (PHP includes only)
location /shared/ {
deny all;
}
# Deny access to tests directory
location /tests/ {
deny all;
}
# Deny access to cache directory
location /cache/ {
deny all;
}
# Admin panel - password protected
location /formulaire/ {
alias /var/www/html/formulaire/;
# HTTP Basic Authentication
auth_basic "Admin Access - Post-ERG";
auth_basic_user_file /etc/nginx/.htpasswd-posterg;
# Rate limiting for admin
limit_req zone=admin burst=5 nodelay;
# PHP handling
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
# Additional security for admin
add_header X-Robots-Tag "noindex, nofollow" always;
}
# Search endpoint - rate limiting
location /search.php {
limit_req zone=search burst=10 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
# Public PHP files
location ~ \.php$ {
limit_req zone=general burst=20 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
# Security parameters
fastcgi_param PHP_VALUE "upload_max_filesize=50M \n post_max_size=100M";
fastcgi_param PHP_ADMIN_VALUE "open_basedir=/var/www/html:/tmp";
# Timeouts
fastcgi_read_timeout 120;
fastcgi_send_timeout 120;
}
# Static files caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|otf)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Root location
location / {
try_files $uri $uri/ =404;
}
# Deny access to specific file types in data directories
location ~* /data/.*\.(php|sh|py)$ {
deny all;
}
}
# Server block - HTTPS (production)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name posterg.erg.be www.posterg.erg.be;
root /var/www/html;
index index.php index.html;
# SSL certificates (Let's Encrypt)
# Run: certbot --nginx -d posterg.erg.be -d www.posterg.erg.be
ssl_certificate /etc/letsencrypt/live/posterg.erg.be/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/posterg.erg.be/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# Security headers (HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Disable server tokens
server_tokens off;
# Max upload size
client_max_body_size 100M;
client_body_timeout 120s;
# Logging
access_log /var/log/nginx/posterg_ssl_access.log;
error_log /var/log/nginx/posterg_ssl_error.log warn;
# Block common attack patterns
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(git|env|db-journal)$ {
deny all;
access_log off;
log_not_found off;
}
# Deny access to sensitive files
location ~* \.(md|txt|sql|sh|json)$ {
deny all;
}
# Deny access to database files
location ~* \.db$ {
deny all;
}
# Deny access to shared/ directory
location /shared/ {
deny all;
}
# Deny access to tests directory
location /tests/ {
deny all;
}
# Deny access to cache directory
location /cache/ {
deny all;
}
# Admin panel - password protected
location /formulaire/ {
alias /var/www/html/formulaire/;
# HTTP Basic Authentication
auth_basic "Admin Access - Post-ERG";
auth_basic_user_file /etc/nginx/.htpasswd-posterg;
# Rate limiting
limit_req zone=admin burst=5 nodelay;
# PHP handling
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
# Security headers
add_header X-Robots-Tag "noindex, nofollow" always;
}
# Search endpoint - rate limiting
location /search.php {
limit_req zone=search burst=10 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
# Public PHP files
location ~ \.php$ {
limit_req zone=general burst=20 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
# Security parameters
fastcgi_param PHP_VALUE "upload_max_filesize=50M \n post_max_size=100M";
fastcgi_param PHP_ADMIN_VALUE "open_basedir=/var/www/html:/tmp";
# Timeouts
fastcgi_read_timeout 120;
fastcgi_send_timeout 120;
}
# Static files caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|otf)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Root location
location / {
try_files $uri $uri/ =404;
}
# Deny access to script files in data directories
location ~* /data/.*\.(php|sh|py)$ {
deny all;
}
}

111
nginx/setup-password.sh Executable file
View File

@@ -0,0 +1,111 @@
#!/bin/bash
#
# Setup script for Post-ERG admin password
# Creates htpasswd file for nginx basic authentication
#
set -e
echo "================================================="
echo "Post-ERG Admin Password Setup"
echo "================================================="
echo ""
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "⚠️ This script must be run as root (use sudo)"
exit 1
fi
# Check if apache2-utils is installed
if ! command -v htpasswd &> /dev/null; then
echo "📦 Installing apache2-utils..."
apt-get update
apt-get install -y apache2-utils
fi
# Configuration
HTPASSWD_FILE="/etc/nginx/.htpasswd-posterg"
BACKUP_FILE="/etc/nginx/.htpasswd-posterg.backup"
# Backup existing file if it exists
if [ -f "$HTPASSWD_FILE" ]; then
echo "📋 Backing up existing password file..."
cp "$HTPASSWD_FILE" "$BACKUP_FILE"
echo " Backup saved to: $BACKUP_FILE"
echo ""
fi
# Prompt for username
echo "Enter admin username (default: admin):"
read -r USERNAME
USERNAME=${USERNAME:-admin}
# Create or update password file
if [ -f "$HTPASSWD_FILE" ]; then
# File exists, update/add user
echo ""
echo "Creating/updating user: $USERNAME"
htpasswd "$HTPASSWD_FILE" "$USERNAME"
else
# Create new file
echo ""
echo "Creating new password file for user: $USERNAME"
htpasswd -c "$HTPASSWD_FILE" "$USERNAME"
fi
# Set correct permissions
chmod 644 "$HTPASSWD_FILE"
chown root:root "$HTPASSWD_FILE"
echo ""
echo "✅ Password file created/updated successfully!"
echo ""
echo "Details:"
echo " File: $HTPASSWD_FILE"
echo " User: $USERNAME"
echo " Permissions: 644 (readable by nginx)"
echo ""
# Ask if user wants to add more users
echo "Do you want to add another user? (y/n)"
read -r ADD_MORE
while [ "$ADD_MORE" = "y" ] || [ "$ADD_MORE" = "Y" ]; do
echo ""
echo "Enter username for additional user:"
read -r USERNAME
if [ -z "$USERNAME" ]; then
echo "❌ Username cannot be empty"
continue
fi
echo "Adding user: $USERNAME"
htpasswd "$HTPASSWD_FILE" "$USERNAME"
echo ""
echo "Add another user? (y/n)"
read -r ADD_MORE
done
echo ""
echo "================================================="
echo "Setup Complete!"
echo "================================================="
echo ""
echo "Current users in $HTPASSWD_FILE:"
cut -d: -f1 "$HTPASSWD_FILE" | while read -r user; do
echo " - $user"
done
echo ""
echo "Next steps:"
echo " 1. Copy nginx config: cp nginx/posterg.conf /etc/nginx/sites-available/posterg"
echo " 2. Enable site: ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/"
echo " 3. Test config: nginx -t"
echo " 4. Reload nginx: systemctl reload nginx"
echo ""
echo "The admin panel at /formulaire/ will now require authentication."
echo ""
echo "⚠️ IMPORTANT: Save these credentials securely!"
echo ""