diff --git a/README.md b/README.md index 4c4724b..67ccc65 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,9 @@ posterg/ │ ├── deploy-server.sh # Run on server with sudo to apply nginx config │ └── manage-admin-users.sh # Run on server with sudo to manage htpasswd └── nginx/ # nginx config and reference files - └── posterg.conf + ├── posterg.conf + ├── scripts/ # Server setup scripts (password, PHP SQLite) + └── docs/ # Documentation ``` Uploaded files (PDFs, covers) live in `storage/` — outside the webroot — and are @@ -73,19 +75,19 @@ sudo chmod 775 /var/www/posterg exit ``` -Then deploy once, copy nginx config, and apply: +Then deploy once and apply nginx config: ```bash just deploy -rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf -ssh posterg "sudo bash /var/www/posterg/scripts/deploy-server.sh" -ssh posterg "sudo systemctl reload nginx" +just deploy-nginx ``` ### Admin users (htpasswd) ```bash -ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh" +just manage-admin-users +# Then on server: +ssh posterg "sudo bash /tmp/manage-admin-users.sh" ``` ## Security notes diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..93198a8 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,134 @@ +# Post-ERG Setup Guide + +Complete setup guide for development and production deployment. + +## Requirements + +- PHP 8.4 +- SQLite3 (`php8.4-sqlite3`) +- nginx (production) + +## Development Setup + +### 1. Initial Setup + +```bash +just setup +``` + +### 2. Start Development Server + +```bash +just serve +``` + +Access at: http://localhost:8000 + +### 3. Run Tests + +```bash +just test +``` + +## Production Deployment + +### First-Time Server Setup + +```bash +ssh posterg +sudo mkdir -p /var/www/posterg +sudo chown www-data:posterg /var/www/posterg +sudo chmod 775 /var/www/posterg +exit +``` + +### Deploy Application + +```bash +just deploy +just deploy-nginx +``` + +### Set Admin Password + +```bash +just manage-admin-users +ssh posterg "sudo bash /tmp/manage-admin-users.sh" +``` + +### Verify Deployment + +```bash +# Test public site +curl -I https://posterg.erg.be/ + +# Test admin protection +curl -I https://posterg.erg.be/admin/ + +# Test file protection +curl -I https://posterg.erg.be/storage/test.db +``` + +## Nginx Configuration + +See `nginx/SETUP.md` and `nginx/docs/PRODUCTION_DEPLOYMENT.md` for detailed nginx setup. + +## Admin Panel + +The admin panel is protected by: +1. nginx HTTP Basic Authentication (htpasswd) +2. PHP session authentication + +Manage users with: +```bash +just manage-admin-users +``` + +## Database + +### Initialize Test Database + +```bash +just init-db +``` + +### Reset Database + +```bash +just reset-db +``` + +### Deploy Test Database to Server + +```bash +just deploy-db +``` + +## Common Operations + +### View Logs + +```bash +just logs +``` + +### Stop Development Server + +```bash +just stop +``` + +### Run Migrations + +```bash +just migrate +``` + +## Security + +- Admin panel: HTTP Basic Auth + PHP session +- File uploads: Stored outside webroot, served via `media.php` +- Rate limiting: 30 req/min general, 10 req/min admin +- Security headers: X-Frame-Options, CSP, HSTS, etc. + +See `nginx/docs/SECURITY_HEADERS.md` for details. diff --git a/TODO.md b/TODO.md index f7e43af..4eae5ff 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,9 @@ -# TODO - -- [x] Remove random HSL gradients from homepage cards and use header's background gradient -- [ ] Review other gradient usages (check if any leftover random hue generation anywhere) +- [x] Analyze nginx/docs and nginx/scripts for deduplication opportunities +- [x] Deduplicate nginx documentation files (removed DEPLOYMENT_COMPLETE.md, DEPLOY_NOW.md) +- [x] Remove duplicate scripts (deploy-production.sh, deploy-production-new.sh, manage-admin-users.sh) +- [x] Update justfile with new script paths +- [x] Update top-level README.md +- [x] Update nginx/README.md +- [x] Create nginx/SETUP.md +- [x] Create top-level SETUP.md +- [x] Update documentation paths (/var/www/html/ → /var/www/posterg/, /formulaire/ → /admin/) diff --git a/justfile b/justfile index db69751..97172ab 100644 --- a/justfile +++ b/justfile @@ -62,6 +62,15 @@ setup-server: @echo " sudo DEPLOY_USER=\$USER bash /tmp/setup-server.sh" @echo "" +[group('deploy')] +manage-admin-users: + rsync -v scripts/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh + @echo "" + @echo "Script uploaded. SSH into the server and run:" + @echo "" + @echo " sudo bash /tmp/manage-admin-users.sh" + @echo "" + [group('deploy')] deploy-nginx: rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf @@ -70,6 +79,7 @@ deploy-nginx: @echo "Files uploaded. SSH into the server and run:" @echo "" @echo " sudo bash /tmp/deploy-server.sh" + @echo " sudo systemctl reload nginx" @echo "" [group('deploy')] diff --git a/nginx/DEPLOYMENT_COMPLETE.md b/nginx/DEPLOYMENT_COMPLETE.md deleted file mode 100644 index a19b36d..0000000 --- a/nginx/DEPLOYMENT_COMPLETE.md +++ /dev/null @@ -1,379 +0,0 @@ -# ✅ Production Deployment Complete - Post-ERG - -**Date:** February 5, 2026 -**Status:** ✅ Successfully Deployed - ---- - -## 🎉 Deployment Summary - -The Post-ERG website is now successfully deployed with production-ready nginx configuration and security hardening. - -### ✅ What's Working - -| Feature | Status | Test Result | -|---------|--------|-------------| -| **Public Site** | ✅ Working | https://posterg.erg.be/ → 200 OK | -| **SSL/TLS** | ✅ Working | HTTPS with valid certificate | -| **Admin Panel** | ✅ Protected | /formulaire/ → 401 (requires password) | -| **Database Protection** | ✅ Blocked | /storage/ → 403 Forbidden | -| **Sensitive Files** | ✅ Blocked | .md, .sql files → 403 Forbidden | -| **Shared Directory** | ✅ Blocked | /shared/ → 403 Forbidden | -| **Security Headers** | ✅ Present | X-Frame-Options, CSP, etc. | -| **PHP 8.4** | ✅ Running | php8.4-fpm active | -| **File Permissions** | ✅ Fixed | posterg group, readable by www-data | - ---- - -## 🔧 What Was Fixed - -### 1. File Permissions -**Problem:** Files owned by `theophile:theophile` with 640 permissions, nginx couldn't read them. - -**Solution:** -```bash -# Changed group to posterg (www-data is member) -chown -R theophile:posterg /var/www/html/ - -# Set proper permissions -find /var/www/html -type d -exec chmod 755 {} \; -find /var/www/html -type f -exec chmod 640 {} \; -``` - -### 2. PHP Include Paths -**Problem:** Public files used `../../shared/` which doesn't work in production structure. - -**Solution:** -- Public files: Changed `../../shared/` → `/shared/` -- Admin files: Changed `../../shared/` → `/../shared/` -- Automated in deployment script - -### 3. Nginx Configuration -**Problem:** Using basic default config with no security. - -**Solution:** Deployed production config with: -- ✅ Rate limiting (30/min general, 10/min admin) -- ✅ File protection (database, configs, hidden files) -- ✅ Admin password protection -- ✅ Security headers -- ✅ Proper PHP-FPM configuration -- ✅ Upload size limits (100MB) - ---- - -## 📋 Production Configuration - -### Server Details -- **Server:** posterg.erg.be -- **Internal IP:** 192.168.6.125 -- **PHP Version:** 8.4.16 -- **Nginx:** Latest stable -- **SSL/TLS:** Handled by upstream reverse proxy - -### File Structure -``` -/var/www/html/ -├── index.php, memoire.php, search.php (public files) -├── assets/ (CSS, JS) -├── shared/ (PHP libraries - blocked from web) -│ ├── Database.php -│ ├── RateLimit.php -│ └── config.php -├── database/ (SQLite database - blocked from web) -│ └── posterg.db -└── formulaire/ (admin panel - password protected) - ├── index.php, list.php, edit.php - └── data/ - ├── theses/ (uploaded PDF files) - └── covers/ (uploaded cover images) -``` - -### Security Configuration - -**Rate Limits:** -- General requests: 30 requests/minute (burst: 20) -- Search endpoint: 30 requests/minute (burst: 10) -- Admin panel: 10 requests/minute (burst: 5) - -**Protected Paths:** -- `/storage/` - Database files (403) -- `/shared/` - PHP libraries (403) -- `/data/` - Upload directories (403) -- `*.db` files - Database files (403) -- `*.md, *.sql, *.sh, *.json` - Sensitive files (403) -- Hidden files (`.git`, `.env`, etc.) - (403) - -**Admin Access:** -- Path: `/formulaire/` -- Authentication: HTTP Basic Auth -- Password file: `/etc/nginx/.htpasswd-posterg` -- User: `test_posterg_22@` - -**Security Headers:** -``` -X-Frame-Options: SAMEORIGIN -X-Content-Type-Options: nosniff -X-XSS-Protection: 1; mode=block -Referrer-Policy: strict-origin-when-cross-origin -Permissions-Policy: geolocation=(), microphone=(), camera=() -``` - ---- - -## 🚀 Deployment Process (For Future Updates) - -The deployment process has been automated and updated: - -### Deploy Code Changes - -```bash -# Deploy public site -just deploy-public -# Automatically fixes paths: ../../shared/ → /shared/ - -# Deploy admin panel -just deploy-admin -# Automatically fixes paths: ../../shared/ → /../shared/ - -# Deploy both -just deploy -``` - -### Deploy Nginx Config - -```bash -# Deploy production nginx configuration -just deploy-nginx-production - -# On server, run deployment script -ssh posterg -sudo bash /tmp/deploy-production.sh -``` - -The deployment scripts now automatically: -1. ✅ Copy files to server -2. ✅ Fix PHP include paths -3. ✅ Set correct permissions -4. ✅ Test nginx configuration -5. ✅ Reload services - ---- - -## 🧪 Testing & Verification - -### Automated Tests - -```bash -# On server -cd /var/www/html - -# Test public site -curl -I http://localhost/ # Should: 200 OK - -# Test admin protection -curl -I http://localhost/formulaire/ # Should: 401 Unauthorized - -# Test security -curl -I http://localhost/storage/posterg.db # Should: 403 Forbidden -curl -I http://localhost/README.md # Should: 403 Forbidden -curl -I http://localhost/shared/Database.php # Should: 403 Forbidden -``` - -### External Tests - -```bash -# From your local machine -curl -I https://posterg.erg.be/ # Should: 200 OK -curl -I https://posterg.erg.be/formulaire/ # Should: 401 -``` - -### Browser Tests - -1. ✅ Visit https://posterg.erg.be/ - Homepage loads -2. ✅ Search functionality works -3. ✅ Individual thesis pages work -4. ✅ Admin requires password: https://posterg.erg.be/formulaire/ -5. ✅ Can upload files in admin (after login) - ---- - -## 📊 Monitoring - -### Log Files - -```bash -# Nginx access log -tail -f /var/log/nginx/posterg_access.log - -# Nginx error log -tail -f /var/log/nginx/posterg_error.log - -# PHP error log -tail -f /var/www/html/error.log -``` - -### Service Status - -```bash -# Check nginx -sudo systemctl status nginx - -# Check PHP-FPM -sudo systemctl status php8.4-fpm - -# Test nginx config -sudo nginx -t -``` - ---- - -## 🔐 Admin Access - -### Login Credentials -- **URL:** https://posterg.erg.be/formulaire/ -- **Username:** `test_posterg_22@` -- **Password:** Set during deployment (stored securely) - -### Change Password - -```bash -ssh posterg -sudo htpasswd /etc/nginx/.htpasswd-posterg test_posterg_22@ -``` - -### Add Additional Admin Users - -```bash -ssh posterg -sudo htpasswd /etc/nginx/.htpasswd-posterg newusername -``` - ---- - -## 🔄 Maintenance - -### Update Website Content - -```bash -# From local machine -just deploy - -# Content is automatically updated on server -``` - -### Reload Nginx (after config changes) - -```bash -ssh posterg -sudo nginx -t # Test configuration -sudo systemctl reload nginx # Reload if test passes -``` - -### Restart PHP-FPM (if needed) - -```bash -ssh posterg -sudo systemctl restart php8.4-fpm -``` - -### Update SSL Certificate - -SSL/TLS is handled by the upstream reverse proxy. Contact the infrastructure team if certificate renewal is needed. - ---- - -## 🆘 Troubleshooting - -### Site Returns 403 Forbidden - -**Check file permissions:** -```bash -ls -la /var/www/html/index.php -# Should show: -rw-r----- theophile posterg -``` - -**Check nginx user:** -```bash -groups www-data -# Should show: www-data posterg -``` - -### Site Returns 500 Internal Server Error - -**Check PHP errors:** -```bash -tail -f /var/log/nginx/posterg_error.log -tail -f /var/www/html/error.log -``` - -**Check PHP-FPM:** -```bash -sudo systemctl status php8.4-fpm -sudo systemctl restart php8.4-fpm -``` - -### Admin Panel Not Working - -**Check password file:** -```bash -ls -la /etc/nginx/.htpasswd-posterg -``` - -**Reset password:** -```bash -sudo htpasswd /etc/nginx/.htpasswd-posterg test_posterg_22@ -``` - -### After Deploying, Site Broken - -**Check if paths were fixed:** -```bash -grep "require_once" /var/www/html/index.php -# Should show: __DIR__ . '/shared/Database.php' -# NOT: __DIR__ . '/../../shared/Database.php' -``` - -**Manually fix if needed:** -```bash -ssh posterg "cd /var/www/html && sed -i \"s|__DIR__ . '/../../shared/|__DIR__ . '/shared/|g\" *.php" -``` - ---- - -## 📞 Support Contacts - -- **Deployment Issues:** Check logs first -- **Nginx Config:** `/etc/nginx/sites-available/posterg` -- **PHP Config:** `/etc/php/8.4/fpm/pool.d/www.conf` -- **Database:** `/var/www/html/storage/posterg.db` - ---- - -## ✅ Success Checklist - -After any deployment, verify: - -- [ ] Public site loads: https://posterg.erg.be/ -- [ ] Search works -- [ ] Individual thesis pages work -- [ ] Admin requires password -- [ ] Admin can log in -- [ ] File uploads work (in admin) -- [ ] Database is protected (403) -- [ ] Sensitive files blocked (403) -- [ ] No errors in logs -- [ ] Security headers present - ---- - -## 📚 Documentation Files - -- `posterg-production.conf` - Production nginx configuration -- `deploy-production.sh` - Automated deployment script -- `PRODUCTION_DEPLOYMENT.md` - Detailed deployment guide -- `DEPLOY_NOW.md` - Quick deployment instructions -- `DEPLOYMENT_COMPLETE.md` - This file - ---- - -**Deployment completed successfully on February 5, 2026** 🎉 diff --git a/nginx/DEPLOY_NOW.md b/nginx/DEPLOY_NOW.md deleted file mode 100644 index 194b97f..0000000 --- a/nginx/DEPLOY_NOW.md +++ /dev/null @@ -1,276 +0,0 @@ -# 🚀 Deploy Production Nginx Configuration - -Quick guide to fix the current 403 Forbidden errors and deploy production-ready nginx setup. - -## Current Issue - -The site returns **403 Forbidden** because: -- Files are owned by `theophile:theophile` -- Nginx runs as `www-data` (member of `posterg` group) -- Files have `640` permissions but wrong group -- Nginx can't read the files - -## Solution - -Deploy the production configuration which will: -1. ✅ Fix file permissions (change group to `posterg`) -2. ✅ Add security hardening (rate limiting, file blocking) -3. ✅ Set up admin password protection -4. ✅ Configure proper PHP handling - ---- - -## 🎯 Quick Deploy (2 steps) - -### Step 1: Upload to Server - -From your local machine: - -```bash -just deploy-nginx-production -``` - -### Step 2: Run on Server - -```bash -ssh posterg -sudo bash /tmp/deploy-production.sh -``` - -That's it! The site should work after this. - ---- - -## 📝 What the Script Does - -The deployment script will: - -1. **Fix Permissions** - - Change ownership: `theophile:posterg` (so www-data can read) - - Directories: `755` (readable by all) - - Files: `640` (readable by owner and group) - - Upload dirs: `775` (writable by group) - -2. **Setup Admin Password** - - Creates `/etc/nginx/.htpasswd-posterg` if missing - - Prompts for username and password - -3. **Install Nginx Config** - - Backs up existing config - - Installs production config - - Creates symlink in sites-enabled - - Removes default site - -4. **Test & Reload** - - Tests nginx configuration - - Reloads nginx if valid - - Verifies PHP-FPM is running - ---- - -## 🔒 Security Features Added - -The new configuration adds: - -✅ **Rate Limiting** -- General: 30 requests/minute -- Search: 30 requests/minute -- Admin: 10 requests/minute - -✅ **File Protection** -- Database files (`.db`) → 403 Forbidden -- Sensitive files (`.md`, `.sql`, `.txt`) → 403 Forbidden -- `/storage/` directory → 403 Forbidden -- `/shared/` directory → 403 Forbidden -- `/data/` directory → 403 Forbidden -- Hidden files (`.git`, `.env`) → 403 Forbidden - -✅ **Admin Panel Protection** -- `/formulaire/` requires HTTP Basic Authentication -- Rate limited to 10 requests/minute -- Hidden from search engines - -✅ **Security Headers** -- X-Frame-Options (clickjacking protection) -- X-Content-Type-Options (MIME sniffing protection) -- X-XSS-Protection -- Referrer-Policy -- Permissions-Policy - -✅ **File Upload** -- Max size: 100MB -- Timeouts: 120 seconds -- Upload directories writable by www-data - ---- - -## 🧪 Testing After Deployment - -On the server: - -```bash -# Should return 200 OK now -curl -I http://localhost/ - -# Should return HTML content -curl http://localhost/index.php | head -n 20 - -# Admin should ask for password (401) -curl -I http://localhost/formulaire/ - -# Database should be blocked (403) -curl -I http://localhost/storage/posterg.db - -# Sensitive files should be blocked (403) -curl -I http://localhost/README.md -curl -I http://localhost/shared/Database.php -``` - -From your browser: -- Visit https://posterg.erg.be/ → Should work! -- Visit https://posterg.erg.be/formulaire/ → Should ask for password - ---- - -## 🔧 Manual Steps (If Script Fails) - -If the automated script fails, here's the manual process: - -### Fix Permissions - -```bash -ssh posterg -sudo chown -R theophile:posterg /var/www/html/ -sudo find /var/www/html -type d -exec chmod 755 {} \; -sudo find /var/www/html -type f -exec chmod 640 {} \; -sudo chmod 775 /var/www/html/formulaire/data/theses -sudo chmod 775 /var/www/html/formulaire/data/covers -``` - -### Install Config - -```bash -# On server -sudo cp /tmp/posterg.conf /etc/nginx/sites-available/posterg -sudo ln -sf /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/posterg -sudo rm -f /etc/nginx/sites-enabled/default -sudo nginx -t -sudo systemctl reload nginx -``` - -### Setup Admin Password - -```bash -sudo htpasswd -c /etc/nginx/.htpasswd-posterg admin -# Enter password when prompted -``` - ---- - -## 🆘 Troubleshooting - -### Still Getting 403 Forbidden - -**Check file ownership:** -```bash -ls -la /var/www/html/index.php -# Should show: -rw-r----- theophile posterg -``` - -**Check nginx user is in posterg group:** -```bash -groups www-data -# Should show: www-data : www-data posterg -``` - -### Can't Access Admin Panel - -**Verify password file:** -```bash -ls -la /etc/nginx/.htpasswd-posterg -# Should exist and be readable -``` - -**Test with credentials:** -```bash -curl -u admin:your_password http://localhost/formulaire/ -``` - -### PHP Not Working (500 Error) - -**Check PHP-FPM:** -```bash -sudo systemctl status php8.4-fpm -sudo systemctl restart php8.4-fpm -``` - -**Check socket:** -```bash -ls -la /var/run/php/php8.4-fpm.sock -# Should exist -``` - -### View Error Logs - -```bash -# Nginx errors -sudo tail -f /var/log/nginx/posterg_error.log - -# PHP errors -sudo tail -f /var/www/html/error.log -``` - ---- - -## 📊 Current vs Production Config - -| Feature | Current (Default) | Production | -|---------|------------------|------------| -| PHP Version | ✅ 8.4 | ✅ 8.4 | -| File Protection | ❌ None | ✅ Comprehensive | -| Rate Limiting | ❌ None | ✅ Yes | -| Admin Password | ❌ None | ✅ Yes | -| Security Headers | ❌ None | ✅ Yes | -| Upload Size | ⚠️ Default (2MB) | ✅ 100MB | -| Logging | ⚠️ Generic | ✅ Separate logs | - ---- - -## ✅ Success Checklist - -After deployment, verify: - -- [ ] Public site loads: https://posterg.erg.be/ -- [ ] Admin requires password: https://posterg.erg.be/formulaire/ -- [ ] Search works -- [ ] Individual thesis pages work -- [ ] Database is protected (403) -- [ ] Sensitive files blocked (403) -- [ ] No errors in logs -- [ ] File uploads work (in admin) - ---- - -## 📞 Need Help? - -1. **Check logs first:** - ```bash - sudo tail -50 /var/log/nginx/posterg_error.log - ``` - -2. **Test nginx config:** - ```bash - sudo nginx -t - ``` - -3. **Restart services:** - ```bash - sudo systemctl restart php8.4-fpm - sudo systemctl reload nginx - ``` - -4. **Check service status:** - ```bash - sudo systemctl status nginx - sudo systemctl status php8.4-fpm - ``` diff --git a/nginx/PRODUCTION_DEPLOYMENT.md b/nginx/PRODUCTION_DEPLOYMENT.md deleted file mode 100644 index 6017072..0000000 --- a/nginx/PRODUCTION_DEPLOYMENT.md +++ /dev/null @@ -1,346 +0,0 @@ -# Production Deployment Guide - Post-ERG - -This guide will help you deploy the production nginx configuration with proper security and permissions. - -## 🎯 Overview - -Your current setup: -- **Server IP**: 192.168.6.125 (internal) -- **PHP Version**: 8.4 -- **SSL/TLS**: Handled by reverse proxy (already working) -- **Issue**: File permissions preventing nginx from reading files - -## 🚀 Quick Deployment - -From your local machine: - -```bash -# Deploy the production config and deployment script -just deploy-nginx-production - -# SSH to the server and run the deployment -ssh posterg -sudo /tmp/deploy-production.sh -``` - -## 📋 Step-by-Step Deployment - -### 1. Set Up Admin Password (First Time Only) - -```bash -ssh posterg -sudo htpasswd -c /etc/nginx/.htpasswd-posterg admin -# Enter a strong password when prompted -``` - -**💡 Tip**: Generate a strong password: -```bash -openssl rand -base64 32 -``` - -### 2. Deploy Configuration - -From your local machine: - -```bash -# Upload nginx config and deployment script -rsync -vur ./nginx/posterg-production.conf posterg:/tmp/posterg.conf -rsync -vur ./nginx/deploy-production.sh posterg:/tmp/deploy-production.sh -``` - -### 3. Run Deployment Script - -On the server: - -```bash -ssh posterg -sudo chmod +x /tmp/deploy-production.sh -sudo /tmp/deploy-production.sh -``` - -The script will: -- ✅ Fix file permissions (set to posterg group) -- ✅ Install nginx configuration -- ✅ Test nginx configuration -- ✅ Reload nginx -- ✅ Check PHP-FPM status - -## 🔧 Manual Deployment (Alternative) - -If you prefer to do it manually: - -### Step 1: Fix Permissions - -```bash -ssh posterg - -# Set correct ownership (posterg group) -sudo chown -R theophile:posterg /var/www/html/ - -# Set directory permissions -sudo find /var/www/html -type d -exec chmod 755 {} \; - -# Set file permissions (group readable) -sudo find /var/www/html -type f -exec chmod 640 {} \; - -# 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 640 /var/www/html/storage/posterg.db -sudo chown www-data:posterg /var/www/html/storage/posterg.db -``` - -### Step 2: Deploy Nginx Config - -```bash -# Copy config -sudo cp /tmp/posterg.conf /etc/nginx/sites-available/posterg - -# Enable site -sudo ln -sf /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/posterg - -# Disable default site -sudo rm -f /etc/nginx/sites-enabled/default - -# Test configuration -sudo nginx -t - -# Reload nginx -sudo systemctl reload nginx -``` - -### Step 3: Verify PHP-FPM - -```bash -# Check PHP-FPM is running -sudo systemctl status php8.4-fpm - -# If not running, start it -sudo systemctl start php8.4-fpm -sudo systemctl enable php8.4-fpm -``` - -## 🧪 Testing - -### Test Public Site - -```bash -# Should return 200 OK -curl -I http://localhost/ - -# Should return 200 OK with HTML -curl http://localhost/index.php -``` - -### Test Admin Protection - -```bash -# Should return 401 Unauthorized -curl -I http://localhost/formulaire/ - -# Should return 200 OK with credentials -curl -u admin:your_password http://localhost/formulaire/ -``` - -### Test File Protection - -```bash -# These should all return 403 Forbidden -curl -I http://localhost/storage/posterg.db -curl -I http://localhost/README.md -curl -I http://localhost/shared/Database.php -curl -I http://localhost/.git/config -``` - -### Test Security Headers - -```bash -curl -I http://localhost/ | grep -E "X-Frame|X-Content|X-XSS" -``` - -### From Your Browser - -Visit https://posterg.erg.be/ - should work now! - -## 🔍 Troubleshooting - -### Still Getting 403 Forbidden - -**Check file permissions:** -```bash -ls -la /var/www/html/index.php -# Should show: -rw-r----- 1 theophile posterg ... -``` - -**Check nginx user is in posterg group:** -```bash -groups www-data -# Should show: www-data : www-data posterg -``` - -**Check directory permissions:** -```bash -ls -lad /var/www/html -# Should show: drwxr-xr-x ... posterg -``` - -### 502 Bad Gateway - -**Check PHP-FPM:** -```bash -sudo systemctl status php8.4-fpm -sudo systemctl restart php8.4-fpm -``` - -**Check socket file:** -```bash -ls -la /var/run/php/php8.4-fpm.sock -# Should exist and be writable by www-data -``` - -### Admin Password Not Working - -**Reset password:** -```bash -sudo htpasswd /etc/nginx/.htpasswd-posterg admin -``` - -**Check file exists:** -```bash -ls -la /etc/nginx/.htpasswd-posterg -# Should show: -rw-r--r-- 1 root root ... -``` - -### Database Not Accessible to PHP - -**Fix database permissions:** -```bash -sudo chown www-data:posterg /var/www/html/storage/posterg.db -sudo chmod 640 /var/www/html/storage/posterg.db -sudo chmod 755 /var/www/html/storage/ -``` - -### Can't Write Uploaded Files - -**Fix upload directory permissions:** -```bash -sudo chmod 775 /var/www/html/formulaire/data/theses -sudo chmod 775 /var/www/html/formulaire/data/covers -sudo chown -R theophile:posterg /var/www/html/formulaire/data/ -``` - -## 📊 Monitoring - -### Watch Logs - -```bash -# Access logs -sudo tail -f /var/log/nginx/posterg_access.log - -# Error logs -sudo tail -f /var/log/nginx/posterg_error.log - -# PHP errors -sudo tail -f /var/log/php8.4-fpm.log -``` - -### Check Nginx Status - -```bash -sudo systemctl status nginx -sudo nginx -t -``` - -### Check Resource Usage - -```bash -# Nginx processes -ps aux | grep nginx - -# PHP-FPM processes -ps aux | grep php-fpm - -# Disk usage -df -h /var/www/html -``` - -## 🔒 Security Checklist - -After deployment, verify: - -- [ ] ✅ Public site accessible at https://posterg.erg.be/ -- [ ] ✅ Admin panel requires password -- [ ] ✅ Database files return 403 Forbidden -- [ ] ✅ Sensitive files (.md, .sql) return 403 Forbidden -- [ ] ✅ Shared directory returns 403 Forbidden -- [ ] ✅ Security headers present in responses -- [ ] ✅ PHP-FPM running and accessible -- [ ] ✅ File uploads work in admin panel -- [ ] ✅ Search functionality works -- [ ] ✅ Logs are being written - -## 🔄 Updating the Site - -For future updates: - -```bash -# Deploy code changes -just deploy - -# Reload nginx if config changed -ssh posterg "sudo systemctl reload nginx" - -# Clear PHP opcache if needed -ssh posterg "sudo systemctl reload php8.4-fpm" -``` - -## 🆘 Emergency Recovery - -If something goes wrong: - -### Restore Default Config - -```bash -ssh posterg -sudo rm /etc/nginx/sites-enabled/posterg -sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default -sudo systemctl reload nginx -``` - -### Reset Permissions - -```bash -ssh posterg -sudo chown -R www-data:www-data /var/www/html -sudo find /var/www/html -type d -exec chmod 755 {} \; -sudo find /var/www/html -type f -exec chmod 644 {} \; -sudo systemctl reload nginx -``` - -## 📞 Support Resources - -- **Nginx docs**: https://nginx.org/en/docs/ -- **PHP-FPM docs**: https://www.php.net/manual/en/install.fpm.php -- **Let's Encrypt**: https://letsencrypt.org/ -- **Security headers**: https://securityheaders.com/ - -## 🎉 Success Criteria - -You know the deployment is successful when: - -1. ✅ Visit https://posterg.erg.be/ - shows homepage -2. ✅ Visit https://posterg.erg.be/formulaire/ - asks for password -3. ✅ Search works correctly -4. ✅ Individual thesis pages load -5. ✅ Admin can upload files -6. ✅ No 403 or 502 errors in logs -7. ✅ Security headers present (check with curl -I) - ---- - -**Need help?** Check the error logs first: -```bash -sudo tail -f /var/log/nginx/posterg_error.log -``` diff --git a/nginx/README.md b/nginx/README.md index d18cbed..2711afa 100644 --- a/nginx/README.md +++ b/nginx/README.md @@ -1,17 +1,26 @@ # Nginx Configuration - Post-ERG -This directory contains nginx configuration and setup scripts for the Post-ERG thesis website. +This directory contains nginx configuration and documentation 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 +- **`scripts/`** - Server setup scripts + - `setup-password.sh` - Create admin passwords + - `install-php-sqlite.sh` - Install PHP SQLite extension + - `fix-paths.sh` - Fix PHP include paths +- **`docs/`** - Documentation + - `PRODUCTION_DEPLOYMENT.md` - Deployment guide + - `QUICK_REFERENCE.md` - Command reference + - `ADMIN_USERS.md` - User management + - `SECURITY_HEADERS.md` - Security headers reference + - `PHP_AUTH_LAYER.md` - Authentication layer documentation + - `HTACCESS_TO_ NGINX.md` - Apache to nginx migration notes + - `TEST_DATABASE_SETUP.md` - Test database deployment ## 🚀 Quick Start -### 1. Deploy nginx configuration (automated) +### Deploy nginx configuration ```bash # From your local machine @@ -19,38 +28,37 @@ just deploy-nginx # Then on the server: ssh posterg -sudo bash /tmp/deploy-production.sh +sudo bash /tmp/deploy-server.sh +sudo systemctl reload nginx ``` The deployment script will: -- ✅ Fix file permissions (posterg group) -- ✅ Set up admin password (if needed) +- ✅ Fix file permissions (www-data:posterg) - ✅ Install nginx configuration - ✅ Test and reload nginx - ✅ Verify PHP-FPM is running -### 2. SSL/TLS +### Manage admin users -SSL/TLS is handled by the upstream reverse proxy and is already working. -No additional SSL setup is needed on this server. +```bash +just manage-admin-users +ssh posterg "sudo bash /tmp/manage-admin-users.sh" +``` ## 🔒 Security Features ### Admin Panel Protection -- **Password required** for `/formulaire/` (admin panel) +- **Password required** for `/admin/` - HTTP Basic Authentication - Rate limited: 10 requests/minute ### File Access Protection - Database files (`.db`) - **BLOCKED** - Sensitive files (`.md`, `.sql`, `.env`) - **BLOCKED** -- `/src` directory (PHP source) - **BLOCKED** -- `/templates` directory (PHP templates) - **BLOCKED** -- `/config` directory (configuration) - **BLOCKED** -- `/storage` directory (databases) - **BLOCKED** -- `/tests` directory - **BLOCKED** -- `/scripts` directory - **BLOCKED** -- `/docs` directory - **BLOCKED** +- `/src` directory - **BLOCKED** +- `/templates` directory - **BLOCKED** +- `/config` directory - **BLOCKED** +- `/storage` directory - **BLOCKED** - Hidden files (`.git`, etc.) - **BLOCKED** ### Rate Limiting @@ -61,45 +69,25 @@ No additional SSL setup is needed on this server. ### 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 +- **[docs/PRODUCTION_DEPLOYMENT.md](docs/PRODUCTION_DEPLOYMENT.md)** - Complete deployment guide +- **[docs/QUICK_REFERENCE.md](docs/QUICK_REFERENCE.md)** - Command reference and troubleshooting +- **[docs/ADMIN_USERS.md](docs/ADMIN_USERS.md)** - Admin user management +- **[docs/SECURITY_HEADERS.md](docs/SECURITY_HEADERS.md)** - Security headers reference ## 🧪 Testing -Test your configuration: - ```bash # Test admin authentication -curl -I https://posterg.erg.be/formulaire/ +curl -I https://posterg.erg.be/admin/ # Test file protection -curl -I https://posterg.erg.be/storage/posterg.db +curl -I https://posterg.erg.be/storage/test.db # Test security headers curl -I https://posterg.erg.be/ | grep -E "X-|Strict-Transport" @@ -109,60 +97,27 @@ curl -I https://posterg.erg.be/ | grep -E "X-|Strict-Transport" ### 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 +sudo systemctl status php8.4-fpm +sudo systemctl restart php8.4-fpm ``` ### Configuration errors ```bash -# Test and show errors sudo nginx -t ``` ## 📊 Monitoring ```bash -# Watch access logs +# Watch 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` diff --git a/nginx/SETUP.md b/nginx/SETUP.md index 72c40c6..32b783a 100644 --- a/nginx/SETUP.md +++ b/nginx/SETUP.md @@ -1,369 +1,127 @@ # Nginx Setup for Post-ERG -This document explains how to set up nginx with security features and password protection for the admin panel. +Complete setup guide for nginx with security features and password protection. ## Prerequisites - Ubuntu/Debian server with root access - Nginx installed -- PHP-FPM installed (PHP 8.2 or later) +- PHP-FPM installed (PHP 8.4) - Domain name pointed to your server -## Installation Steps +## Quick Setup (Recommended) + +### 1. Deploy from your local machine + +```bash +just deploy-nginx +``` + +### 2. Apply on the server + +```bash +ssh posterg +sudo bash /tmp/deploy-server.sh +sudo systemctl reload nginx +``` + +### 3. Set admin password (first time only) + +```bash +just manage-admin-users +ssh posterg "sudo bash /tmp/manage-admin-users.sh" +``` + +## Manual Setup 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 +sudo apt install nginx apache2-utils php8.4-fpm ``` -### 2. Create Password File for Admin Panel - -Create a password-protected admin area: +### 2. Create Admin Password ```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 +sudo rm -f /etc/nginx/sites-enabled/default ``` -### 4. Update Domain Name - -Edit the configuration to use your domain: +### 4. Test and Reload ```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/storage/posterg.db -sudo chown www-data:www-data /var/www/html/storage/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 +### Test Admin Authentication ```bash -# Should prompt for password -curl -I https://posterg.erg.be/formulaire/ +# Should return 401 +curl -I https://posterg.erg.be/admin/ # With credentials -curl -u admin:your_password https://posterg.erg.be/formulaire/ +curl -u admin:password https://posterg.erg.be/admin/ ``` -### Test Rate Limiting +### Test File Protection ```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/storage/posterg.db -curl -I https://posterg.erg.be/shared/Database.php -curl -I https://posterg.erg.be/README.md +# Should return 403 +curl -I https://posterg.erg.be/storage/test.db +curl -I https://posterg.erg.be/src/Database.php ``` ### 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 +curl -I https://posterg.erg.be/ | grep -E "X-|Strict-Transport" ``` ## 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 +### 403 Forbidden on admin +```bash +sudo ls -l /etc/nginx/.htpasswd-posterg +sudo chmod 644 /etc/nginx/.htpasswd-posterg +``` -### "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` +### 502 Bad Gateway +```bash +sudo systemctl status php8.4-fpm +sudo systemctl restart php8.4-fpm +``` -### "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 +### Configuration errors +```bash +sudo nginx -t +``` ## 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 - +### Reload Configuration ```bash -# Manual renewal -sudo certbot renew - -# Check expiry -sudo certbot certificates +sudo nginx -t && sudo systemctl reload nginx ``` -### Update Configuration +## See Also -```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. +- **[docs/PRODUCTION_DEPLOYMENT.md](docs/PRODUCTION_DEPLOYMENT.md)** - Detailed deployment +- **[docs/QUICK_REFERENCE.md](docs/QUICK_REFERENCE.md)** - Command reference +- **[docs/ADMIN_USERS.md](docs/ADMIN_USERS.md)** - User management diff --git a/nginx/TEST_DATABASE_SETUP.md b/nginx/TEST_DATABASE_SETUP.md deleted file mode 100644 index 427e2f5..0000000 --- a/nginx/TEST_DATABASE_SETUP.md +++ /dev/null @@ -1,352 +0,0 @@ -# Test Database Setup - Post-ERG - -Complete guide for deploying and managing the test database on the production server. - ---- - -## 🎯 Quick Deploy - -```bash -just test-deploy -``` - -This automatically: -1. ✅ Creates `/var/www/html/storage/` directory -2. ✅ Uploads `test.db` to the server -3. ✅ Sets correct group ownership (`posterg`) -4. ✅ Sets correct permissions (775 for dir, 660 for file) - ---- - -## 🔧 Prerequisites (One-Time Setup) - -### 1. Install PHP SQLite Extension - -On the server: -```bash -ssh posterg -sudo bash /tmp/install-php-sqlite.sh -``` - -Or manually: -```bash -ssh posterg -sudo apt update -sudo apt install php8.4-sqlite3 -sudo systemctl restart php8.4-fpm -``` - -### 2. Verify Installation - -```bash -ssh posterg -php -m | grep sqlite3 -# Should output: pdo_sqlite, sqlite3 -``` - ---- - -## 📋 How Database Selection Works - -The system automatically detects which database to use: - -1. **If `test.db` exists** → Uses test database -2. **Otherwise** → Uses production database (`posterg.db`) - -This is configured in `shared/config.php`: - -```php -function getDatabasePath() { - // If test.db exists, use it - if (file_exists(DB_TEST_PATH)) { - return DB_TEST_PATH; - } - // Otherwise use production database - return DB_PROD_PATH; -} -``` - ---- - -## 🧪 Complete Testing Workflow - -### 1. Create Test Data Locally - -```bash -# Create empty test database from schema -just init-test-db - -# Or create with sample fixtures -just create-fixtures -``` - -### 2. Deploy Test Database - -```bash -just test-deploy -``` - -### 3. Test the Site - -Visit: https://posterg.erg.be/ - -The site now uses test data! 🎉 - -### 4. Check What Database is Being Used - -```bash -ssh posterg -php -r "require_once '/var/www/html/shared/Database.php'; echo 'Using: ' . Database::getInstance()->getDatabasePath() . PHP_EOL;" -``` - -Output will be: -- `/var/www/html/storage/test.db` (test mode) -- `/var/www/html/storage/posterg.db` (production mode) - -### 5. Switch Back to Production - -Simply remove the test database: - -```bash -ssh posterg -rm /var/www/html/storage/test.db -``` - -The site automatically switches to production database. - ---- - -## 🔒 Permissions Explained - -### Directory Permissions - -``` -drwxrwxr-x theophile posterg /var/www/html/storage/ -``` - -- **775**: Owner and group can read/write/execute, others can read/execute -- **Group: posterg**: `www-data` is member of this group -- **Writable by group**: SQLite needs to create journal/temp files - -### File Permissions - -``` --rw-rw---- theophile posterg test.db -``` - -- **660**: Owner and group can read/write, others have no access -- **Group: posterg**: `www-data` can read/write the database -- **No public access**: Security - only PHP-FPM can access - ---- - -## 🐛 Troubleshooting - -### Site Shows Empty Page or Error - -**Check error logs:** -```bash -ssh posterg -tail -f /var/log/nginx/posterg_error.log -``` - -### "could not find driver" - -**SQLite extension not installed:** -```bash -ssh posterg -sudo apt install php8.4-sqlite3 -sudo systemctl restart php8.4-fpm -``` - -### "unable to open database file" - -**Wrong permissions:** -```bash -ssh posterg -# Fix group ownership -chgrp posterg /var/www/html/database /var/www/html/storage/test.db - -# Fix permissions -chmod 775 /var/www/html/database -chmod 660 /var/www/html/storage/test.db -``` - -### "SQLSTATE[HY000]: General error: 8 attempt to write a readonly database" - -**Directory not writable:** -```bash -ssh posterg -chmod 775 /var/www/html/database -``` - -### Database Doesn't Update - -**Clear SQLite cache:** -```bash -ssh posterg -rm -f /var/www/html/storage/test.db-journal -rm -f /var/www/html/storage/test.db-shm -rm -f /var/www/html/storage/test.db-wal -``` - -Then redeploy: -```bash -just test-deploy -``` - ---- - -## 📊 Check Database Stats - -### On Server - -```bash -ssh posterg -cd /var/www/html - -# Count theses -php -r "require_once 'shared/Database.php'; echo 'Theses: ' . Database::getInstance()->countPublishedTheses() . PHP_EOL;" - -# Check database file -ls -lh database/test.db -``` - -### From Local Machine - -```bash -# Show stats from local test database -sqlite3 database/test.db "SELECT COUNT(*) FROM theses;" -sqlite3 database/test.db "SELECT COUNT(*) FROM theses WHERE is_published = 1;" -``` - ---- - -## 🔄 Update Test Data - -### Update Locally and Redeploy - -```bash -# Make changes to local test database -sqlite3 database/test.db -# ... make changes ... - -# Deploy updated database -just test-deploy -``` - -### Update Directly on Server - -```bash -ssh posterg -sqlite3 /var/www/html/storage/test.db -# ... make changes ... -``` - ---- - -## ⚠️ Important Notes - -### Production Safety - -The `just deploy` command **excludes all `.db` files** by default: - -```bash -# Safe - deploys code only -just deploy -just deploy-public -just deploy-admin - -# Safe - deploys schema/docs only -just deploy-database - -# Requires explicit command - deploys test.db -just test-deploy -``` - -This prevents accidentally overwriting production data! - -### Never Commit test.db to Git - -The `.gitignore` already excludes it: - -``` -*.db -*.db-journal -``` - -### Backup Production Database - -Before deploying test database, backup production if needed: - -```bash -ssh posterg -cp /var/www/html/storage/posterg.db /var/www/html/storage/posterg.db.backup.$(date +%Y%m%d) -``` - ---- - -## 🎓 Database File Locations - -### Local (Development) - -``` -/home/theophile/dev/posterg-website/ -└── database/ - ├── schema.sql # Database schema - ├── test.db # Test database (gitignored) - └── fixtures/ # Test data generators -``` - -### Server (Production) - -``` -/var/www/html/ -└── database/ - ├── posterg.db # Production database - └── test.db # Test database (if deployed) -``` - ---- - -## 📚 Related Commands - -| Command | Description | -|---------|-------------| -| `just init-test-db` | Create empty test database | -| `just create-fixtures` | Create test database with sample data | -| `just test-deploy` | Deploy test database to server | -| `just stats-public` | Show local database statistics | -| `just query-db` | Open SQLite prompt for local test.db | - ---- - -## ✅ Deployment Checklist - -After running `just test-deploy`, verify: - -- [ ] Database file exists: `ssh posterg "ls -la /var/www/html/storage/test.db"` -- [ ] Correct permissions: `-rw-rw---- theophile posterg` -- [ ] Directory writable: `drwxrwxr-x theophile posterg` -- [ ] Site loads: Visit https://posterg.erg.be/ -- [ ] No errors in logs: `ssh posterg "tail /var/log/nginx/posterg_error.log"` -- [ ] Database accessible: Test with admin panel - ---- - -## 🎉 Success! - -When working correctly: - -- ✅ Main page shows test data -- ✅ Search works with test data -- ✅ Admin panel loads form -- ✅ No database errors in logs -- ✅ Can create/edit/delete test entries - -To switch back to production, just: -```bash -ssh posterg "rm /var/www/html/storage/test.db" -``` - -Site automatically uses `posterg.db` again! 🚀 diff --git a/nginx/deploy-production-new.sh b/nginx/deploy-production-new.sh deleted file mode 100755 index c856491..0000000 --- a/nginx/deploy-production-new.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/bash -# Deploy production nginx configuration for Post-ERG (NEW STRUCTURE) -# This script applies the nginx config for /var/www/posterg/public/ structure - -set -e - -echo "🚀 Post-ERG Production Deployment (NEW STRUCTURE)" -echo "==================================================" -echo "" - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - echo -e "${RED}Error: This script must be run as root (use sudo)${NC}" - exit 1 -fi - -echo "📋 Step 1: Fixing file permissions..." -echo "--------------------------------------" - -# Change ownership to www-data:posterg -chown -R www-data:posterg /var/www/posterg/ -echo "✓ Changed ownership to www-data:posterg" - -# Set directory permissions (755) -find /var/www/posterg -type d -exec chmod 755 {} \; -echo "✓ Set directory permissions to 755" - -# Set file permissions (644) -find /var/www/posterg -type f -exec chmod 644 {} \; -echo "✓ Set file permissions to 644" - -# Make storage directory writable by group -if [ -d "/var/www/posterg/storage" ]; then - chmod 775 /var/www/posterg/storage - echo "✓ Made storage directory group-writable (775)" -fi - -# Fix database file permissions -if [ -f "/var/www/posterg/storage/test.db" ]; then - chmod 660 /var/www/posterg/storage/test.db - chown www-data:posterg /var/www/posterg/storage/test.db - echo "✓ Fixed database file permissions (660)" -fi - -# Make admin upload directories writable by group -if [ -d "/var/www/posterg/public/admin/data" ]; then - find /var/www/posterg/public/admin/data -type d -exec chmod 775 {} \; - echo "✓ Made admin upload directories group-writable" -fi - -echo "" -echo "📋 Step 2: Deploying nginx configuration..." -echo "--------------------------------------" - -# Backup existing config -if [ -f "/etc/nginx/sites-available/posterg" ]; then - cp /etc/nginx/sites-available/posterg /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S) - echo "✓ Backed up existing config" -fi - -# Copy new config -if [ -f "/tmp/posterg.conf" ]; then - cp /tmp/posterg.conf /etc/nginx/sites-available/posterg - echo "✓ Installed new nginx config" -else - echo -e "${RED}Error: /tmp/posterg.conf not found${NC}" - echo "Run 'just deploy-nginx' first" - exit 1 -fi - -# Test nginx configuration -echo "" -echo "📋 Step 3: Testing nginx configuration..." -echo "--------------------------------------" - -if nginx -t; then - echo -e "${GREEN}✓ Nginx configuration is valid${NC}" -else - echo -e "${RED}✗ Nginx configuration has errors!${NC}" - echo "Restoring backup..." - cp /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S | tail -1) /etc/nginx/sites-available/posterg - exit 1 -fi - -echo "" -echo "📋 Step 4: Summary..." -echo "--------------------------------------" -echo -e "${GREEN}✓ Permissions fixed${NC}" -echo -e "${GREEN}✓ Nginx config installed${NC}" -echo -e "${GREEN}✓ Configuration validated${NC}" -echo "" -echo -e "${YELLOW}Ready to reload nginx!${NC}" -echo "" -echo "Run: ${GREEN}sudo systemctl reload nginx${NC}" -echo "" -echo "After reload, verify:" -echo " • https://posterg.erg.be/" -echo " • https://posterg.erg.be/admin/" -echo " • https://posterg.erg.be/storage/test.db (should 404)" diff --git a/nginx/deploy-production.sh b/nginx/deploy-production.sh deleted file mode 100755 index 6893824..0000000 --- a/nginx/deploy-production.sh +++ /dev/null @@ -1,182 +0,0 @@ -#!/bin/bash -# Deploy production nginx configuration and fix permissions for Post-ERG - -set -e - -echo "🚀 Post-ERG Production Deployment" -echo "==================================" -echo "" - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - echo -e "${RED}Error: This script must be run as root (use sudo)${NC}" - exit 1 -fi - -echo "📋 Step 1: Fixing file permissions..." -echo "--------------------------------------" - -# Change group to posterg (www-data is member of this group) -chown -R theophile:posterg /var/www/html/ -echo "✓ Changed group to posterg" - -# Set directory permissions (755 - readable/executable by everyone) -find /var/www/html -type d -exec chmod 755 {} \; -echo "✓ Set directory permissions to 755" - -# Set file permissions (640 - owner read/write, group read) -find /var/www/html -type f -exec chmod 640 {} \; -echo "✓ Set file permissions to 640" - -# Make upload directories writable by group (for www-data to write) -if [ -d "/var/www/html/formulaire/data/theses" ]; then - chmod 775 /var/www/html/formulaire/data/theses - chmod 775 /var/www/html/formulaire/data/covers - echo "✓ Set upload directories to 775" -fi - -# Protect database if it exists -if [ -f "/var/www/html/storage/posterg.db" ]; then - chmod 660 /var/www/html/storage/posterg.db - chown www-data:posterg /var/www/html/storage/posterg.db - echo "✓ Protected database file" -fi - -echo "" -echo "📋 Step 2: Checking prerequisites..." -echo "--------------------------------------" - -# Check if htpasswd is available -if ! command -v htpasswd &> /dev/null; then - echo -e "${YELLOW}⚠️ htpasswd not found, installing apache2-utils...${NC}" - apt-get update -qq - apt-get install -y apache2-utils - echo -e "${GREEN}✓ apache2-utils installed${NC}" -fi - -# Check if htpasswd file exists -if [ ! -f "/etc/nginx/.htpasswd-posterg" ]; then - echo -e "${YELLOW}⚠️ Warning: /etc/nginx/.htpasswd-posterg not found${NC}" - echo " Creating it now..." - echo "" - echo "Please enter admin username:" - read -r ADMIN_USER - htpasswd -c /etc/nginx/.htpasswd-posterg "$ADMIN_USER" - echo -e "${GREEN}✓ Password file created${NC}" - echo "" -else - echo "✓ Password file exists" -fi - -# Check if config file was uploaded -if [ ! -f "/tmp/posterg.conf" ]; then - echo -e "${RED}✗ Error: /tmp/posterg.conf not found${NC}" - echo "Please upload it first: rsync -vur ./nginx/posterg-production.conf posterg:/tmp/posterg.conf" - exit 1 -fi - -echo "" -echo "📋 Step 3: Installing nginx configuration..." -echo "--------------------------------------" - -# Backup existing config if it exists -if [ -f "/etc/nginx/sites-available/posterg" ]; then - cp /etc/nginx/sites-available/posterg /etc/nginx/sites-available/posterg.backup.$(date +%Y%m%d_%H%M%S) - echo "✓ Backed up existing config" -fi - -# Copy new configuration -cp /tmp/posterg.conf /etc/nginx/sites-available/posterg -echo "✓ Installed configuration to /etc/nginx/sites-available/posterg" - -# Create symlink -if [ ! -L "/etc/nginx/sites-enabled/posterg" ]; then - ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/posterg - echo "✓ Created symlink in sites-enabled" -else - echo "✓ Symlink already exists" -fi - -# Remove default site -if [ -L "/etc/nginx/sites-enabled/default" ]; then - rm /etc/nginx/sites-enabled/default - echo "✓ Disabled default site" -fi - -echo "" -echo "📋 Step 4: Testing nginx configuration..." -echo "--------------------------------------" - -if nginx -t; then - echo -e "${GREEN}✓ Nginx configuration is valid${NC}" -else - echo -e "${RED}✗ Nginx configuration has errors!${NC}" - echo "Restoring backup..." - if ls /etc/nginx/sites-available/posterg.backup* 1> /dev/null 2>&1; then - BACKUP=$(ls -t /etc/nginx/sites-available/posterg.backup* | head -1) - cp "$BACKUP" /etc/nginx/sites-available/posterg - echo "Configuration restored from backup" - fi - exit 1 -fi - -echo "" -echo "📋 Step 5: Reloading nginx..." -echo "--------------------------------------" - -if systemctl reload nginx; then - echo -e "${GREEN}✓ Nginx reloaded successfully${NC}" -else - echo -e "${RED}✗ Failed to reload nginx${NC}" - exit 1 -fi - -echo "" -echo "📋 Step 6: Verifying services..." -echo "--------------------------------------" - -# Check PHP-FPM -if systemctl is-active --quiet php8.4-fpm; then - echo -e "${GREEN}✓ PHP 8.4-FPM is running${NC}" -else - echo -e "${YELLOW}⚠️ PHP-FPM is not running, starting it...${NC}" - systemctl start php8.4-fpm - systemctl enable php8.4-fpm - echo -e "${GREEN}✓ PHP-FPM started${NC}" -fi - -# Check nginx -if systemctl is-active --quiet nginx; then - echo -e "${GREEN}✓ Nginx is running${NC}" -else - echo -e "${RED}✗ Nginx is not running!${NC}" - exit 1 -fi - -echo "" -echo "═══════════════════════════════════════" -echo -e "${GREEN}✅ Deployment Complete!${NC}" -echo "═══════════════════════════════════════" -echo "" -echo "🧪 Quick Tests:" -echo " • Test public site: curl -I http://localhost/" -echo " • Test admin panel: curl -I http://localhost/formulaire/" -echo " • Test PHP: curl http://localhost/index.php" -echo "" -echo "📊 View logs:" -echo " • Access log: tail -f /var/log/nginx/posterg_access.log" -echo " • Error log: tail -f /var/log/nginx/posterg_error.log" -echo "" -echo "🔒 Security Checks:" -echo " • Database blocked: curl -I http://localhost/storage/posterg.db" -echo " • MD files blocked: curl -I http://localhost/README.md" -echo " • Source blocked: curl -I http://localhost/src/Database.php" -echo " • Templates blocked: curl -I http://localhost/templates/header.php" -echo " • Config blocked: curl -I http://localhost/config/bootstrap.php" -echo "" diff --git a/nginx/ADMIN_USERS.md b/nginx/docs/ADMIN_USERS.md similarity index 90% rename from nginx/ADMIN_USERS.md rename to nginx/docs/ADMIN_USERS.md index 3dfde39..d277f02 100644 --- a/nginx/ADMIN_USERS.md +++ b/nginx/docs/ADMIN_USERS.md @@ -9,6 +9,10 @@ Quick guide to manage admin users for the Post-ERG admin panel. ### Interactive Menu (Recommended) ```bash +# From your local machine +just manage-admin-users + +# Then on the server ssh posterg sudo bash /tmp/manage-admin-users.sh ``` @@ -71,10 +75,10 @@ To upload the interactive management script to the server: ```bash # From your local machine -just deploy-admin-tools +just manage-admin-users # Or manually: -rsync -vur ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh +rsync -v scripts/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh ``` --- @@ -82,7 +86,7 @@ rsync -vur ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh ## 🔑 Current Setup After deployment, your admin panel has: -- **URL:** https://posterg.erg.be/formulaire/ +- **URL:** https://posterg.erg.be/admin/ - **Current user:** `test_posterg_22@` - **Password:** Set during initial deployment @@ -127,7 +131,7 @@ sudo htpasswd /etc/nginx/.htpasswd-posterg admin2 sudo htpasswd /etc/nginx/.htpasswd-posterg admin3 ``` -All users can log into `/formulaire/` with their own credentials. +All users can log into `/admin/` with their own credentials. ### Scenario 5: Start Over with New Username @@ -145,11 +149,11 @@ After changing users/passwords: ```bash # Test that password is required -curl -I https://posterg.erg.be/formulaire/ +curl -I https://posterg.erg.be/admin/ # Should return: 401 Unauthorized # Test with credentials -curl -u username:password https://posterg.erg.be/formulaire/ +curl -u username:password https://posterg.erg.be/admin/ # Should return: 200 OK ``` @@ -193,7 +197,7 @@ username:$apr1$encrypted_password_hash ```bash # Check who's accessing the admin panel ssh posterg - sudo grep "formulaire" /var/log/nginx/posterg_access.log + sudo grep "admin" /var/log/nginx/posterg_access.log ``` 5. **Backup Password File** @@ -263,7 +267,7 @@ No action needed! Changes to the password file take effect immediately. You can verify with: ```bash -curl -u username:password https://posterg.erg.be/formulaire/ +curl -u username:password https://posterg.erg.be/admin/ ``` --- diff --git a/nginx/HTACCESS_TO_NGINX.md b/nginx/docs/HTACCESS_TO_NGINX.md similarity index 96% rename from nginx/HTACCESS_TO_NGINX.md rename to nginx/docs/HTACCESS_TO_NGINX.md index f9c6e6f..3c94372 100644 --- a/nginx/HTACCESS_TO_NGINX.md +++ b/nginx/docs/HTACCESS_TO_NGINX.md @@ -1,9 +1,11 @@ -# `.htaccess` → nginx migration (item #6) +# `.htaccess` → nginx migration > **Problem:** `public/admin/.htaccess` contained Apache-specific security > directives that nginx **silently ignores**. None of the rules were active > in production. +> **Status:** Migrated into `nginx/posterg.conf` + --- ## Rules migrated into `nginx/posterg.conf` diff --git a/nginx/PHP_AUTH_LAYER.md b/nginx/docs/PHP_AUTH_LAYER.md similarity index 97% rename from nginx/PHP_AUTH_LAYER.md rename to nginx/docs/PHP_AUTH_LAYER.md index bb37006..c753a45 100644 --- a/nginx/PHP_AUTH_LAYER.md +++ b/nginx/docs/PHP_AUTH_LAYER.md @@ -11,7 +11,7 @@ The admin panel uses **two independent authentication layers** with a single UX | Layer | Mechanism | Configured by | |-------|-----------|---------------| | **1st** | nginx HTTP Basic Auth | `/etc/nginx/.htpasswd-posterg` (see `ADMIN_USERS.md`) | -| **2nd** | PHP session guard (`lib/AdminAuth.php`) | `config/admin_credentials.php` | +| **2nd** | PHP session guard (`src/AdminAuth.php`) | `config/admin_credentials.php` | The user only sees **one prompt** (the browser Basic Auth dialog). PHP reads the same password from `$_SERVER['PHP_AUTH_PW']` and validates it independently with @@ -103,7 +103,7 @@ nginx Basic Auth invalidation requires closing the browser tab / window. | File | Change | |------|--------| -| `lib/AdminAuth.php` | New — auth guard class | +| `src/AdminAuth.php` | New — auth guard class | | `config/admin_credentials.php` | New — credential store (gitignored) | | `config/admin_credentials.example.php` | New — example / template | | `config/bootstrap.php` | Load credentials on startup | diff --git a/nginx/docs/PRODUCTION_DEPLOYMENT.md b/nginx/docs/PRODUCTION_DEPLOYMENT.md new file mode 100644 index 0000000..7e0ce09 --- /dev/null +++ b/nginx/docs/PRODUCTION_DEPLOYMENT.md @@ -0,0 +1,210 @@ +# Production Deployment Guide - Post-ERG + +This guide covers deploying the production nginx configuration with proper security and permissions. + +## 🎯 Overview + +- **Server**: posterg.erg.be (internal IP: 192.168.6.125) +- **PHP Version**: 8.4 +- **SSL/TLS**: Handled by upstream reverse proxy +- **Document Root**: `/var/www/posterg/public/` + +## 🚀 Quick Deployment + +From your local machine: + +```bash +# Deploy nginx config and upload deployment script +just deploy-nginx + +# Then on the server: +ssh posterg +sudo bash /tmp/deploy-server.sh +sudo systemctl reload nginx +``` + +This uploads: +- `nginx/posterg.conf` → `/tmp/posterg.conf` +- `scripts/deploy-server.sh` → `/tmp/deploy-server.sh` + +## 📋 Step-by-Step Deployment + +### 1. Set Up Admin Password (First Time Only) + +```bash +ssh posterg +sudo htpasswd -c /etc/nginx/.htpasswd-posterg admin +# Enter a strong password when prompted +``` + +**💡 Tip**: Generate a strong password: +```bash +openssl rand -base64 32 +``` + +### 2. Deploy Configuration + +```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:posterg) +- ✅ Install nginx configuration +- ✅ Test nginx configuration +- ✅ Check PHP-FPM status + +## 🔧 Manual Deployment (Alternative) + +### Step 1: Fix Permissions + +```bash +ssh posterg + +# Set correct ownership +sudo chown -R www-data:posterg /var/www/posterg/ + +# 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/posterg/storage + +# Protect database +sudo chmod 660 /var/www/posterg/storage/test.db +sudo chown www-data:posterg /var/www/posterg/storage/test.db +``` + +### Step 2: Deploy Nginx Config + +```bash +# Copy config +sudo cp /tmp/posterg.conf /etc/nginx/sites-available/posterg + +# Enable site and disable default +sudo ln -sf /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/posterg +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://posterg.erg.be/ +``` + +### Test Admin Protection + +```bash +# Should return 401 Unauthorized +curl -I https://posterg.erg.be/admin/ + +# With credentials +curl -u admin:your_password https://posterg.erg.be/admin/ +``` + +### Test File Protection + +```bash +# Should return 403 Forbidden +curl -I https://posterg.erg.be/storage/test.db +curl -I https://posterg.erg.be/src/Database.php +curl -I https://posterg.erg.be/config/bootstrap.php +``` + +### Test Security Headers + +```bash +curl -I https://posterg.erg.be/ | grep -E "X-Frame|X-Content|Strict-Transport" +``` + +## 🔍 Troubleshooting + +### Still Getting 403 Forbidden + +**Check file permissions:** +```bash +ls -la /var/www/posterg/public/index.php +groups www-data # Should include posterg +``` + +### 502 Bad Gateway + +**Check PHP-FPM:** +```bash +sudo systemctl status php8.4-fpm +sudo systemctl restart php8.4-fpm +``` + +### Admin Password Not Working + +```bash +sudo htpasswd /etc/nginx/.htpasswd-posterg admin +``` + +## 📊 Monitoring + +```bash +# Watch logs +sudo tail -f /var/log/nginx/posterg_access.log +sudo tail -f /var/log/nginx/posterg_error.log + +# Check status +sudo systemctl status nginx +``` + +## 🔒 Security Checklist + +After deployment, verify: + +- [ ] Public site accessible at https://posterg.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 posterg "sudo systemctl reload nginx" +``` + +## 🆘 Emergency Recovery + +```bash +# Restore default nginx config +ssh posterg +sudo rm /etc/nginx/sites-enabled/posterg +sudo systemctl reload nginx + +# Reset permissions +sudo chown -R www-data:posterg /var/www/posterg/ +sudo find /var/www/posterg -type d -exec chmod 755 {} \; +sudo find /var/www/posterg -type f -exec chmod 644 {} \; +``` + +--- + +**See also:** +- [QUICK_REFERENCE.md](QUICK_REFERENCE.md) - Command reference +- [ADMIN_USERS.md](ADMIN_USERS.md) - User management +- [SECURITY_HEADERS.md](SECURITY_HEADERS.md) - Security headers diff --git a/nginx/QUICK_REFERENCE.md b/nginx/docs/QUICK_REFERENCE.md similarity index 93% rename from nginx/QUICK_REFERENCE.md rename to nginx/docs/QUICK_REFERENCE.md index d94689a..0c7fd4e 100644 --- a/nginx/QUICK_REFERENCE.md +++ b/nginx/docs/QUICK_REFERENCE.md @@ -3,22 +3,13 @@ ## 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/ +sudo rm -f /etc/nginx/sites-enabled/default -# Test configuration +# Test and reload sudo nginx -t - -# Reload nginx sudo systemctl reload nginx ``` @@ -27,6 +18,10 @@ sudo systemctl reload nginx ### Password Management ```bash +# Interactive menu (recommended) +sudo bash /tmp/manage-admin-users.sh + +# Or manual commands: # Add new user sudo htpasswd /etc/nginx/.htpasswd-posterg username @@ -103,10 +98,10 @@ sudo certbot renew --dry-run ```bash # Should require password (returns 401) -curl -I https://posterg.erg.be/formulaire/ +curl -I https://posterg.erg.be/admin/ # With authentication -curl -u admin:password https://posterg.erg.be/formulaire/ +curl -u admin:password https://posterg.erg.be/admin/ ``` ### Test Rate Limiting @@ -173,7 +168,7 @@ sudo tail -50 /var/log/nginx/error.log ```bash # Disable password protection temporarily sudo nano /etc/nginx/sites-available/posterg -# Comment out these lines in /formulaire/ location: +# Comment out these lines in /admin/ location: # auth_basic "Admin Access - Post-ERG"; # auth_basic_user_file /etc/nginx/.htpasswd-posterg; diff --git a/nginx/SECURITY_HEADERS.md b/nginx/docs/SECURITY_HEADERS.md similarity index 97% rename from nginx/SECURITY_HEADERS.md rename to nginx/docs/SECURITY_HEADERS.md index b499317..eea8631 100644 --- a/nginx/SECURITY_HEADERS.md +++ b/nginx/docs/SECURITY_HEADERS.md @@ -18,6 +18,7 @@ These were previously declared in `public/admin/.htaccess` as Apache `mod_headers` directives, which nginx silently ignores. They are now +properly configured in `nginx/posterg.conf`. enforced directly; see `HTACCESS_TO_NGINX.md` for the full migration log. ## Intentionally omitted headers diff --git a/nginx/docs/TEST_DATABASE_SETUP.md b/nginx/docs/TEST_DATABASE_SETUP.md new file mode 100644 index 0000000..b9f8adf --- /dev/null +++ b/nginx/docs/TEST_DATABASE_SETUP.md @@ -0,0 +1,152 @@ +# Test Database Setup - Post-ERG + +Guide for deploying the test database to production server. + +--- + +## 🎯 Quick Deploy + +```bash +just deploy-db +``` + +This automatically: +1. ✅ Checks remote DB doesn't exist (safety check) +2. ✅ Uploads `storage/test.db` to the server +3. ✅ Sets correct permissions (660, www-data:posterg) + +--- + +## 🔧 Prerequisites (One-Time Setup) + +### 1. Install PHP SQLite Extension + +```bash +ssh posterg +sudo apt update +sudo apt install php8.4-sqlite3 +sudo systemctl restart php8.4-fpm +``` + +### 2. Verify Installation + +```bash +ssh posterg +php -m | grep sqlite3 +# Should output: pdo_sqlite, sqlite3 +``` + +--- + +## 🧪 Complete Testing Workflow + +### 1. Create Test Data Locally + +```bash +# Create empty test database from schema +just init-db + +# Or create with sample fixtures +just fixtures +``` + +### 2. Deploy Test Database + +```bash +just deploy-db +``` + +### 3. Test the Site + +Visit: https://posterg.erg.be/ + +### 4. Check What Database is Being Used + +```bash +ssh posterg +php -r "require_once '/var/www/posterg/src/Database.php'; echo 'Using: ' . Database::getInstance()->getDatabasePath() . PHP_EOL;" +``` + +### 5. Switch Back to Production + +```bash +ssh posterg +rm /var/www/posterg/storage/test.db +``` + +--- + +## 🔒 Permissions Explained + +``` +/var/www/posterg/storage/ +drwxrwxr-x www-data posterg # 775 - group writable + +/var/www/posterg/storage/test.db +-rw-rw---- www-data posterg # 660 - owner/group read/write +``` + +--- + +## 🐛 Troubleshooting + +### "could not find driver" + +```bash +ssh posterg +sudo apt install php8.4-sqlite3 +sudo systemctl restart php8.4-fpm +``` + +### "unable to open database file" + +```bash +ssh posterg +chown www-data:posterg /var/www/posterg/storage/test.db +chmod 660 /var/www/posterg/storage/test.db +chmod 775 /var/www/posterg/storage/ +``` + +### "attempt to write a readonly database" + +```bash +ssh posterg +chmod 775 /var/www/posterg/storage/ +rm -f /var/www/posterg/storage/test.db-* +``` + +--- + +## ⚠️ Important Notes + +### Production Safety + +`just deploy` **excludes all `.db` files** by default. Only `just deploy-db` uploads the test database. + +### Backup Production Database + +```bash +ssh posterg +cp /var/www/posterg/storage/posterg.db /var/www/posterg/storage/posterg.db.backup.$(date +%Y%m%d) +``` + +--- + +## 📚 Related Commands + +| Command | Description | +|---------|-------------| +| `just init-db` | Create empty test database | +| `just fixtures` | Create test database with sample data | +| `just deploy-db` | Deploy test database to server | + +--- + +## ✅ Deployment Checklist + +After running `just deploy-db`, verify: + +- [ ] Database file exists: `ssh posterg "ls -la /var/www/posterg/storage/test.db"` +- [ ] Correct permissions: `-rw-rw---- www-data posterg` +- [ ] Site loads: Visit https://posterg.erg.be/ +- [ ] No errors in logs: `ssh posterg "tail /var/log/nginx/posterg_error.log"` diff --git a/nginx/manage-admin-users.sh b/nginx/manage-admin-users.sh deleted file mode 100755 index 0de3972..0000000 --- a/nginx/manage-admin-users.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/bash -# Manage admin users for Post-ERG nginx basic authentication - -set -e - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -PASSWORD_FILE="/etc/nginx/.htpasswd-posterg" - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - echo -e "${RED}Error: This script must be run as root (use sudo)${NC}" - exit 1 -fi - -# Check if htpasswd is available -if ! command -v htpasswd &> /dev/null; then - echo -e "${YELLOW}Installing apache2-utils...${NC}" - apt-get update -qq - apt-get install -y apache2-utils -fi - -show_menu() { - echo "" - echo -e "${BLUE}════════════════════════════════════════${NC}" - echo -e "${BLUE} Post-ERG Admin User Management${NC}" - echo -e "${BLUE}════════════════════════════════════════${NC}" - echo "" - echo "1. List all users" - echo "2. Add new user" - echo "3. Change user password" - echo "4. Delete user" - echo "5. Reset all (create new password file)" - echo "6. Exit" - echo "" - echo -n "Choose an option [1-6]: " -} - -list_users() { - echo "" - if [ ! -f "$PASSWORD_FILE" ]; then - echo -e "${YELLOW}No password file found.${NC}" - return - fi - - echo -e "${GREEN}Current admin users:${NC}" - echo "────────────────────────" - cut -d: -f1 "$PASSWORD_FILE" | nl - echo "" -} - -add_user() { - echo "" - echo -n "Enter new username: " - read -r USERNAME - - if [ -z "$USERNAME" ]; then - echo -e "${RED}Username cannot be empty${NC}" - return - fi - - # Check if user already exists - if [ -f "$PASSWORD_FILE" ] && grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then - echo -e "${YELLOW}User '$USERNAME' already exists. Use option 3 to change password.${NC}" - return - fi - - # Add user (use -c only if file doesn't exist) - if [ ! -f "$PASSWORD_FILE" ]; then - htpasswd -c "$PASSWORD_FILE" "$USERNAME" - else - htpasswd "$PASSWORD_FILE" "$USERNAME" - fi - - echo -e "${GREEN}✓ User '$USERNAME' added successfully${NC}" -} - -change_password() { - list_users - echo -n "Enter username to change password: " - read -r USERNAME - - if [ -z "$USERNAME" ]; then - echo -e "${RED}Username cannot be empty${NC}" - return - fi - - if [ ! -f "$PASSWORD_FILE" ]; then - echo -e "${RED}Password file not found${NC}" - return - fi - - if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then - echo -e "${RED}User '$USERNAME' not found${NC}" - return - fi - - htpasswd "$PASSWORD_FILE" "$USERNAME" - echo -e "${GREEN}✓ Password changed for user '$USERNAME'${NC}" -} - -delete_user() { - list_users - echo -n "Enter username to delete: " - read -r USERNAME - - if [ -z "$USERNAME" ]; then - echo -e "${RED}Username cannot be empty${NC}" - return - fi - - if [ ! -f "$PASSWORD_FILE" ]; then - echo -e "${RED}Password file not found${NC}" - return - fi - - if ! grep -q "^${USERNAME}:" "$PASSWORD_FILE"; then - echo -e "${RED}User '$USERNAME' not found${NC}" - return - fi - - echo -n "Are you sure you want to delete user '$USERNAME'? [y/N] " - read -r CONFIRM - - if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then - htpasswd -D "$PASSWORD_FILE" "$USERNAME" - echo -e "${GREEN}✓ User '$USERNAME' deleted${NC}" - else - echo "Cancelled" - fi -} - -reset_all() { - echo "" - echo -e "${YELLOW}WARNING: This will delete ALL existing users!${NC}" - echo -n "Are you sure? [y/N] " - read -r CONFIRM - - if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then - echo "Cancelled" - return - fi - - # Backup existing file - if [ -f "$PASSWORD_FILE" ]; then - BACKUP="${PASSWORD_FILE}.backup.$(date +%Y%m%d_%H%M%S)" - cp "$PASSWORD_FILE" "$BACKUP" - echo -e "${GREEN}✓ Backed up to: $BACKUP${NC}" - fi - - echo "" - echo -n "Enter new username: " - read -r USERNAME - - if [ -z "$USERNAME" ]; then - echo -e "${RED}Username cannot be empty${NC}" - return - fi - - htpasswd -c "$PASSWORD_FILE" "$USERNAME" - echo -e "${GREEN}✓ Password file reset with user '$USERNAME'${NC}" -} - -# Main loop -while true; do - show_menu - read -r CHOICE - - case $CHOICE in - 1) - list_users - ;; - 2) - add_user - ;; - 3) - change_password - ;; - 4) - delete_user - ;; - 5) - reset_all - ;; - 6) - echo "" - echo "Goodbye!" - exit 0 - ;; - *) - echo -e "${RED}Invalid option${NC}" - ;; - esac -done diff --git a/nginx/fix-paths.sh b/nginx/scripts/fix-paths.sh similarity index 100% rename from nginx/fix-paths.sh rename to nginx/scripts/fix-paths.sh diff --git a/nginx/install-php-sqlite.sh b/nginx/scripts/install-php-sqlite.sh similarity index 100% rename from nginx/install-php-sqlite.sh rename to nginx/scripts/install-php-sqlite.sh diff --git a/nginx/setup-password.sh b/nginx/scripts/setup-password.sh similarity index 100% rename from nginx/setup-password.sh rename to nginx/scripts/setup-password.sh