Nginx config, working deploy, basic theme, repo cleanup

This commit is contained in:
Théophile Gervreau-Mercier
2026-02-05 17:33:10 +01:00
parent 2cb5436647
commit f23fbb481b
30 changed files with 4536 additions and 760 deletions

271
nginx/ADMIN_USERS.md Normal file
View File

@@ -0,0 +1,271 @@
# Managing Admin Users - Post-ERG
Quick guide to manage admin users for the Post-ERG admin panel.
---
## 🎯 Quick Commands
### Interactive Menu (Recommended)
```bash
ssh posterg
sudo bash /tmp/manage-admin-users.sh
```
This gives you an interactive menu to:
1. List all users
2. Add new user
3. Change user password
4. Delete user
5. Reset all (start fresh)
---
## 📝 Manual Commands
### List Current Users
```bash
ssh posterg
sudo cut -d: -f1 /etc/nginx/.htpasswd-posterg
```
### Change Password for Existing User
```bash
ssh posterg
sudo htpasswd /etc/nginx/.htpasswd-posterg username_here
```
You'll be prompted to enter the new password twice.
### Add New User
```bash
ssh posterg
sudo htpasswd /etc/nginx/.htpasswd-posterg new_username
```
### Delete User
```bash
ssh posterg
sudo htpasswd -D /etc/nginx/.htpasswd-posterg username_to_delete
```
### Reset Everything (Start Fresh)
```bash
ssh posterg
sudo htpasswd -c /etc/nginx/.htpasswd-posterg new_username
```
⚠️ **Warning:** The `-c` flag creates a new file, deleting all existing users!
---
## 🚀 Deploy Management Script
To upload the interactive management script to the server:
```bash
# From your local machine
just deploy-admin-tools
# Or manually:
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/
- **Current user:** `test_posterg_22@`
- **Password:** Set during initial deployment
---
## 💡 Common Scenarios
### Scenario 1: Change Current Password
```bash
ssh posterg
sudo htpasswd /etc/nginx/.htpasswd-posterg test_posterg_22@
# Enter new password when prompted
```
### Scenario 2: Change Username
Since you can't rename users, you need to:
```bash
ssh posterg
# Add new user
sudo htpasswd /etc/nginx/.htpasswd-posterg new_username
# Delete old user
sudo htpasswd -D /etc/nginx/.htpasswd-posterg test_posterg_22@
```
### Scenario 3: Forgot Username
```bash
ssh posterg
sudo cut -d: -f1 /etc/nginx/.htpasswd-posterg
```
### Scenario 4: Multiple Admins
```bash
ssh posterg
# Add second admin
sudo htpasswd /etc/nginx/.htpasswd-posterg admin2
# Add third admin
sudo htpasswd /etc/nginx/.htpasswd-posterg admin3
```
All users can log into `/formulaire/` with their own credentials.
### Scenario 5: Start Over with New Username
```bash
ssh posterg
# This will DELETE ALL existing users and create a new one
sudo htpasswd -c /etc/nginx/.htpasswd-posterg new_admin
```
---
## 🧪 Testing
After changing users/passwords:
```bash
# Test that password is required
curl -I https://posterg.erg.be/formulaire/
# Should return: 401 Unauthorized
# Test with credentials
curl -u username:password https://posterg.erg.be/formulaire/
# Should return: 200 OK
```
No nginx reload needed - changes take effect immediately!
---
## 📊 Password File Details
**Location:** `/etc/nginx/.htpasswd-posterg`
**Format:** Standard Apache htpasswd format
```
username:$apr1$encrypted_password_hash
```
**Permissions:**
```bash
-rw-r--r-- root root /etc/nginx/.htpasswd-posterg
```
---
## 🔒 Security Tips
1. **Use Strong Passwords**
```bash
# Generate a strong password
openssl rand -base64 32
```
2. **Avoid Common Usernames**
- ❌ Bad: `admin`, `administrator`, `root`
- ✅ Good: `posterg_admin`, `erg_webmaster`
3. **Regular Password Changes**
- Change passwords every 3-6 months
- Change immediately if compromised
4. **Monitor Access**
```bash
# Check who's accessing the admin panel
ssh posterg
sudo grep "formulaire" /var/log/nginx/posterg_access.log
```
5. **Backup Password File**
```bash
ssh posterg
sudo cp /etc/nginx/.htpasswd-posterg /etc/nginx/.htpasswd-posterg.backup
```
---
## 🆘 Troubleshooting
### "401 Unauthorized" even with correct password
**Check file exists:**
```bash
ssh posterg
ls -la /etc/nginx/.htpasswd-posterg
```
**Verify user exists:**
```bash
sudo cat /etc/nginx/.htpasswd-posterg
```
**Check nginx config:**
```bash
sudo grep -A 5 "auth_basic" /etc/nginx/sites-available/posterg
```
### Can't change password - "command not found"
**Install apache2-utils:**
```bash
ssh posterg
sudo apt update
sudo apt install apache2-utils
```
### Password file got deleted
**Recreate it:**
```bash
ssh posterg
sudo htpasswd -c /etc/nginx/.htpasswd-posterg new_admin
```
---
## 📞 Quick Reference
| Task | Command |
|------|---------|
| **Interactive menu** | `sudo bash /tmp/manage-admin-users.sh` |
| **List users** | `sudo cut -d: -f1 /etc/nginx/.htpasswd-posterg` |
| **Change password** | `sudo htpasswd /etc/nginx/.htpasswd-posterg username` |
| **Add user** | `sudo htpasswd /etc/nginx/.htpasswd-posterg newuser` |
| **Delete user** | `sudo htpasswd -D /etc/nginx/.htpasswd-posterg username` |
| **Reset all** | `sudo htpasswd -c /etc/nginx/.htpasswd-posterg newuser` |
| **Generate password** | `openssl rand -base64 32` |
---
## ✅ After Making Changes
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/
```
---
**Remember:** Store passwords securely using a password manager! 🔐

