Skip to content

Security

enconf provides multiple security layers to protect the panel and hosted websites.


Customer isolation (defence in depth)

enconf isolates every customer across several independent layers. Worth being clear about: 100% isolation on a single-kernel system is physically impossible — that takes containers or VMs. What we build is defence in depth: each layer has to be defeated separately for a cross-tenant attack to succeed.

Active and rolled out automatically

Layer What it protects
A Linux user per customer Standard Unix DAC. Files belong to the customer and are not readable by others.
Webroot mode 0710 user:www-data The owner may do everything, nginx (www-data) may traverse, every other customer has no access — they cannot even cd into the directory.
A PHP-FPM pool per customer Its own user and group — PHP runs as the customer, not as a shared www-data process.
open_basedir PHP requests can only read and write within <docroot>:<tmpDir>:/usr/share/php:/var/cache/php.
disable_functions exec, passthru, shell_exec, system, popen, proc_open, dl and every pcntl_* are disabled — no shell access through PHP.
Per-customer /tmp/<user> mode 0700 Its own path for session.save_path, upload_tmp_dir and sys_temp_dir. No cross-tenant session or upload leak through a shared /tmp or /var/lib/php/sessions/.
MariaDB grants per customer GRANT … ON customer_db.* strictly — no user holds global rights, cross-database queries are blocked.
Panel secrets 0600 root:root JWT_SECRET, DB_PASSWORD and AGENT_TOKEN are not readable by customer PHP pools.
ProFTPD DefaultRoot ~ FTP and SFTP users are chrooted into their own home and cannot navigate to other customers or system paths.
Nginx upload path block .php inside upload directories is not executed — this closes the most common WordPress attack pattern.
HTTPS catch-all (incl. HTTP/3) A name no vhost serves gets a neutral, self-signed certificate and a 404 response — never a customer's certificate. This also holds for HTTP/3 (QUIC); there the server used to hand out the certificate of the first customer in the configuration.
nftables SMTP egress block Customer PHP cannot connect directly to foreign SMTP servers, which blocks spam. Authenticated submission on 587 stays open.
FFI disabled ffi.enable=0 closes the FFI::cdef route to libc that would otherwise sidestep disable_functions.
An AppArmor profile per customer Its own profile per PHP-FPM, switchable between complain and enforce mode per server, with automatic fallback when false positives pile up.
A systemd slice per customer MemoryMax, CPUQuota, TasksMax and IO limits — applied to new and existing sites.
Kernel hardening (sysctl) kptr_restrict, dmesg_restrict, ptrace_scope, unprivileged_bpf_disabled and others.
fail2ban Brute-force protection for SSH, FTP, mail and the panel login.
Per-customer nginx logs Logs live under /var/www/customers/<user>/logs/ — no cross-tenant log reading.
Hardening migration on agent start Existing sites are brought up to the current hardening state automatically on update, idempotently.

Demonstrability (active)

Layer Status
Per-customer security score Every customer sees live in the dashboard which isolation layers are actually active per website.
Security Advisor Server-wide audit checklist, SBOM, live CVE feed, evidence and export.
SIEM log forwarding Security logs shipped fleet-wide to a collector over TLS.

In depth: see the security architecture whitepaper.

Planned (phase 2)

Layer Status
AppArmor enforce fleet-wide The per-server enforce toggle is built; the broad rollout waits for enough complain-mode observation data.
True kernel isolation as the default Rootless containers (gVisor) are available opt-in; not yet the default for untrusted workloads.

What fundamentally is not isolated

These points apply to every single-kernel, single-MariaDB system — Plesk, cPanel and CyberPanel included — and cannot be fixed by a panel layer:

  • The Linux kernel — every customer shares one. Mitigation: apply kernel updates promptly.
  • The MariaDB process — one instance for everyone. Mitigation: updates.
  • The PHP binary — all pools on the same PHP version share the binary code.
  • Postfix, Dovecot and PowerDNS — one instance for all domains.

Two-Factor Authentication (2FA)

2FA protects panel access through an additional authentication factor beyond the password.

Enable 2FA

  1. Navigate to Settings > Security (or Settings > Password)
  2. Click Set Up 2FA
  3. Scan the QR code with an authenticator app:
  4. Google Authenticator
  5. Authy
  6. Microsoft Authenticator
  7. FreeOTP
  8. Enter the displayed 6-digit code
  9. Click Confirm

Backup Codes

Note the secret key displayed below the QR code. You can use it to restore access if you lose your device.

Disable 2FA

  1. Navigate to Settings > Security
  2. Click Disable 2FA
  3. Enter your current password for confirmation
  4. Click Disable

