Documentation
The CyberPanel plugin
The plugin is a user interface, not a second scanner. The broker and its workers keep running when the panel is down, being upgraded or broken.
Before you install
The Shelltrap broker must be running, /run/shelltrap/api.sock must exist, and CyberPanel must
be installed. The plugin archive is built reproducibly and contains a single top-level directory
shelltrap/, without caches or Python bytecode.
cd /usr/local/CyberCP
/usr/local/CyberCP/bin/python /usr/local/CyberCP/pluginInstaller/pluginInstaller.py \
install --pluginName shelltrap
Why the install is so careful
The CyberPanel installer patches core files — CyberCP/settings.py, CyberCP/urls.py and the
sidebar template — by inserting lines, and its hook calls use subprocess.call and ignore
exit codes. A failed preflight therefore cannot stop the installation from claiming success.
So the plugin defends itself:
pre_installwrites a root-owned transaction marker at/var/lib/shelltrap/plugin-install-transaction.jsonand takes three atomic snapshots of the core files. The marker is published first in statepreparingwith the planned snapshot paths, sizes, SHA-256 checksums and original metadata; the complete set then moves topendingand, after all checks, toready. Symlinks, hard links, wrong owners, partial snapshots or mismatched checksums are refused.post_installevaluates that marker regardless of what the native installer printed. A completepreparing,failedorpendingset is validated and rolled back. An incomplete set leads todegradedwithout a partial core rollback, and the marker is kept for inspection.- Only on
readydoespost_installrun the shippedrepair.sh, which checks the three core files, synchronises only the Shelltrap static subtree, runsmanage.py checkand restartslscpd. - Finally the health response is checked over the Unix socket and the result written to
/var/lib/shelltrap/plugin-status.json. An unreachable or degraded broker is never marked healthy, and a success message from the historical installer does not substitute for that file.
Upgrade and repair after a panel upgrade
The historical installer unpacks without -o and can turn interactive when files exist. For a
controlled upgrade, unpack explicitly and run the hooks yourself:
unzip -oq /path/to/shelltrap.zip -d /usr/local/CyberCP
/usr/local/CyberCP/shelltrap/pre_install
/usr/local/CyberCP/shelltrap/post_install
A CyberPanel upgrade can rewrite the same core files, so the repair script is idempotent and can be run on its own:
/usr/local/CyberCP/shelltrap/repair.sh
It restores exactly one shelltrap entry in INSTALLED_APPS, exactly one
path('shelltrap/', include('shelltrap.urls')) entry and exactly one sidebar link, recognising
both the old url(...)/<li> structure and the current path(...)/<a class="menu-item">
form. Symlinks, hard links and unexpected structures are refused. On failure it rolls back the
three files and its own static subtree and restarts lscpd with the previous state. It never
deletes lines it did not write.
Pages
Dashboard and health · findings with filters, paging, detail and signals · quarantine with restore, and purge for administrators only · policies per domain with visible global → account → domain inheritance · ignore lists · feed generations · audit for administrators only · jobs · help and status.
Every signal display carries author, source and licence attribution where the rule provides them. That is a licence obligation for some of the third-party rule sets, and it is also simply useful.
Roles
| Role | Read | Restore | Changes |
|---|---|---|---|
| Administrator | all sites, findings, jobs, policies, feeds, audit, quarantine | all sites | policies, ignores, feed actions, finding resolutions, quarantine purge |
| Reseller | own sites and their objects | own scope | released policy keys of their own domains |
| Customer | own sites and their objects | own scope | released policy keys of their own domains |
upload.on_error and heuristics.action stay locked for resellers and customers at all times,
and the broker enforces that boundary independently of the UI. Bulk editing across several
domains is offered only to administrators; for anyone else the endpoint refuses a multi-selection
fail-safe and accepts at most one broker-authorised domain.
An administrator can narrow this further through two Django settings that are intersected with the fixed list of supported keys:
SHELLTRAP_POLICY_ALLOWED_KEYS = {
"reseller": ["scan.window", "scan.exclude", "notify.targets"],
"user": ["scan.window", "scan.exclude"],
}
SHELLTRAP_POLICY_LOCKED_KEYS = {
"default": [],
"user": ["scan.exclude"],
}
If a role is missing from an explicit allowlist, or a setting is invalid, the plugin refuses the write fail-safe rather than guessing.
Security boundaries
The API is reachable only as HTTP/1.1 over /run/shelltrap/api.sock. The plugin client sets
X-Shelltrap-Actor from the authenticated CyberPanel session, and the broker resolves the
effective role and site scope itself. There is no TCP connection and the plugin transmits no
root paths. Every browser mutation is a POST form protected by CyberPanel’s Django CSRF
mechanism; the PUT and DELETE calls the API needs are made only by the server-side client.
The plugin runs no parser, no scanner and no quarantine operation as root. It displays IDs and metadata supplied by the broker. A UI outage stops no running scan and no scheduled job.
Known API limits
Two rough edges are documented rather than hidden:
- The global policy layer is only partly reliable through the HTTP endpoint, because
/v1/policies/{scope}/{scope_id}formally requires a non-emptyscope_idwhile the internal global scope uses an empty one. The effective site policy, including its origin, works and is the authoritative view for operations. - The ignore API supports create and delete but has no atomic update. A change therefore creates the fully validated new rule first, checks the returned opaque ID strictly, and only then deletes the old one. If that delete fails, the plugin tries to compensate by removing the new rule and reports the failure visibly — including a failure of the compensation itself.
Removal
/usr/local/CyberCP/bin/python /usr/local/CyberCP/pluginInstaller/pluginInstaller.py \
remove --pluginName shelltrap
pre_remove idempotently removes the app entry, the URL entry, the modern or historical sidebar
entry and the shipped static subtree, then deletes plugin-status.json. It stops no broker
service and deletes no findings, quarantine, configuration, logs or broker database. The plugin
holds no Django models and no database state of its own.
Troubleshooting
test -S /run/shelltrap/api.sockconfirms the socket really is a Unix socket; then checkstatand the contents ofplugin-status.json.- On
degraded, check the broker service first and the CyberPanel service second. A health error is an operating fault, not a clean scan. - After a panel upgrade, run
repair.shagain. Messages about symlinks, hard links or an unexpected structure need a human; the script deletes nothing it does not own. - If a repair fails, the previous version of the three core files is intact. Run
manage.py check, inspect the file it names, then repeat the repair.
If the native installer reports success despite a red hook exit, do not delete the marker by hand. Inspect it, then re-run the intended rollback path:
stat -c '%a %U:%G %n' /var/lib/shelltrap/plugin-install-transaction.json
cat /var/lib/shelltrap/plugin-install-transaction.json
/usr/local/CyberCP/shelltrap/post_install
Next
Abridged from the Shelltrap plugin manual, revision 2026-09-04.