Private
Public Access
1
0

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:
constantprojects
2026-02-20 15:24:23 -07:00
commit a440026701
32 changed files with 3397 additions and 0 deletions

63
docs/SECURITY.md Normal file
View 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
View 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
View 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
```