Security Architecture — Whitepaper¶
Guiding principle: not "secure", but provably secure. We never claim a protection layer that isn't real, active and verifiable. Honesty over marketing.
This document describes how enconf isolates customers from one another on a shared server, how we make that isolation verifiable, and — just as important — where the physical limits are.
The one hard truth¶
100% isolation on a shared kernel is physically impossible. This holds for every single-server hosting panel (Plesk, cPanel, DirectAdmin, CloudPanel …), not just enconf. A local kernel privilege-escalation CVE, a MariaDB CVE or a PHP-binary CVE affects, in principle, every tenant of the same instance. True hard isolation exists only with a dedicated VM or container per customer.
So "nobody gets to the basics" means, honestly:
- Defense in depth — every layer must be broken individually.
- Verifiability — every layer is verified active and visible to the customer.
- Timely patching — the indivisible basics (kernel, MariaDB, PHP, Postfix/Dovecot) are updated as fast as possible.
Point 2 is our claim: provably secure, not claimed secure.
Defense in depth — the active layers¶
Every row corresponds to a real, system-verifiable mechanism. They are brought up to date on existing sites too, via an idempotent migration on updates.
Process & filesystem isolation¶
| Layer | What it does |
|---|---|
| One Linux user per website | Own UID/GID; standard Unix DAC. Other sites cannot read the files. |
Webroot 0710 user:www-data |
Owner full, Nginx may traverse, everyone else 0 — no cd from outside. |
| PHP-FPM pool per website | PHP runs as the site's identity, no shared www-data. |
open_basedir |
PHP is confined to home + its own /tmp + system PHP. |
disable_functions |
exec, system, shell_exec, proc_open, popen, all pcntl_* and more blocked. |
| FFI disabled | ffi.enable=0 — closes the FFI::cdef→libc bypass of disable_functions. |
Per-user /tmp/<user> 0700 |
Own path for sessions/uploads — no cross-tenant leak via shared /tmp. |
| AppArmor profile per customer | Own profile per PHP-FPM; switchable per server between complain and enforce mode, with automatic fallback on a burst of false positives. |
| systemd slice per customer | MemoryMax / CPUQuota / TasksMax / IO — applies to new and existing sites. |
Database & secrets¶
| Layer | What it does |
|---|---|
| Per-customer MariaDB/PostgreSQL grants | GRANT … ON customer_db.* strictly — no global rights, no cross-DB queries. |
Panel secrets 0600 root:root |
JWT_SECRET, DB_PASSWORD, AGENT_TOKEN are not readable by customer PHP. |
Network & mail¶
| Layer | What it does |
|---|---|
| nftables SMTP egress block | Only root/Postfix may reach port 25 outbound — no direct-to-MX spam from customer processes. |
| GeoIP filter with lock-out safety | Country blocks with an allowlist for the admin/controller IP. |
| fail2ban | Brute-force protection for SSH, FTP, mail and panel login. |
Nginx upload .php block |
.php in upload directories is not executed — closes the most common WordPress hack pattern. |
Kernel hardening¶
kernel.kptr_restrict, kernel.dmesg_restrict, yama.ptrace_scope,
unprivileged_bpf_disabled, bpf_jit_harden, perf_event_paranoid are set. User
namespaces are deliberately left enabled, since rootless containers/gVisor need them.
Verifiability — the decisive difference¶
Security you cannot see is, to a customer, indistinguishable from marketing. So enconf makes the isolation visible and continuously checked:
- Per-customer security score: each customer sees, in their dashboard, an isolation
score that shows per website which protection layers (dedicated PHP process,
open_basedir,disable_functions, FFI off, sealed-off directory, AppArmor, resource limits …) are actually active — every row read back live from the server, not claimed. - Security Advisor (admin): server-wide audit checklist, SBOM (CycloneDX), a live CVE feed, and an evidence & export section for customers who are themselves manufacturers.
- Central SIEM log forwarding: security-relevant logs are forwarded fleet-wide over TLS to a collector.
Supply chain & patch discipline¶
- SBOM + live CVE feed (OSV / Debian security tracker) report vulnerable components proactively.
- Signed APT repository (GPG-signed release metadata) for authentic updates.
- Reproducible builds + checksums: packages are built deterministically
(
-trimpath, fixed timestamps), so rebuilding from source yields byte-identical artifacts; aSHA256SUMSlist is published per release. Anyone can verify their install independently — not "trust us", but check for yourself. - Vendor repos pinned with key self-heal.
- Automatic OS security updates (
unattended-upgrades). - Evidence & export section supports customers who are themselves manufacturers of a product with digital elements with their EU Cyber Resilience Act supply-chain evidence (incident register, SBOM, CVE cross-check, export) — it is a tool, not a certification, and not relevant to most customers (see Security Advisor).
What we deliberately do NOT do¶
- No on-server WAF. A WAF belongs upstream at the edge/reverse proxy, not on the hosting server. On-server WAFs are fragile in practice and create a false sense of security. WAF protection happens exclusively upstream.
- No claimed isolation without verification. Every row of the security score must correspond to a real, checked mechanism.
Limits — named honestly¶
These points are architectural and cannot be fixed by panel layers; they affect every single-kernel system equally:
- Kernel — all customers share one kernel. A local privesc CVE affects all.
- MariaDB/PostgreSQL process — one instance per server.
- PHP binary — all pools of the same version share the same code.
- Postfix/Dovecot/PowerDNS — one instance each for all domains.
The mitigation is the same as at any serious provider: timely updates of the indivisible basics. Anyone who needs true kernel isolation runs customers in dedicated VMs or containers — enconf optionally offers rootless container isolation (gVisor) as a stronger tier for untrusted workloads.
This whitepaper is maintained alongside the security model. Current as of: see changelog.