Security¶
CreeperCLI assumes the TCP port may be reachable by strangers: LAN exposure, a misconfigured firewall, a panel that opens the port. Every control below is enforced server-side and cannot be bypassed by a custom client.
Before you deploy¶
- Run the server as a non-root OS user. CreeperCLI is a full admin channel: file access, console commands, restart. Compromise of it is compromise of the host.
- Decide how the port is reached. The default bind is
0.0.0.0:45678, chosen so hosted panels work out of the box. On a machine you control, setnetwork.host: "127.0.0.1"inconfig.yml, restart, and reach the server through an SSH tunnel. Do not open the port to the public internet. - Enable 2FA with
creepercli totp setupfor every admin account. - Watch the audit log (
plugins/CreeperCLI/creepercli-audit.log) and the Banned IPs counter in/creepercli status.
Threat model¶
| Threat | Mitigation |
|---|---|
| Password sniffing on the wire | Passwords never travel after login; sessions use 128-bit tokens. Transport is not encrypted yet: tunnel with SSH, or wait for TLS in a future release. |
| Brute force / credential stuffing | bcrypt cost 12 (about 0.3 s per verify), per-IP login limiter, fail2ban |
| Token theft / reuse | 15-minute inactivity timeout, token bound to the login IP, logout invalidates, passwd invalidates other sessions |
| Reading files outside the server | Sandbox jail: .., symlink, and absolute-path escapes blocked (E_PATH_ESCAPE) |
| Running arbitrary console commands | Default-deny allowlist only |
| API flooding | Per-session token bucket (30/s), connection cap, payload cap |
| Conflicting edits | Per-file locks with 5-minute TTL |
| No accountability | Append-only audit log with timestamp, IP, user, action, params |
Authentication¶
- Users live in
creepercli-users.ymlas bcrypt hashes (cost 12). - Login: password verify, then a TOTP code if the account has 2FA, then a session token.
- TOTP is RFC 6238, 6-digit, 30-second period.
totp setupsecrets expire after 10 minutes;totp disableneeds a password and a valid code. - A nonexistent user and a wrong password both return
E_AUTH_FAILED. The server never reveals which failed.
Sessions¶
- 128-bit tokens, held in memory only (
SessionManager). - Bound to the login IP. Presenting a token from a different IP ends the session.
- 15-minute inactivity timeout; a sweeper runs every minute.
auth.resumerevalidates the token on every CLI run, so scripts skip the password.logoutrevokes the token server-side;passwdrevokes every other session of that user.- The CLI stores the token in
~/.creepercli/creds, mode 0600, keyed per host and port.
Rate limiting and fail2ban¶
- Login limiter: per-IP sliding window, default 3 failures / 5 minutes →
E_RATE_LIMITED. - fail2ban: more failures within its window (default 3 / 10 minutes) bans the IP for 10 minutes →
E_BANNEDwith the remaining time. A successful login clears the failure history. - Command rate limit: token bucket per session, default 30/s →
E_RATE_LIMITEDon bursts.
Network¶
- Default bind
0.0.0.0:45678. On a machine you control, setnetwork.hostto127.0.0.1and tunnel. Changing host or port requires a restart. max-connections(16): extra sockets getE_SERVER_FULLand close.max-payload-bytes(10 MiB): oversized frames are rejected before processing.- Every response carries
v: 1; unknown frames are ignored.
File sandbox¶
PathSanitizer resolves every path against the jail root (sandbox.server-root, default the server directory):
..cannot escape the jail →E_PATH_ESCAPE.- Absolute paths are jail-relative:
/etc/passwdreads<root>/etc/passwd. - Symlinks are resolved to their real target and checked against the jail. A link to the host's
/etc/passwdis rejected. - NUL bytes, drive letters, and backslashes are normalized or rejected.
Console execution¶
Executed commands pass the allowlist first, then dispatch on the main thread as a proxied ConsoleCommandSender. Output is captured and returned to the client. Pattern matching:
list matches "list" only
whitelist * matches "whitelist add bob", "whitelist list"
say * matches "say hello everyone"
restart matches "restart" only
Anything else gets E_ALLOWLIST_DENIED. Do not add a bare *.
Transfers¶
- SHA-256 verified on both ends of push and pull; a mismatch returns
E_CHECKSUM_MISMATCHand removes the partial file. - Chunked streaming, capped at
limits.max-transfer-bytes(1 GiB default). - A dropped connection aborts the transfer and cleans up server-side.
Editing¶
edittakes a per-file lock; concurrent editors getE_LOCKED. Locks release on disconnect, unlock, or the 5-minute TTL.- Pushes carry a SHA-256 of the new content; the server rejects mismatches.
Audit log¶
Every authenticated action appends to creepercli-audit.log:
Format: timestamp | source-ip | username | action | params. Rotates at audit.max-mb (10 MiB). Failed logins are logged with the attempted username, which makes brute-force patterns visible even before fail2ban triggers.
Roadmap (deferred)¶
Scoped beyond v1.0.0, deliberately not in the release:
- TLS transport with certificate pinning
- Idle-connection timeout and per-IP connection caps
- Session token rotation on resume
- Persistent fail2ban bans
- Password policy options and forced 2FA
- Per-user roles (
admin/member)