Login with 2FA

  1. Enter email and password
  2. Enter the 6-digit code from your authenticator app
  3. Click Sign In

TOTP Protocol

WebPanel uses the TOTP protocol (Time-based One-Time Password) as per RFC 6238.


Enforce 2FA Policy

As an administrator, you can require specific user groups to set up two-factor authentication.

Configuration

  1. Navigate to Settings > Security
  2. Select the desired policy under Enforce 2FA:
Option Description
Disabled 2FA is optional for all users
Admins Admins must set up 2FA
Admins & Resellers Admins and resellers must set up 2FA
All Users All users (including customers) must set up 2FA
  1. Click Save

Behavior for Affected Users

Users subject to the policy who have not yet set up 2FA:

  • See a yellow warning banner in the panel after login
  • Can continue to use the panel normally
  • Are prompted to set up 2FA under Settings

Recommendation

Enable the policy at minimum for admins to protect panel accounts with elevated privileges.


Brute-Force Protection

The built-in brute-force protection locks IP addresses after too many failed login attempts.

Configuration

Navigate to Settings > Security > Login Protection:

Setting Default Description
Enabled Yes Enable/disable brute-force protection
Max. Failed Attempts 5 Number of allowed attempts
Lockout Duration (Minutes) 15 Duration of the lockout

Login protection IP whitelist

IPs that are never locked out:

  1. Navigate to Settings > Security > Login Protection
  2. Enter trusted IP addresses
  3. These IPs will not be locked out even after failed attempts

Whitelist Your Own IP

Add your own static IP address to the whitelist to prevent accidentally locking yourself out.

Not the same as IP Access Restriction

This list only decides who is never locked out after failed logins. It grants no access and denies none. The feature that restricts who may reach the panel at all is a different one — see below.


IP Access Restriction (panel access)

Restricts access to the admin panel to selected IP addresses. Unlike the login protection whitelist, this is a gate: anything not on the list is refused.

  1. Navigate to Settings > Security
  2. Enable IP whitelist enabled
  3. Under Allowed IP Addresses, enter one address per line

Single addresses and CIDR ranges are accepted, IPv4 as well as IPv6:

203.0.113.5
198.51.100.0/24
2001:db8::1
2001:db8::/64

What the restriction covers

It applies to every administrative request and is evaluated before you are identified. If your address is not on the list, it is not only saving that fails but loading as well: the dashboard reads zero everywhere, settings pages look empty, and the restriction itself appears to be switched off. Nothing is lost — the panel is simply refusing every request, including the one that would show you the current state.

Entering the right address

The form shows you the address the server actually sees you coming from. Use that one, not the answer from a "what is my IP" service. The two answer different questions and routinely differ — for instance when your browser reaches the panel over IPv6 while that lookup went out over IPv4, or when a router or provider proxy sits in between.

Saving is refused if it would lock you out

Since version 2.9.83 the panel checks whether your own address is covered by the list you are saving. If it is not, nothing is saved and the message names the address to add instead. Locking yourself out through the interface is no longer possible.

Access from the server itself always stays open

Requests from 127.0.0.1 and ::1 are always permitted, whatever the list says. That is the way back in should the restriction ever become too narrow — for example because your address changed.

Undoing a lockout

Connect to the server via SSH as root. The log shows which address the panel actually sees:

journalctl -u enconf-api | grep "IP whitelist: access denied" | tail -5

Switch the restriction off:

DB=$(grep -oP '^DB_NAME=\K.*' /etc/enconf/api.env | tr -d '"')
sudo -u postgres psql -d "$DB" \
  -c "UPDATE panel_settings SET value='false' WHERE key='panel_ip_whitelist_enabled';"
systemctl restart enconf-api

The panel is then fully reachable again and every setting is exactly as you left it.

Changes are not instant

The list is cached for up to 60 seconds. Without restarting the service a change can therefore take a moment to take effect.


Password Policy

The password policy applies to all users (admin, reseller, customers):

Setting Default Description
Minimum Length 12 Minimum number of characters
Uppercase No At least one uppercase letter
Lowercase No At least one lowercase letter
Digits No At least one digit
Special Characters No At least one special character

Why no character classes are enforced

The default follows NIST 800-63B: length protects better than enforced character classes. A long passphrase such as correct-horse-battery-staple is stronger than a short P4ssw!rd — yet a special-character rule would reject it.

You can switch the character classes on at any time if a policy in your organisation requires it. Bear in mind that doing so also rules out strong passphrases.

Configuration

  1. Navigate to Settings > Security > Password Policy
  2. Adjust the desired rules
  3. Click Save

Audit Log

The audit log records all security-relevant actions in the panel.

Logged Events

