Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

docs/v1/scanner-signature-bundles.md

Scanner signatures across the airgap

Holger's malware scanner is ClamAV, run as clamd in its own sandbox (no network, read-only root). On a box that never touches the internet, its signature database crosses the airgap the same way an intel bundle does: as a signed holger.update-bundle/v1, here with product = holger.scanner-sigs/v1 (lane HOL-NCSC; code in intel/src/sigs.rs, installed by server/lib/src/import_door.rs).

 ONLINE                                    │ AIRGAP │                    SECURE
 freshclam: main/daily/bytecode .cvd/.cld ─┐        │
 a hotfix: hotfix-<id>.ndb ────────────────┼─ holger scanner-sigs build ─ USB ─ holger bundle import
 YARA: rules.yar ──────────────────────────┘    (signed, two approvers)        verify · floor · audit · install · clamd RELOAD

What a bundle may carry

Only signature files: payload/clamav/<name> with a ClamAV database extension (.cvd, .cld, .cud, .ndb, .hdb, .hsb, .ldb, .mdb, .fp, .sfp, .ign2, … the full list is holger_intel::sigs::CLAMAV_EXTENSIONS) and payload/yara/<name>.yar|.yara. A bare file name only: no path, no leading dot. The builder refuses anything else, and so does the import (holger-sigs-1, the whole bundle refused, nothing written).

The first set (online side, then the secure side)

On the online side, with freshclam's output in ./clamav and the site's release PKI:

holger scanner-sigs build --clamav-dir ./clamav --sequence 1 \
  --policy trust-policy.ron --release-cert release.der --release-key release.pk8 \
  --approve security-reviewer=reviewer.der,reviewer.pk8 --approve system-owner=owner.der,owner.pk8 \
  --out sigs-1.holger.znippy

The trust policy names holger.scanner-sigs/v1 in allowed_products. On the secure side, the first bundle sets the anti-rollback floor, so an admin grants it once:

holger bundle allow-first-import --config holger.ron --from-bundle sigs-1.holger.znippy
holger bundle import --config holger.ron --bundle sigs-1.holger.znippy
holger scanner-sigs status --config holger.ron

The same import runs through every door: holger-cli bundle import, POST /-/bundle/import, gRPC AdminService.ImportBundle, and the <data_root>/bundle.inbox drop directory. The import verifies the release signature and the approvals, holds the sequence to the floor, writes its audit record first (scanner-sigs-import: bundle_id=… seq=… files=…), then moves the floor, then installs the files into <data_root>/sandbox/signatures/clamav/ and asks clamd to reload. From then on clamd reads that directory (mounted read-only over its DatabaseDirectory /var/lib/clamav) instead of the database its pinned image carried; a clamd started before the first bundle takes it at its next start.

A hotfix

A hotfix is one urgent signature, shipped before the next full refresh. It is the same kind of bundle with the next sequence and one file; it lands over the installed set and leaves every other file as it was.

  1. Online: write the signature, for example hotfix-2026-10-31.ndb (Holger.Hotfix.Example:0:*:<hex>), check it with clamscan -d hotfix-2026-10-31.ndb on a sample, and put it alone in a directory.
  2. Online: holger scanner-sigs build --clamav-dir ./hotfix --sequence <installed + 1> …, approved and release-signed exactly like a full set (two people, the release key).
  3. Carry it across. Secure side: holger bundle import --config holger.ron --bundle sigs-<n>.holger.znippy (no new grant: the floor stands). holger scanner-sigs status lists the hotfix file with its sequence.

A file is replaced by a later bundle that carries the same name (the next full refresh carries daily.cld again); an older bundle is a rollback and is refused, so a hotfix is never undone by replaying the set before it. Every refusal names its code, and the import door writes it into the audit log.

What is not here yet