View File

@@ -0,0 +1,379 @@
# ✅ 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 | /database/ → 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:**
- `/database/` - 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/database/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/database/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** 🎉

276
nginx/DEPLOY_NOW.md Normal file
View File

@@ -0,0 +1,276 @@
# 🚀 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
- `/database/` 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/database/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
```

View File

@@ -0,0 +1,346 @@
# 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/database/posterg.db
sudo chown www-data:posterg /var/www/html/database/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/database/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/database/posterg.db
sudo chmod 640 /var/www/html/database/posterg.db
sudo chmod 755 /var/www/html/database/
```
### 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
```

View File

@@ -11,36 +11,28 @@ This directory contains nginx configuration and setup scripts for the Post-ERG t
## 🚀 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
### 1. Deploy nginx configuration (automated)
```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
ssh posterg
sudo bash /tmp/deploy-production.sh
```
### 3. Set up SSL (production)
The deployment script will:
- ✅ Fix file permissions (posterg group)
- ✅ Set up admin password (if needed)
- ✅ Install nginx configuration
- ✅ Test and reload nginx
- ✅ Verify PHP-FPM is running
```bash
# On server
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d posterg.erg.be -d www.posterg.erg.be
```
### 2. SSL/TLS
SSL/TLS is handled by the upstream reverse proxy and is already working.
No additional SSL setup is needed on this server.
## 🔒 Security Features

View File

@@ -0,0 +1,352 @@
# 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/database/` 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/database/test.db` (test mode)
- `/var/www/html/database/posterg.db` (production mode)
### 5. Switch Back to Production
Simply remove the test database:
```bash
ssh posterg
rm /var/www/html/database/test.db
```
The site automatically switches to production database.
---
## 🔒 Permissions Explained
### Directory Permissions
```
drwxrwxr-x theophile posterg /var/www/html/database/
```
- **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/database/test.db
# Fix permissions
chmod 775 /var/www/html/database
chmod 660 /var/www/html/database/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/database/test.db-journal
rm -f /var/www/html/database/test.db-shm
rm -f /var/www/html/database/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/database/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/database/posterg.db /var/www/html/database/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/database/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/database/test.db"
```
Site automatically uses `posterg.db` again! 🚀

180
nginx/deploy-production.sh Executable file
View File

@@ -0,0 +1,180 @@
#!/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/database/posterg.db" ]; then
chmod 660 /var/www/html/database/posterg.db
chown www-data:posterg /var/www/html/database/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/database/posterg.db"
echo " • MD files blocked: curl -I http://localhost/README.md"
echo " • Shared blocked: curl -I http://localhost/shared/Database.php"
echo ""

