shelltrap.com
en de

Guides

Signed signature feeds explained

A signature feed is a supply chain into your server. Ed25519 signing, per-rule provenance, activation gates, rollback, and why feed age belongs in health.

Illustration — Signed signature feeds explained

A signature feed is not data. It is a stream of matching logic that runs against every customer file on your server, and it arrives from outside your machine on a schedule. That makes it a supply chain, with the three failure modes every supply chain has: someone tampers with it, it silently stops, or a legitimate update turns out to be wrong.

Here is how each one is handled, and what to ask a vendor who has not thought about it.

Failure mode 1: tampering

Every generation is signed with Ed25519, and the public keys are pinned. The package ships Panomity’s key under /usr/share/shelltrap/keys/; configuration may add keys but never replace the shipped one, so an operator — or anyone who gets write access to a config file — cannot quietly swap the trust anchor.

The manifest is canonical JSON. It names every file with a 64-character SHA-256 digest, a size, and full provenance: source_uri, an SPDX license, an author, the upstream_commit it came from, and whether it was modified. The signature covers that list, sorted by path.

Fetching is deliberately narrow: HTTPS with normal certificate validation, same origin for the base URL, for redirects and for every artefact, with hard limits — 30 seconds per HTTP request, 16 MiB per response, 64 MiB per file, 512 MiB per generation, 4096 files. Each file is checked against its size and digest as it lands; the complete generation is verified again from the temporary directory before it becomes visible in staging; and after the activation gate has run, the canonical manifest, the signature, the expiry and every digest are verified once more immediately before the symlink swap. The expensive check happens in the middle, so the last thing before activation is a fresh verification.

Failure mode 2: the feed that quietly stops

This is the failure nobody instruments, and it is not hypothetical. The free signature feed most self-built stacks depend on is served from cdn.rfxn.com; when we measured it on 4 September 2026, rfxn.yara returned:

Last-Modified: Sun, 24 May 2026 14:33:02 GMT

and maldet.sigs.ver read 2026052490478 — the same date, 90,478 signatures. The infrastructure still serves HTTP 200 and no end-of-life notice is published anywhere, so this is a maintenance gap rather than a shutdown, and a single push would change it. Re-measure before you rely on it:

curl -sI https://cdn.rfxn.com/downloads/rfxn.yara | grep -i last-modified

The lesson is not “that project is bad” — it is that nothing told anyone. A scanner whose feed stopped updating months ago looks exactly like a scanner that is working. That is why every Shelltrap generation carries an expires_at which must still be in the future at each verification, why feed state is a field in shelltrap health, and why a missing active generation puts the daemon into degraded rather than into silence.

shelltrap feeds list
shelltrap health

It also matters because engine and feed are separate things. ClamAV’s own documentation states that it “is not a traditional anti-virus or endpoint security suite”; PHP webshell coverage comes from whichever third-party feed you loaded into it. Auditing the feed is auditing the coverage. We go through that in ClamAV alone is not enough for webshells and maldet versus Shelltrap .

Failure mode 3: the update that is wrong

Three mechanisms, in increasing order of drama.

The activation gate. No generation is activated because it verified. It is compiled by a staging worker and run against two corpora: a known-good set and a known-bad set. A single malicious verdict on the good corpus is a hard failure — the generation does not activate. The detection ratio against the bad corpus is written as a metric and warns below 90% rather than silently passing. For vendor generations the rule and hash counts must match the signed manifest exactly. A public corpus such as bartblaze/PHP-backdoors , which separates obfuscated from deobfuscated samples, is a fair way to keep that honest.

The gate also enforces licences and engine versions. A build that cannot prove its licence position is rejected fail-closed, as is a generation whose manifest requires a newer YARA or ClamAV than the workers actually run.

Rollback. Generations are retained — current, anything explicitly kept, any vendor base an overlay depends on, plus the newest keep_generations (3 by default) — so going back is a command, not an archaeology project:

shelltrap feeds list
shelltrap feeds rollback 20260903T120000Z-0001

