SpipCP
Platform

Security

The security protocols SpipCP is built on — how secrets, access, the fleet, and your hosted sites are protected, and how each control is tested.

Security in SpipCP isn't bolted on the side — it's how the system is built. This page is a plain-language tour of the protections in place, grouped by what they protect: the panel, the fleet, and your hosted sites. Each control here is backed by an automated test or kept in place by the system, not just a promise.

Secrets & encryption

Everything sensitive is encrypted at rest with one method — SSH enroll keys, agent tokens, database and site credentials, SMTP tokens, deploy keys, and every DNS provider token (Cloudflare, Bunny, Hetzner, deSEC, Gcore). All of it is AES-256-GCM, under a single MASTER_KEY. In production the panel refuses to boot if that key is missing, a placeholder, or shorter than 32 characters — so it can never serve traffic while silently unable to encrypt, and a weak, brute-forceable key is rejected up front (generate a real one with openssl rand -hex 32).

A DNS provider token (like a git token or an SSL-issuer credential) never reaches a node: it's decrypted only for the panel's own call to that provider, and never returned to a client. The one exception is self-hosted DNS — when you run your own nameservers, each PowerDNS box's API key is generated and placed by the agent on that box itself, because the box is the nameserver. Even then it's never returned to a client; the panel talks to each box over its API.

ControlWhat it means
Encrypted at restAll secrets are stored as ciphertext (AES-256-GCM), with one key.
Audited revealViewing a credential (DB password, access key) is one-time and writes an audit row.
No secrets in the openNone in logs (redacted), none in images, none in .env beyond the boot essentials.
Timing-safe comparisonTokens and signatures are compared in a way that resists timing attacks.

Sign-in & sessions

Login is password + TOTP two-factor, with recovery codes for a lost authenticator. Sessions use HTTP-only, Secure, SameSite cookies with CSRF protection and short lifetimes. The first account is made admin under a database lock so two admins can't be created at once, and login never depends on email — so a broken mailer can't lock you out of your own infrastructure.

Access control (RBAC)

Access is checked on the server for every action. Every action declares the permission it needs, and an automated test checks every action against every role (anonymous, operator, admin, API key) to prove the access is correct. Hiding a menu in the UI is a convenience; the server check is the real boundary.

Input & output safety

ControlWhat it means
Validated inputEvery API input is checked (lengths, formats, allowed values) before it's used.
No SQL injectionAll queries are parameterized; LIKE patterns are escaped.
No path traversalEvery user-supplied path is resolved and checked to stay in bounds (file manager, backups, uploads).
No shell-string buildingThe panel never runs a shell, and the agent never builds commands from strings.
Safe outputOutput is HTML-escaped; log streams are sanitized.

The HTTP edge

The panel's own reverse proxy sets a strict set of security headers — HSTS, a locked-down Content-Security-Policy (frame-ancestors 'none', object-src 'none'), nosniff, a strict referrer policy, and a permissions policy that disables camera/mic/geolocation — and tests check they're present. Expensive actions (enroll, reconcile-all, backups, test email) and login/2FA attempts are rate-limited, with Retry-After on 429s.

Fleet security

This is the part a normal web app doesn't have — protecting the servers themselves:

  • SSH host-key pinning. The server's key is pinned at enrollment; any later mismatch is refused and logged — a changed key could mean someone's intercepting the connection.
  • Hardening that stays in place. No root login, no passwords, firewall default-deny, fail2ban — all kept in place, so a security improvement rolls out fleet-wide at once, and any drift is visible and auto-restored.
  • Signed agent releases. Agent binaries are checksummed and signed; a node verifies both before updating itself and refuses an unverified one.
  • Rotating, revocable per-node tokens, encrypted at rest. Panel containers run non-root, with no Docker socket, and Postgres is never public.

Hosted-site isolation

Each instance is the blast radius: one compromised site can't reach its neighbors. Instances default to KVM VMs (hardware isolation); a site can reach the internet but never the panel or other instances' private services. Per-instance resource limits also contain abuse — one hammered site can't starve the node. Inside an instance, each site gets its own Linux owner with a narrow sudo grant, and sites ship hardened defaults (PHP disable_functions + open_basedir, Node units with NoNewPrivileges + ProtectSystem=strict, unique WordPress salts, login rate-limit jails).

Edge & DDoS protection

Several layers, outermost first: an optional Cloudflare proxy per domain for large floods (the honest answer — a single VPS can't absorb multi-Gbps on its own), then the node's reverse proxy (per-IP rate limits, slowloris timeouts, body caps), kernel-level flood protection, and fail2ban jails for scanners and WordPress login/xmlrpc brute force.

TLS, backups & updates

  • TLS everywhere — certificates auto-issue and auto-renew, HSTS on by default, expiry alerts well before any deadline.
  • Encrypted backups — every backup is encrypted before it leaves the panel, so offsite storage holds only ciphertext and the key never reaches a node. Backups are proven by a test-restore drill, not just by existing.
  • Patching — automatic security updates on hosts and instances; "update available" badges for catalog services; emergency hardening rolls out fleet-wide at once.

Audit & accountability

Every destructive or privileged action — enrolling or removing a node, destroying an instance or site, revealing or rotating a key, opening a break-glass terminal, restoring a backup, changing a role or setting — writes an audit row you can't edit or delete. Security events (auth failures, rate-limit hits, crypto errors) are logged and can feed alerts.

How it's tested

These controls are checked continuously, not assumed: pnpm test:security verifies encryption and tamper detection, the access checks, rate-limit triggers, path-traversal attempts, and the security headers against a running server. The test rig's hostile profiles add real attack cases — enrolling against a changed host key (must fail), a revoked agent token (must be refused and alerted), and drift injection (edit the firewall on the node → drift badge → reconcile restores it).

On this page