Repository navigation
Audit Log
Who did what, to which honeypot, and whether it actually worked — superadmin-only, and it never forgets on purpose.
Every route or background job that mutates something — or that refuses
to because a safeguard tripped (a typed confirmation that didn't match,
a missing pinned host key, a bad self-registration token, a failed or
locked-out login) — writes one AuditLogEntry: actor, action, outcome,
source IP, and a plain-language summary. A background job (the
scheduler, a retention purge) logs under a fixed actor label instead of
a user, since it has no request to read one from.
In the reader's language. The summary is always recorded in English
— exports, syslog, the Wazuh rules and the REST API stay English, so a
machine-consumed record never varies by who triggered it. On the Audit
log page an English reader sees that summary; any other language shows
the translated label of the action code instead (audit.action_label.<code>
in each locale file, falling back to the parent code —
honeypot.power.reboot → honeypot.power — and then to the English
summary), with the full English summary as the cell's tooltip. The
details column is shown as key: value · key: value. A test fails when a
new audit action has no label in every shipped language
(tests/test_audit_labels_and_nav.py).
The Audit log page itself live-refreshes over WebSocket the moment a new entry is written — no need to reload to see something that just happened. See Honeypot Management's "Live updates over WebSocket" for how that mechanism works across the app.
Every entry is hash-chained: entry_hash covers this entry's own
fields plus the previous entry's entry_hash, so altering or deleting
an entry breaks the chain from that point on. Concurrent writers (the
web app and every Celery worker child can all log at once) serialize
through a single locked row for the duration of one entry's write, so
two concurrent entries can never both link to the same previous hash.
Settings → Security → "Verify chain" walks the whole table and reports the first broken link, if any — a proactive integrity check, not something that only fires when someone goes looking.
Logging commits independently of whatever the caller is doing, after its own commit — a failed audit write can never roll back the action it describes, and a rejected/blocked action still gets its own record even though nothing else was worth committing.
Kept audit_log_retention_days (Settings → Checks & retention, default
90) before the daily purge job deletes old entries — None means keep
forever. Exportable as CSV or JSON (/audit/export) for archival or
compliance, with the same spreadsheet-formula-injection guard the REST
API's own event export uses (a leading =/+/-/@ gets a defusing
prefix, since a raw one opens as a live formula in Excel/Sheets).
A separate, optional target (Settings → Integrations,
app.audit_syslog) mirrors every entry to a syslog server over UDP/TCP/
TCP-over-TLS as it's written — best-effort, a delivery failure is
logged and swallowed, never blocking the write it's mirroring. This is
the global, audit-only target — never a honeypot alert; those have
their own three targets entirely, see
Honeypot Management.
Wazuh users: wazuh/honeypotshelf_decoders.xml +
wazuh/honeypotshelf_rules.xml (repo root) ship a ready-made decoder +
rule set (id range 107000-107099) for both this feed and the
honeypot-alert one — covers every audit action this app emits and
every OpenCanary module, plus brute-force correlation rules for
repeated failed logins. Both files are plain XML with no comments (some
Wazuh manager versions' own upload/file-editor path chokes on comments
and non-ASCII characters), so the install steps live here instead:
- Copy
honeypotshelf_decoders.xmlto/var/ossec/etc/decoders/andhoneypotshelf_rules.xmlto/var/ossec/etc/rules/— or point a<decoder_dir>/<rule_dir>pair inossec.conf's<ruleset>block at wherever you keep thiswazuhfolder instead:<ruleset> <decoder_dir>etc/decoders</decoder_dir> <rule_dir>etc/rules</rule_dir> ... <decoder_dir>/path/to/wazuh</decoder_dir> <rule_dir>/path/to/wazuh</rule_dir> </ruleset>
- Point Honeypot Shelf's syslog target(s) at this manager (or an intermediate syslog-ng/rsyslog relay forwarding to it) — Settings → Integrations for the global audit target, each Company's own page and/or "All honeypots" → Integrations for alerts.
-
/var/ossec/bin/wazuh-control restart(or just restartwazuh-manager) to load both files. - Verify with
/var/ossec/bin/wazuh-logtestagainst a captured line before relying on it in production, as always.
The rules file matches on the <decoded_as> names the decoders produce
(honeypotshelf-audit / honeypotshelf-honeypot-alert — each decoder
needs <use_own_name>true</use_own_name>, or Wazuh reports the parent
decoder's name instead and <decoded_as> never matches) and on the
fields both decoders expose from each feed's JSON body via
JSON_Decoder — every top-level key in the payload, referenced by its
own name with no data. prefix (JSON_Decoder doesn't add one):
event, id,
timestamp, action, actor, ip, outcome, target_type,
target_id, target_label, summary, details for the audit feed;
event, id, timestamp, companies, honeypot, honeypot_ip,
type — OpenCanary's own numeric logtype id, as a string — label,
src_ip, src_port, dst_port, source, raw for the honeypot-alert
feed).
Watch out for Wazuh's 13 reserved "static" field names (srcip,
dstip, srcport, dstport, protocol, user, status, id,
url, data, extra_data, action, hostname) — the audit feed's
own action key collides with one. A rule can't match it via
<field name="action">...</field> (Wazuh rejects the whole rule file
at upload with a bare "XML syntax error"/code 1113, no line number);
it needs the dedicated <action type="pcre2">...</action> element
instead — same attributes, different tag. honeypotshelf_rules.xml
already does this.
Severity levels: every honeypot-alert rule (107001, 107045-
107069) sits at level 8 or higher — a honeypot has no legitimate
traffic, so any interaction with one is inherently an attacker doing
something, never routine noise. Credential submissions/login attempts
step up to 9, and the brute-force correlation rules (107007,
107050) stay at 10. The audit-feed rules (107000-107043) stay
mostly informational (level 3-6) and only escalate to 8+ for
genuinely security-relevant events (failed/locked-out logins, CSRF
rejection, rate limiting, destructive actions) — ordinary CRUD by a
logged-in admin is expected, routine activity, not something worth an
analyst's attention.