Event Description
Login Successful and failed logins
Logout Logouts
Password Change Password changes for all users
2FA Change Activation/deactivation of 2FA
Impersonation Admin logs in as customer/reseller
Customer Created/Deleted Customer management actions
Subscription Created/Deleted Subscription actions
Settings Changed Changes to panel settings
API Key Created/Deleted API key management
Firewall Rule Changed Firewall changes

View Audit Log

  1. Navigate to Audit Log
  2. The table shows all logged events:
Column Description
Timestamp Date and time
User Executing user
Action Type of action
Details Additional information
IP Address Source IP of the user

Filters

  • Time Range — Filter by date
  • User — Filter by user
  • Action — Filter by action type

Security Advisor

The Security Advisor checks the security configuration and provides recommendations:

Checked Areas

Area Check
Firewall Is the firewall active?
fail2ban Is fail2ban active and configured?
2FA Is 2FA enabled for the admin?
SSL Do all domains have valid SSL certificates?
Password Policy Is the policy sufficiently strong?
PHP Versions Are outdated PHP versions in use?
Updates Are system updates available?
SSH Is the SSH configuration secure?
open_basedir Configured for all sites?
disable_functions Dangerous functions disabled?

Ratings

Each check receives a rating:

  • Passed (Green) — Securely configured
  • Warning (Yellow) — Improvement recommended
  • Critical (Red) — Immediate action required

Customer Isolation

Every hosting account is fully isolated:

Linux Level

  • Dedicated Linux user per subscription
  • PHP-FPM pool runs under the customer's user
  • open_basedir restricts PHP to the customer's directory
  • disable_functions disables dangerous PHP functions
  • systemd slice limits CPU, RAM and processes

Network Level

  • nftables blocks outgoing SMTP connections from customer processes
  • Customers cannot send emails directly (only through the mail server)

Web Server Level

  • Nginx blocks PHP execution in upload directories
  • Each site has its own vhost configuration
  • Access restrictions per directory are possible

SSH Hardening

Recommended SSH settings (in /etc/ssh/sshd_config):

Setting Recommendation Description
PermitRootLogin no Disable root login
PasswordAuthentication no Key-based authentication only
Port Non-standard SSH on a port other than 22
MaxAuthTries 3 Max. login attempts
AllowUsers Specific Allow only specific users

Ensure Access

Before making changes to the SSH configuration, ensure you have alternative access methods (e.g. console at your hosting provider).


Login anomaly detection

On top of the IP-based brute-force lockout (fail2ban + account lockout after too many failed attempts), the panel watches sign-in patterns and alerts on two suspicious signals:

  • Sign-in from an unknown network: when an admin or reseller account signs in from a network (IPv4 /24, IPv6 /48) it has never used before, a notification is raised. An account's very first login establishes its baseline and deliberately does not alert.
  • Distributed login attack: when a single account is hit with many failed logins from several IPs within 30 minutes, the panel reports it — even when no single IP reaches the lockout threshold (the "low-and-slow" attack the per-IP lockout misses).

Both signals are alert-only (no automatic lockout) and enabled out of the box. Disable via the panel setting login_anomaly_enabled=false; tune the distributed-attack threshold via login_anomaly_account_threshold (default 15).

Restoring administrator access

If you are locked out of the panel — forgotten password, a login address mistyped while changing it, or the suspicion that someone else has taken over the account — restore access on the server. A command-line tool does this; it runs only as root (over SSH, with sudo) and therefore needs neither a password nor a session:

sudo enconfctl admin list                                   # admin accounts, login address, 2FA
sudo enconfctl admin reset-password <address> [-p <password>]
sudo enconfctl admin set-email <old-address> <new-address> [--sync-admin-email]
  • reset-password sets a new password (without -p one is generated and shown) and ends all existing sessions of that account. The tool deliberately bypasses the password policy.
  • set-email changes the login address. Like the panel it checks that no other address is the same (ignoring upper/lower case) and leaves 2FA, password and sessions untouched. The operator contact address (admin_email, also the Let's Encrypt and MTA-STS contact) follows the login address only if the two matched before or admin_email was empty; if it differs it is left alone and the tool prints the command with --sync-admin-email in case it should follow.

Suspected takeover (for example after the notice "Your sign-in address was changed" that you did not trigger): first set-email back to your address, then reset-password, then revoke unknown sessions under Security → Sessions and check 2FA. "Forgot password" in the panel does not help here: the link goes to the stored login address, which is the new one.

Repeated wrong passwords when changing the password, the login address or 2FA lock that check for the account (default: 5 failed attempts in 15 minutes, the same limits as the login, see Brute-Force Protection). The lock then applies to the correct password too, until the time has passed.