A rollback re-verifies everything, refuses expired generations, and can never target a generation that appears on the signed revocation list. That denylist is an append-only tombstone on disk: once a generation is revoked it stays revoked, even if a later index no longer mentions it.

The circuit breaker. If a new generation starts quarantining at scale, it is rolled back automatically. The thresholds are deliberately multi-tenant: the last hour is compared with the 24-hour average, and by default the breaker needs at least 50 quarantines, more than ten times the average, and at least three distinct accounts, and at least 20% of active accounts. One busy customer can never trigger a server-wide rollback. Heuristic, missing or contradictory signals do not trigger one either; the evidence has to be unambiguous signature or hash hits from the new generation.

Provenance, because rules have authors and licences

Every YARA rule that can appear in a finding must carry author, license and one of reference, ref or source in its metadata, and those values must match the provenance CSV row for the file exactly. Missing or contradictory attribution is not skipped quietly — it is rejected and listed deterministically as rejected at build time.

The licence filter only admits DRL-1.1, BSD-3-Clause, Apache-2.0 and MIT (plus GPL-2.0-only for ClamAV databases). That is stricter than “whatever the community publishes”, on purpose: mixed-licence rule aggregates are common, and a non-commercial rule inside a commercial product is a real exposure rather than a theoretical one.

The payoff is visible to the customer: a finding shows which engine fired, which rule, and — where they exist — the rule’s author, source and licence. A verdict you can attribute is a verdict a customer can argue with.

Key rotation without a flag day

A generation may announce the next public key as an object carrying both the key and a mandatory expires_at. The rotation edge is only committed after the generation has been fully verified and published, is stored in a canonical, root-owned, 0600 state file, and is serialised with a lock so two processes cannot race it. Expired edges are not used as trust anchors, and edges older than a year raise a warning. The operator keeps at least the anchor pinned, so trust always traces back to something you installed.

What this means for CyberPanel operators

  1. Ask every scanner vendor how a signature update is authenticated on the way in, and what stops a bad update. “It downloads over HTTPS” is an answer about transport, not about trust.
  2. Instrument feed age. If your monitoring cannot tell you that a feed stopped moving months ago, you do not have detection coverage, you have a detection process.
  3. Check the licence position of the rules you run commercially — including in free stacks you assembled yourself.
  4. Keep at least a few generations retained so a rollback is a command rather than a rebuild, and test that rollback once before you need it.
  5. Feed configuration, key pinning and the rollback commands are documented in the installation docs ; the campaign background that these rules are written against is in the WordPress webshell wave, 2024–2026 .

Shelltrap ships signed, provenance-tracked, gated signature generations with automatic rollback — and tells you when the feed is old. See what it does .

Frequently asked

Why does a detection rule need a licence field?

Because rules are somebody’s work, and community rule collections are frequently mixed-licence artefacts. A rule whose licence forbids commercial redistribution has no business inside a paid product, and the only way to know is to carry the licence with the rule and check it at build time.

What happens if a bad generation quarantines half the fleet?

A circuit breaker compares the last hour against the 24-hour average and rolls the generation back automatically — but only when the spike crosses multiple accounts. A single busy account can never trigger a server-wide rollback, and ambiguous or heuristic-only signals never trigger one at all.

How do I know my feed is still being updated?

Feed state is part of health output, and every generation carries an expiry that must still be in the future when it is verified. That turns a feed that quietly stopped moving into a visible degraded state instead of a silent gap.

Sources

Every number, date and vendor claim in this article links to one of these.

  1. Linux Malware Detect signature feed, rfxn.yara (measured with curl -I) — accessed 2026-09-04
  2. Linux Malware Detect signature version file, maldet.sigs.ver — accessed 2026-09-04
  3. ClamAV documentation — accessed 2026-09-04
  4. bartblaze/PHP-backdoors — obfuscated and deobfuscated sample corpus — accessed 2026-09-04

More from the research desk

Shelltrap watches the files this article is about

Real-time detection, an upload gate in front of your PHP, explainable verdicts, and nothing leaving your server.