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
ufwcommand 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
- Browser
-
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
- Unix socket, mode 0660 root:klyrn, peer credentials checked by the kernel, the actor is re-authorised against core's own records
-
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.
- nginx, php-fpm, mariadb, systemd, ufw, filesystem
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
0710that no other account can list. - One PHP-FPM pool per site, running as that user, confined by
open_basedir, withexecdisabled 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
openat2andRESOLVE_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.