KLYRN
KLYRN Web
All documentation
  1. Home
  2. Docs
  3. Security

Security

The process that faces the internet cannot become root, open the database, execute a program or use a shell. The process that can do those things is not on the network.

Firewall and SSH

Security, then Network and SSH reads the server itself: ufw status numbered, ss -ltnp, and sshd -T for sshd's own resolved configuration. KLYRN's own table answers exactly one question: did we create this rule.

The page pairs what is allowed with what is listening. A database listening on all interfaces with its port open is shown as an exposure.

What KLYRN will not do:

  • Remove a rule it did not create. Rules added by the installer, by you, or by another tool are shown, marked, and left alone. The page gives you the ufw command to run yourself.
  • Close the port sshd is listening on, unless another SSH port is verified both open and answering. The refusal says what would have happened: every session ended, every new one refused, including yours.
  • Remove the last authorised key while password sign-in is off.
  • Flush or reset anything. There is no such button.

Ports and source addresses are validated and rejected, never escaped. A port of 0, a port above 65535, or an address containing a newline or a semicolon is refused before anything runs. ufw is only ever reached with typed arguments.

Public keys are shown by fingerprint, type and comment. The key material itself is never sent to the panel, and a private key never touches KLYRN at all.

SFTP, the SSH shell and the terminal

Every hosting account can have SFTP, locked inside its own files, with keys managed in the panel. The administrator turns SFTP on once per server.

The SSH shell runs in a jail that holds the account's own home and the system's programs, read-only. It sees only its own processes and allows no forwarding. It stays off until the administrator hands it out in Settings, Features.

The terminal in the browser is the same shell in the same jail. It is closed the moment the shell is turned off, and it too stays off until the administrator hands it out.

Keys and passwords for SFTP are kept where only root can write them. A key placed in a home folder is ignored.

The boundary

  1. Browser HTTPS :7443 on the panel's own listener, TLS 1.2+, strict CSP, no inline scripts
  2. klyrn-web user klyrn

    Sign-in, sessions, the REST API, live events and the panel's static files. Nothing else.

    • become root
    • open the database
    • execute a program
    • use a shell
    • make outbound HTTP
    • write outside its state directory
  3. Unix socket, mode 0660 root:klyrn, peer credentials checked by the kernel, the actor is re-authorised against core's own records
  4. klyrn-core root, never on the network

    A closed registry of typed operations: create site, create database, issue certificate, and so on. Each has its own request type, validator, authoriser and audit entry. No operation takes a command string or an unconstrained path.

  5. nginx, php-fpm, mariadb, systemd, ufw, filesystem one Unix user per hosting account, one PHP-FPM master per account in its own cgroup, one grant per database, sandboxed unit per app
The only path from the internet to root on a KLYRN server. A compromised web tier can do no more than a signed-in administrator, and every one of those actions is in the audit log with actor, address, operation, target and outcome.

klyrn-web runs as uid 999 and handles sign-in, sessions, the REST API, live events and the panel's static files. Nothing else. It reaches klyrn-core over a Unix socket at mode 0660 root:klyrn, where the kernel checks the peer's credentials and core re-authorises the actor against its own records.

Core exposes a closed registry of typed operations: create site, create database, issue certificate. Each has its own request type, validator, authoriser and audit entry. No operation takes a command string or an unconstrained path. Programs get argument lists and never a shell. A CI check forbids process execution anywhere else in the codebase.

Accounts are hostile to each other

  • One Unix user and group per hosting account, with a home at 0710 that no other account can list.
  • One PHP-FPM pool per site, running as that user, confined by open_basedir, with exec disabled unless an administrator turns it on for that site.
  • One database user per database, granted on that database only, from localhost. Root stays on the Unix socket.
  • One sandboxed systemd unit per Node application, on a private port with a memory ceiling.
  • Kernel symlink and hardlink protections on.
  • Paths inside an account resolved with openat2 and RESOLVE_BENEATH, so escaping the tree fails in the kernel.

Verified on the test server: a second site's PHP pool cannot read the first site's files, or /etc/hostname.

Signing in

No default credentials
The setup token is printed once on the console, and the setup endpoint stops answering the moment the first administrator exists.
Passwords
argon2id.
Sessions
Server-side, HttpOnly, Secure, SameSite=Strict, rotated on sign-in, seven days. Visible and revocable from the account page.
Session cookie
Host-only by name (__Host-), so no other name under the same domain can plant one.
Second factor
TOTP with recovery codes. An administrator can clear a lost factor for another user, and that is audited.
Rate limits
On setup and on sign-in, per address and again per account, so one attacker cannot lock every account by guessing.
Cross-site requests
A custom request header, an origin check and Sec-Fetch-Site, on every method that changes something.

Supply chain

  • Releases are ed25519-signed, with the public key built into the binary. A failed update rolls back automatically.
  • Node.js runtimes are checked against nodejs.org's published SHA-256 list.
  • WP-CLI is pinned by SHA-512, and a mismatch refuses to install it.
  • The PHP PPA is added with a pinned key fingerprint. KLYRN never runs a blind add-apt-repository.
  • The bootstrap script verifies the manifest signature before running anything.

What the panel sends

The panel is on its own TLS listener on port 7443, minimum TLS 1.2, never sharing a listener with a customer site. Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin and Cross-Origin-Opener-Policy: same-origin. It also carries a Permissions-Policy that turns off camera, microphone and geolocation, and a content security policy with frame-ancestors 'none' and object-src 'none'.

API responses are Cache-Control: no-store, and an unknown JSON field in a request body is an error.

What is not covered

The full model, including the parts that are not finished, is on the security page. In short:

  • Isolation is by Unix users and systemd sandboxing. It does not use containers. A kernel vulnerability is a kernel vulnerability.
  • Disk limits are measured. They are not filesystem quotas. See Resource limits.
  • Per-account CPU and memory limits reach PHP and Node.js. They do not reach cron jobs. See Resource limits for what is covered.
  • The audit log has no retention policy and grows without bound.
Discord WhatsApp