# CyberPanel
Hosting control panel on kaburusvr.uk (Hetzner), built on LiteSpeed. Manages all client WordPress sites.
Access: `https://kaburusvr.uk:8090`
## Key paths
| Path | Purpose |
| —— | ——— |
| `/home/{domain}/public_html/` | Site web root |
| `/usr/local/lsws/lsphp83/bin/lsphp` | PHP 8.3 binary |
| `/usr/local/lsws/conf/vhosts/` | Per-site vhost configs |
## ⚠ Known quirks — read before creating sites
1. Wrong PHP path from CLI — CyberPanel CLI creates vhosts with `lsphpPHP83` instead of `lsphp83`. Always verify and correct after creating a site via CLI.
2. Site not registered in DB — New sites created via CLI are not inserted into the CyberPanel database. Either create via UI, or insert manually:
```sql INSERT INTO cyberpanel.websiteFunctions_websites (domain, adminEmail, phpSelection, ssl, state, externalApp, config, BackupLock, admin_id, package_id) VALUES ('domain.com', '[email protected]', 'PHP 8.3', 1, 1, 'linuxuser', '', 0, 1, 1); ```
3. Missing .user.ini — New sites need `.user.ini` created in `public_html` with `open_basedir` setting.
4. public_html permissions — Must be `chmod 755` for LiteSpeed to serve files.
5. .user.ini permissions — Must be `chmod 640`. Wordfence flags `644` as a Critical vulnerability. All future site migrations must include this step — already documented in site-migration.
## Wordfence Extended Protection on OLS
Background: Wordfence's WAF requires `auto_prepend_file` to load its bootstrap before WordPress on every request. Without this, WAF rule updates (cron) never fire and the WAF UI shows “undefined NaN” for next update time.
The problem: Wordfence's own optimization wizard writes `auto_prepend_file` to `.htaccess` and `.user.ini`. OLS ignores both. This is a documented OLS limitation — `php_value auto_prepend_file` in `.htaccess` does not work on OpenLiteSpeed. Sites migrated from Apache/cPanel hosting silently lose Extended Protection.
Reference: https://openlitespeed.org/kb/enable-wordfence-on-openlitespeed/
The fix: Add `php_admin_value auto_prepend_file` to the OLS vhost conf `phpIniOverride` block directly. This is the OLS-native equivalent of what WF's wizard tries to do via `.htaccess`.
### Applying the fix to a site
Edit `/usr/local/lsws/conf/vhosts/{domain}/vhost.conf` and add to the `phpIniOverride` block:
``` phpIniOverride { php_value upload_max_filesize 64M php_value post_max_size 128M php_value memory_limit 256M php_value max_execution_time 300 php_value max_input_time 300 php_admin_value auto_prepend_file /home/{domain}/public_html/wordfence-waf.php } ```
⚠️ Use `php_admin_value` not `php_value` — only `php_admin_value` is honoured for `auto_prepend_file` by OLS.
Then restart OLS and kill lsphp: ```bash killall lsphp /usr/local/lsws/bin/lswsctrl restart ```
### Verifying it worked
```bash # Drop this file and curl it — must show WFWAF_AUTO_PREPEND: 1 cat > /home/{domain}/public_html/wf-check.php « 'EOF' <?php echo (defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND) ? 'OK' : 'FAIL'; unlink(FILE); EOF curl -s https://{domain}/wf-check.php ```
⚠️ `wp eval` will always show “WAF auto prepend active: No” — WP-CLI is CLI, not a web request, so the prepend never fires. Use the curl method above to verify.
### After applying — trigger cron reschedule
After the prepend is active, hit the site a few times to let the bootstrap `runCron()` fire the stale events and reschedule:
```bash for i in 1 2 3; do curl -s https://{domain}/ > /dev/null; done ```
Then verify cron events are in the future: ```bash wp –path=/home/{domain}/public_html –allow-root eval ' $cron = (array) wfWAF::getInstance()→getStorageEngine()→getConfig(“cron”, null, “livewaf”); $now = time(); foreach ($cron as $e) {
echo get_class($e) . ": " . date("Y-m-d H:i", $e->getFireTime()) . ($e->getFireTime() > $now ? " [OK]" : " [STALE]") . "\n";
} ' ```
### Estate-wide application (2026-06-13)
Applied to all 22 WF sites on kaburusvr via Python script editing `vhost.conf` directly. All sites confirmed working via curl checks. WAF cron rescheduled on all sites — rule updates will resume on schedule.
