shelltrap.com
en de

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_install writes a root-owned transaction marker at /var/lib/shelltrap/plugin-install-transaction.json and takes three atomic snapshots of the core files. The marker is published first in state preparing with the planned snapshot paths, sizes, SHA-256 checksums and original metadata; the complete set then moves to pending and, after all checks, to ready. Symlinks, hard links, wrong owners, partial snapshots or mismatched checksums are refused.
  • post_install evaluates that marker regardless of what the native installer printed. A complete preparing, failed or pending set is validated and rolled back. An incomplete set leads to degraded without a partial core rollback, and the marker is kept for inspection.
  • Only on ready does post_install run the shipped repair.sh, which checks the three core files, synchronises only the Shelltrap static subtree, runs manage.py check and restarts lscpd.
  • 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

RoleReadRestoreChanges
Administratorall sites, findings, jobs, policies, feeds, audit, quarantineall sitespolicies, ignores, feed actions, finding resolutions, quarantine purge
Resellerown sites and their objectsown scopereleased policy keys of their own domains
Customerown sites and their objectsown scopereleased 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-empty scope_id while 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

  1. test -S /run/shelltrap/api.sock confirms the socket really is a Unix socket; then check stat and the contents of plugin-status.json.
  2. On degraded, check the broker service first and the CyberPanel service second. A health error is an operating fault, not a clean scan.
  3. After a panel upgrade, run repair.sh again. Messages about symlinks, hard links or an unexpected structure need a human; the script deletes nothing it does not own.
  4. 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.