Initial commit: complete WebsiteBox project
Docker-based self-hosted WordPress deployment system with: - Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot) - Automatic SSL via Let's Encrypt with self-signed fallback - First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins) - Interactive setup wizard and one-line install script - Backup, update, healthcheck, and SSL renewal scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
63
docs/SECURITY.md
Normal file
63
docs/SECURITY.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Security Practices
|
||||
|
||||
## Container Security
|
||||
|
||||
- All containers use minimal Alpine-based images where possible
|
||||
- MariaDB is not exposed to the host network — it communicates only with WordPress on the internal Docker network
|
||||
- Only ports 80 (HTTP) and 443 (HTTPS) are exposed to the host
|
||||
- No containers run in privileged mode
|
||||
- The Docker socket is never mounted into any container
|
||||
- All containers use `restart: unless-stopped` (not `always`)
|
||||
|
||||
## SSL/TLS
|
||||
|
||||
- Let's Encrypt certificates are acquired automatically on first boot
|
||||
- Certificates are renewed automatically every 12 hours (per Let's Encrypt recommendation)
|
||||
- TLS 1.2+ only (TLS 1.0 and 1.1 are disabled)
|
||||
- Strong cipher suites with forward secrecy
|
||||
- HSTS header with a 2-year max-age
|
||||
- OCSP stapling enabled
|
||||
- SSL session tickets disabled for forward secrecy
|
||||
|
||||
## WordPress Hardening
|
||||
|
||||
- **`DISALLOW_FILE_EDIT`**: The theme and plugin file editor in wp-admin is disabled. This prevents an attacker with admin access from injecting code via the editor. Note: this does NOT block plugin/theme updates via the admin GUI — those still work normally.
|
||||
- **XML-RPC disabled**: The XML-RPC endpoint is disabled both at the application level (via mu-plugin) and at the nginx level (returns 403). XML-RPC is a common attack vector for brute-force and DDoS amplification.
|
||||
- **Non-standard table prefix**: Tables use `wbox_` instead of the default `wp_`, which mitigates automated SQL injection attacks targeting default table names.
|
||||
- **Strong passwords**: Database passwords and WordPress salts are auto-generated using cryptographic randomness (`openssl rand`). Admin passwords must be at least 12 characters.
|
||||
- **Post revisions limited**: WordPress stores a maximum of 10 revisions per post to limit database growth.
|
||||
- **Minor auto-updates**: WordPress core security patches are applied automatically.
|
||||
|
||||
## Nginx Security
|
||||
|
||||
- **Rate limiting**: `wp-login.php` is rate-limited to 1 request/second with a burst of 3, mitigating brute-force login attempts.
|
||||
- **Security headers**: HSTS, X-Content-Type-Options, X-Frame-Options (SAMEORIGIN), X-XSS-Protection, Referrer-Policy.
|
||||
- **PHP upload restrictions**: Blocked in `uploads/` directory at nginx level.
|
||||
- **Hidden files blocked**: Dotfiles (`.htaccess`, `.git`, etc.) return 403.
|
||||
|
||||
## Wordfence
|
||||
|
||||
Wordfence is pre-installed and provides:
|
||||
- Application-level web application firewall (WAF)
|
||||
- Brute-force login protection
|
||||
- Malware scanning
|
||||
- Real-time threat intelligence
|
||||
|
||||
Note: Wordfence's `.htaccess`-based WAF rules do not apply to nginx. The nginx rate limiting and Wordfence's application-level firewall provide equivalent protection.
|
||||
|
||||
## Secrets Management
|
||||
|
||||
- All secrets are stored in `.env` with permissions set to `600` (owner read/write only)
|
||||
- The `.env` file is gitignored and never committed to the repository
|
||||
- Auto-generated admin passwords are stored temporarily in `.credentials` — users are instructed to delete this file after recording the password
|
||||
- WordPress salts are generated uniquely per installation
|
||||
|
||||
## Network Architecture
|
||||
|
||||
```
|
||||
Internet → :80/:443 → nginx → wordpress (FastCGI :9000) → db (:3306 internal only)
|
||||
↕
|
||||
certbot (ACME challenges)
|
||||
```
|
||||
|
||||
Only nginx is reachable from the internet. All other services communicate on the internal Docker network (`websitebox_internal`).
|
||||
187
docs/TROUBLESHOOTING.md
Normal file
187
docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Check Container Health
|
||||
|
||||
```bash
|
||||
./scripts/healthcheck.sh
|
||||
```
|
||||
|
||||
Or individually:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -f nginx
|
||||
docker compose logs -f wordpress
|
||||
docker compose logs -f db
|
||||
```
|
||||
|
||||
## SSL Certificate Issues
|
||||
|
||||
### "Setting up SSL..." page persists
|
||||
|
||||
Your DNS is not pointing to this server yet.
|
||||
|
||||
1. Verify DNS: `dig yourdomain.com`
|
||||
2. The A record should show your server's IP
|
||||
3. Once DNS is correct, restart nginx:
|
||||
```bash
|
||||
docker compose restart nginx
|
||||
```
|
||||
|
||||
### Certificate renewal fails
|
||||
|
||||
Check certbot logs:
|
||||
|
||||
```bash
|
||||
docker compose logs certbot
|
||||
```
|
||||
|
||||
If you've hit Let's Encrypt rate limits, wait and retry. For testing, you can use the staging environment by editing `nginx/entrypoint.sh` and adding `--staging` to the certbot command.
|
||||
|
||||
### Force certificate re-acquisition
|
||||
|
||||
```bash
|
||||
rm -rf websitebox-data/certs/live/yourdomain.com
|
||||
docker compose restart nginx
|
||||
```
|
||||
|
||||
## WordPress Issues
|
||||
|
||||
### WordPress first-boot setup failed partially
|
||||
|
||||
The admin dashboard will show a warning banner if setup was incomplete.
|
||||
|
||||
To retry:
|
||||
|
||||
```bash
|
||||
docker compose restart wordpress
|
||||
```
|
||||
|
||||
The setup will re-run idempotently — it's safe to run multiple times.
|
||||
|
||||
### Force a clean WordPress setup
|
||||
|
||||
To re-run the entire first-boot setup from scratch:
|
||||
|
||||
```bash
|
||||
docker compose exec wordpress rm /var/www/html/.websitebox-setup-complete /var/www/html/.websitebox-setup-partial
|
||||
docker compose restart wordpress
|
||||
```
|
||||
|
||||
### WordPress shows "Error establishing a database connection"
|
||||
|
||||
1. Check that the database container is healthy:
|
||||
```bash
|
||||
docker compose ps db
|
||||
docker compose logs db
|
||||
```
|
||||
2. Verify `.env` database credentials match what MariaDB was initialized with
|
||||
3. If you changed database passwords after first run, you'll need to reset the database:
|
||||
```bash
|
||||
docker compose down
|
||||
rm -rf websitebox-data/database
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Can't upload files / upload size limit
|
||||
|
||||
The default upload limit is 64MB. If you need larger uploads, edit `wordpress/uploads.ini`:
|
||||
|
||||
```ini
|
||||
upload_max_filesize = 128M
|
||||
post_max_size = 128M
|
||||
```
|
||||
|
||||
Also update `nginx/nginx.conf`:
|
||||
|
||||
```nginx
|
||||
client_max_body_size 128M;
|
||||
```
|
||||
|
||||
Then rebuild:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
## Database Issues
|
||||
|
||||
### MariaDB won't start
|
||||
|
||||
Check logs:
|
||||
|
||||
```bash
|
||||
docker compose logs db
|
||||
```
|
||||
|
||||
Common causes:
|
||||
- Corrupted data: `rm -rf websitebox-data/database && docker compose up -d` (WARNING: destroys all data)
|
||||
- Disk full: `df -h`
|
||||
|
||||
### Connect to database directly
|
||||
|
||||
```bash
|
||||
docker compose exec db mariadb -u websitebox -p websitebox
|
||||
```
|
||||
|
||||
## Docker Issues
|
||||
|
||||
### "permission denied" when running docker commands
|
||||
|
||||
```bash
|
||||
sudo usermod -aG docker $USER
|
||||
# Log out and back in, or run:
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
### Containers won't start after VPS reboot
|
||||
|
||||
```bash
|
||||
sudo systemctl start docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Port 80 or 443 already in use
|
||||
|
||||
Find what's using the port:
|
||||
|
||||
```bash
|
||||
sudo ss -tlnp | grep ':80 '
|
||||
sudo ss -tlnp | grep ':443 '
|
||||
```
|
||||
|
||||
Common culprits: Apache (`sudo systemctl stop apache2`), another nginx instance, or Caddy.
|
||||
|
||||
## Backup Issues
|
||||
|
||||
### UpdraftPlus backup fails
|
||||
|
||||
Check that the backup directory is writable:
|
||||
|
||||
```bash
|
||||
docker compose exec wordpress ls -la /var/backups/websitebox/
|
||||
```
|
||||
|
||||
### Manual backup fails
|
||||
|
||||
```bash
|
||||
./scripts/backup.sh
|
||||
```
|
||||
|
||||
If this fails, check that the WordPress and database containers are running:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
## Resetting Everything
|
||||
|
||||
To completely reset WebsiteBox (WARNING: destroys all site data):
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
rm -rf websitebox-data/
|
||||
rm .env .credentials
|
||||
./setup.sh
|
||||
docker compose up -d
|
||||
```
|
||||
59
docs/UPDATING.md
Normal file
59
docs/UPDATING.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Updating WebsiteBox
|
||||
|
||||
## Quick Update
|
||||
|
||||
```bash
|
||||
cd ~/websitebox
|
||||
./scripts/update.sh
|
||||
```
|
||||
|
||||
The update script will:
|
||||
|
||||
1. Check for local modifications to tracked files (warns and aborts if found)
|
||||
2. Show you what's changing before applying
|
||||
3. Ask for confirmation
|
||||
4. Pull the latest code
|
||||
5. Pull updated Docker base images
|
||||
6. Rebuild containers with any changes
|
||||
7. Clean up old Docker images
|
||||
8. Run any required migrations
|
||||
9. Verify all containers are healthy
|
||||
|
||||
## What Gets Updated
|
||||
|
||||
| Component | How it updates |
|
||||
|-----------|---------------|
|
||||
| WebsiteBox configs (nginx, docker-compose, entrypoints) | `update.sh` pulls from git |
|
||||
| Base Docker images (nginx, MariaDB) | `docker compose pull` via update script |
|
||||
| WordPress core (minor/security) | Automatic via WordPress auto-updates |
|
||||
| WordPress core (major) | Manual via WordPress admin GUI |
|
||||
| Plugins (Wordfence, UpdraftPlus, Age Gate) | Manual via WordPress admin GUI |
|
||||
| GeneratePress parent theme | Manual via WordPress admin GUI |
|
||||
| WebsiteBox child theme | `update.sh` pulls from git |
|
||||
|
||||
## Before Updating
|
||||
|
||||
- Your site data in `websitebox-data/` is never modified by updates
|
||||
- If you've edited any tracked files (Dockerfiles, nginx configs, etc.), the update script will warn you
|
||||
- Consider running a backup first: `./scripts/backup.sh`
|
||||
|
||||
## After Updating
|
||||
|
||||
Check that everything is working:
|
||||
|
||||
```bash
|
||||
./scripts/healthcheck.sh
|
||||
```
|
||||
|
||||
Visit your site and wp-admin to verify normal operation.
|
||||
|
||||
## Rollback
|
||||
|
||||
If an update causes issues:
|
||||
|
||||
```bash
|
||||
cd ~/websitebox
|
||||
git log --oneline -5 # Find the previous commit
|
||||
git checkout <commit-hash> # Revert to that commit
|
||||
docker compose up -d --build # Rebuild with previous code
|
||||
```
|
||||
Reference in New Issue
Block a user