24
nginx/fix-paths.sh Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Fix shared library paths for production deployment
echo "🔧 Fixing shared library paths for production..."
cd /var/www/html
# Fix paths in PHP files
find . -maxdepth 1 -name "*.php" -type f -exec sed -i "s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/shared/|g" {} \;
echo "✓ Updated paths in:"
echo " - index.php"
echo " - memoire.php"
echo " - search.php"
echo " - test_db.php"
# Test if it works
echo ""
echo "🧪 Testing..."
php -r "require_once '/var/www/html/shared/Database.php'; echo 'Database.php loads successfully\n';"
echo ""
echo "✅ Path fix complete!"
echo "Try: curl http://localhost/"

34
nginx/install-php-sqlite.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Install PHP SQLite extension
echo "🔧 Installing PHP SQLite extension..."
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Error: This script must be run as root (use sudo)"
exit 1
fi
# Detect PHP version
PHP_VERSION=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;")
echo "Detected PHP version: $PHP_VERSION"
# Install SQLite extension
echo "Installing php${PHP_VERSION}-sqlite3..."
apt-get update -qq
apt-get install -y php${PHP_VERSION}-sqlite3
# Restart PHP-FPM
echo "Restarting PHP-FPM..."
systemctl restart php${PHP_VERSION}-fpm
# Verify installation
if php -m | grep -q sqlite3; then
echo "✅ SQLite extension installed successfully"
echo ""
echo "Installed extensions:"
php -m | grep -i sqlite
else
echo "❌ Failed to install SQLite extension"
exit 1
fi

199
nginx/manage-admin-users.sh Executable file
View File

@@ -0,0 +1,199 @@
#!/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

View File

@@ -1,24 +1,23 @@
# Nginx configuration for Post-ERG thesis website
# Nginx configuration for Post-ERG thesis website (Production)
# Based on existing default config with security enhancements
# 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)
# Main server block
server {
listen 80;
listen [::]:80;
listen 80 default_server;
listen [::]:80 default_server;
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;
# Add index.php to the list
index index.php index.html index.htm;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
@@ -27,8 +26,8 @@ server {
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Disable server tokens
server_tokens off;
# Server tokens already disabled in nginx.conf
# server_tokens off;
# Max upload size (for thesis files)
client_max_body_size 100M;
@@ -38,48 +37,35 @@ server {
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)$ {
# Block access to hidden files (except .well-known for Let's Encrypt)
location ~ /\.(?!well-known).* {
deny all;
access_log off;
log_not_found off;
}
# Deny access to sensitive files
location ~* \.(md|txt|sql|sh|json)$ {
location ~* \.(md|txt|sql|sh|json|gitignore)$ {
deny all;
}
# Deny access to database files
location ~* \.db$ {
# Deny access to database directory
location ^~ /database/ {
deny all;
}
# Deny access to shared/ directory (PHP includes only)
location /shared/ {
location ^~ /shared/ {
deny all;
}
# Deny access to tests directory
location /tests/ {
deny all;
}
# Deny access to cache directory
location /cache/ {
# Deny access to data directory
location ^~ /data/ {
deny all;
}
# Admin panel - password protected
location /formulaire/ {
alias /var/www/html/formulaire/;
location ^~ /formulaire/ {
# HTTP Basic Authentication
auth_basic "Admin Access - Post-ERG";
auth_basic_user_file /etc/nginx/.htpasswd-posterg;
@@ -87,35 +73,42 @@ server {
# 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;
# Allow access to public assets without authentication
location ~ ^/formulaire/(css|js|images)/ {
auth_basic off;
}
# Additional security for admin
# PHP handling for admin
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
}
# Additional security headers for admin
add_header X-Robots-Tag "noindex, nofollow" always;
# Try to serve file, otherwise 404
try_files $uri $uri/ =404;
}
# Search endpoint - rate limiting
location /search.php {
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;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
}
# Public PHP files
# PHP files handler
location ~ \.php$ {
# Rate limiting for general PHP requests
limit_req zone=general burst=20 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_pass unix:/var/run/php/php8.4-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;
@@ -128,156 +121,20 @@ server {
access_log off;
}
# Root location
# PDF files (thesis documents)
location ~* \.pdf$ {
expires 7d;
add_header Cache-Control "public";
add_header Content-Disposition "inline";
}
# Root location - try files or 404
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 access to .htaccess files (if Apache's document root concurs with nginx's)
location ~ /\.ht {
deny all;
}
}

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;
}
}