This document exists for two audiences: administrators deciding whether to install this extension, and reviewers assessing it for the Plesk Extensions Catalog. It describes what the extension is allowed to do, why it needs those privileges, and what is deliberately not defended against.
Open a private security advisory at https://github.com/CidQu/plesk-minecraft-manager/security/advisories/new, or an issue if you are confident the problem is not exploitable. Please do not post working exploits publicly before a fix is available.
Four things run, at three different privilege levels.
| Component | Runs as | Why |
|---|---|---|
Panel UI (plib/) |
psaadm, inside Plesk's own PHP |
Renders pages and proxies calls. Holds no privileged capability of its own. |
sbin/mcpd-ctl |
root, via pm_ApiCli::callSbin |
Only during install, upgrade and uninstall: lays down the daemon, writes the systemd unit, reads the daemon's root-owned token. It accepts a fixed set of subcommands and nothing else. |
mcpd daemon |
root |
Needs to create per-server system accounts, drop privileges when spawning a JVM, write cgroup limits, and bind privileged-adjacent ports. |
| Minecraft servers | mc_<id>, unprivileged, one per server |
Never root. Each account can reach only its own server directory. |
The daemon is the only long-lived privileged component, and it exists precisely so that the servers need not be privileged.
Panel PHP is request-scoped and unprivileged, and a Minecraft server needs a
long-lived owner: something to hold the child process, keep its stdin open for console
input, accumulate output between page loads, notice a crash, run scheduled tasks, and
hold a TCP port to implement wake-on-join. Doing that from PHP would mean either
running servers as psaadm (so every customer's server shares one identity and can
read the panel's files) or shelling out to a setuid helper for every operation. The
daemon is the smaller attack surface of the three.
The daemon's HTTP API binds 127.0.0.1 only unless an administrator explicitly
enables the public listener. Every route except /health requires a bearer token.
The panel's token is generated by the daemon, stored 0600 root-owned in
/etc/plesk-minecraft/config.json, and mirrored into Plesk's encrypted settings
store. Tokens issued to external clients are stored only as SHA-256 hashes, are
scope-limited, and can be pinned to a single server. Token comparison for the master
token uses hmac.compare_digest.
The panel proxy never forwards a browser-supplied path blindly. Administrator-only routes are refused before the request is made, non-administrators are additionally scoped by Plesk client id inside the daemon, and state-changing calls must arrive as a POST carrying Plesk's forgery-protection token.
The filesystem is reached only through util.safe_join, which resolves with
os.path.realpath and re-checks that the result is inside the server root. This
defeats .. traversal and, importantly, symlinks planted inside a server directory by
a plugin. Archive extraction rejects absolute and parent-relative members, and backup
creation skips symlinks rather than storing them.
Console input is written to the JVM's stdin. It is never passed through a shell, so there is no command-injection surface in the console.
The wake proxy parses only what it must: a handshake packet's length, packet id and next-state, with implausible lengths rejected before allocation. It answers status and login attempts itself while a server is down and splices bytes once it is up.
Third-party content. This is the largest residual risk and it is inherent to the product: installing a plugin or mod means downloading a jar and letting a Minecraft server execute it. The extension reduces the blast radius — content runs as the server's own unprivileged account, confined to its own directory — but a malicious plugin can still do anything that account can. Downloads happen only when a user asks for them, from the four catalogues named in the privacy statement, and checksums are verified when the catalogue supplies one.
Loader installers. Forge, NeoForge, Quilt and Spigot ship installers rather than runnable jars. These execute on the host, as the server's unprivileged account, in the server's own directory.
Stated plainly, so nobody assumes otherwise:
- A Plesk administrator is trusted. Anyone with panel administrator rights can already run arbitrary code on the host; this extension does not change that.
- A malicious plugin or mod that the operator chose to install. See above.
- Denial of service by a legitimately-granted customer, beyond the memory budget and optional cgroup limits. A customer given a server can make it slow.
- The security of the Minecraft server software itself, or of the game protocol.
- Traffic between a player and their server, which Minecraft does not encrypt in a way this extension could improve.
- Leave the public REST listener off unless you need it, and put TLS in front of it if you turn it on. The daemon speaks plain HTTP by design and expects to sit behind the panel or a reverse proxy.
- Open only the port range you actually use. RCON is bound to loopback and must never be forwarded.
- Grant
manage_minecraftdeliberately. It lets its holder run processes on your host. - Keep the memory reserve high enough that the host survives every server running at once.
The daemon uses the Python standard library only. There is no pip install at any point, no vendored third-party Python, and therefore no transitive dependency tree to audit or keep patched. The PHP side uses Plesk's own SDK and no Composer packages. The browser UI is vanilla JavaScript with no bundled libraries and loads nothing from a CDN.