Honeypot Initialize - sparkycz1/honeypot-shelf GitHub Wiki
A blank device walks in a plain OS install and walks out a convincing liar.
Initialize (top nav, visible to anyone with write access —
company-scoped READ_WRITE or superadmin) provisions a brand new
device into a working OpenCanary honeypot over SSH, in one run: base +
admin-tool packages, a Python venv with OpenCanary/scapy/pcapy-ng, the
opencanary.service systemd unit, locale (English + Czech, matching this
app's own two) and timezone (Europe/Prague), a full apt upgrade, the
team's vim/bash.bashrc config, hostname//etc/hosts, at most one of
NetBird or WireGuard
(see Architecture's "VPN connectivity" section for how
this relates to Honeypot Shelf's own, independent VPN choice in Settings
→ VPN), generating OpenCanary's config (opencanaryd --copyconfig), and
host-side prep for the two modules that need it beyond enabled: true
(see "Modules prepared, not enabled" below — confirmed against
OpenCanary's own wiki).
Adapted from the team's own Ansible playbook — see app.ssh.initialize's
module docstring for why this runs as one shell script instead of a real
ansible-playbook invocation.
Six supported OS releases, auto-detected — nothing to pick on the
form. The two newest releases of each of Raspberry Pi OS, Debian, and
Ubuntu (LTS, Desktop or Server — indistinguishable over plain SSH) — see
app.ssh.platform_detect.SUPPORTED_RELEASES for the exact list. Right
after connecting, a quick read-only probe (/etc/os-release plus
command -v raspi-config) decides which; an unsupported OS is refused
before any change is made, with a clear error naming what's supported.
Only one thing actually differs by platform — where OpenCanary's own log
lives (a tmpfs ramdisk on a device with raspi-config, to spare an SD
card the write wear the read-only-root toggle exists for — see
Honeypot Management's own Config-tab section —
or a plain persistent path otherwise, since Debian/Ubuntu typically
aren't running off an SD card at all). Every other step is identical
across all six.
Run history: /initialize/history keeps the last 50 runs (device,
outcome, who ran it, full output) — the run page's own live output is
otherwise gone the moment its tab closes.
You see it happen, live: the run page opens a WebSocket
(app.web.routes.initialize_ws) on load — a banner tracks the current
phase ("Installing packages", "Upgrading the system", ...) while the
script's output streams in line by line, the same feel as the interactive
SSH terminal. A run can take up to an hour on a slow Pi (apt full-upgrade and compiling pcapy-ng are the long parts) — the socket
stays open the whole time.
A separate, earlier step from onboarding a Honeypot already in
Honeypot Shelf (Architecture.md's onboarding
paragraph, the "Run initial setup" button on a honeypot's Settings tab):
Initialize targets a device with no row in Honeypot Shelf yet — no pinned
host key, nothing to onboard. Once it succeeds, add the device the normal
way (Installation//honeypots/new), which discovers
and pins its host key the usual, non-TOFU way, then (optionally) run its
own "Run initial setup" to hand SSH management over to Honeypot Shelf's
shared honeypotshelf identity.
| Field | Notes |
|---|---|
| IP address | The device's current IP — no DNS lookup, no discovery. |
| Device name | Set as hostname and /etc/hosts entry. Must be a valid hostname (letters/digits/hyphens). |
| User |
root, or any other account already reachable over SSH. Anything other than root runs the whole script via sudo. |
| SSH port | Defaults to 22 — used for this run only, to reach the device as it is right now. |
| MAC address | Optional. Typed in, or drawn for a manufacturer picked from the list (see Honeypot Management's "MAC address" section). The device uses it from the reboot that ends the run; with DHCP it then usually comes back on a different IP address, and the "waiting for the device" step says so instead of reporting a failure. Empty keeps the hardware address. |
| New SSH port | Defaults to 22222. As the last step, on success, sshd moves to this port (see "SSH moves to a new port on success" below) — use it, not the port above, when adding the device afterward. Editable per run — e.g. set it equal to "SSH port" to leave a device's port unchanged on a re-run. |
| Authentication | Honeypot Shelf's shared identity key (assumed already authorized — e.g. preseeded via RPi Imager; see Settings for the public key) or a one-time password. Neither the password nor any VPN field below is ever stored — used for this one run only. |
| VPN | None (default), NetBird, or WireGuard — the device's own connection, independent of Honeypot Shelf's own VPN choice in Settings → VPN (see Architecture). Picking one reveals its own fields; picking neither installs neither package. |
| NetBird setup key | Optional (VPN = NetBird). NetBird installs either way; a setup key also joins the device to your network right away (netbird up --setup-key ...). Get one from your NetBird management console. |
| NetBird management URL | Optional (VPN = NetBird). Blank = NetBird Cloud; set only for a self-hosted management server. |
| WireGuard config | Required (VPN = WireGuard). The device's own peer config — the same .conf your WireGuard server (or its UI) hands any client. Written to /etc/wireguard/wg0.conf, brought up with wg-quick up wg0 and systemctl enable wg-quick@wg0 (survives a reboot). |
Every other SSH connection this app makes uses strict pinned host-key
verification — no blind trust on first use (see app.ssh.client's
module docstring). Initialize is a narrow exception: the device isn't in
Honeypot Shelf yet, so there's no prior fingerprint to compare against.
The fingerprint is shown back after the run to note down and verify
independently if wanted — nothing about it is stored. The normal "Add
honeypot" flow that follows uses the real discover-then-confirm flow, not
this one.
Second-to-last (right before the port change below), Initialize installs Honeypot Shelf's own shared identity public key, plus every current superadmin's personal SSH public key(s) (My account → SSH public keys — see Honeypot Management), onto the account it connected as — so both Honeypot Shelf and every superadmin can reach the freshly provisioned device directly afterward, without the one-time password/key this run used (useful especially when "Password" was the authentication method, since that password is never stored once the run ends). Skipped harmlessly if there's nothing to install; safe to re-run — every key is added idempotently, never removing or overwriting one already there, by hand or otherwise.
Alongside the SSH keys above, Initialize grants the connecting account
the exact scoped, passwordless sudo app.ssh.readiness's "missing
requirements" banner otherwise asks an operator to fix by hand: apt-get
(checking/running updates), apt-mark (holding a package back, since 0.70.0), shutdown (reboot/power actions), dmidecode
(the RAM speed fact), systemctl (the Honeypot Config tab's module
editor) — plus flatpak/snap if present. Skipped for a root
connection. Same grant app.ssh.onboarding gives its own dedicated
honeypotshelf user — a freshly Initialized device no longer shows up
already failing every readiness check.
The last step reboots the device — a full clean boot, not trusting everything the script just did is already in its final running state. Initialize polls the device's new SSH port (same trust-on-first-use host-key probe, never a real login) until it answers again, for up to a few minutes, before reporting success — so "Initialize succeeded" means the device actually came back up. A different host key across the reboot is reported as a problem, never silently accepted (see Architecture for the full timing). If it never comes back within the wait window (a slow SD card, a first-boot fsck), the run is reported failed with that explanation — check the device by hand.
The last step moves sshd off port 22 to the "New SSH port" field
(22222 by default, app.ssh.initialize.NEW_SSH_PORT), via a drop-in
file (/etc/ssh/sshd_config.d/honeypotshelf-ssh-port.conf) rather than
editing the distro's own sshd_config — idempotent, leaves the
maintained file untouched. It's last for a reason: every earlier step has
already fully succeeded over the original connection by the time this
runs — restarting sshd doesn't drop that already-open session, only new
connections see the new port. sshd -t validates the merged config
first; the whole script is set -e, so a config problem aborts before
sshd restarts — this can never lock an operator out mid-run.
Use the new port, not 22, when adding the device as a honeypot
afterward (/honeypots/new's "Port" field) — the form and run page both
call this out. A re-run against a device already moved to the new port
still works: it connects on whichever port you give it and re-applies the
drop-in file, a no-op.
Connecting as root needs no sudo. Otherwise:
- With a password: supplied to
sudo -S. - With the shared key:
sudo -n, which only works if the account already has passwordless sudo — Raspberry Pi OS's own default for its initial user, not Debian's or Ubuntu's. Otherwise use password auth, or grant NOPASSWD sudo by hand first.
Before generating the config, Initialize decides this per the detected
platform (see above): on a device with raspi-config, a 512 MB tmpfs
at /mnt/tmpfs (an /etc/fstab entry + mounting it, idempotent) — what
the Honeypot Config tab's read-only-root toggle
assumes exists, so OpenCanary still has somewhere to write once / is
read-only, sparing the SD card that same write. On Debian/Ubuntu instead
— no read-only-root toggle offered at all, see
Honeypot Management — a plain persistent path,
/var/log/opencanary/opencanary.log, which survives a reboot (a feature
there, not something to work around). Either way, Initialize repoints
OpenCanary's file logger at whichever path it set up, and the honeypot's
own opencanary_log_path column (kept in sync by every facts refresh,
not just at Initialize — see app.ssh.facts) is what the Logs tab's
"Honeypot" source and the Activity tab's poll actually read. A
correction from a facts refresh resets the stored byte offset too (a
different file's old offset means nothing) and immediately triggers a
canary-log poll — so a honeypot added right after Initialize doesn't sit
with an empty Activity tab until the next scheduled poll happens to
notice; click "Refresh facts" on the Overview tab if you don't want to
wait for the periodic sweep either.
opencanaryd --copyconfig generates /etc/opencanaryd/opencanary.conf
(skipped if it already exists — a re-run never clobbers a hand-edited
config). Every module ships disabled by default; Initialize never
flips "<module>.enabled" to true. What it does do, for the two
modules that need real host-OS setup beyond that (every other module is a
self-contained listener, nothing further to prepare):
-
portscan — Debian 12+ (and Ubuntu, same systemd/journald and
nftables-backed-
iptablesdefaults) dropped file-based kernel logging for journald-only; neither works with the module as shipped. Fixed by loading rsyslog'simjournalmodule (bridges journald back to/var/log/kern.log) and switching theiptablesalternative toiptables-legacy— see OpenCanary's wiki. -
smb — Samba is installed and configured with a
full_auditVFS module (/etc/samba/smb.conf) logging file access to syslog facilitylocal7, which rsyslog routes to/var/log/samba-audit.logOpenCanary tails — see OpenCanary's wiki.smbd/nmbdare left disabled — prepared, not live; nothing listens until an operator deliberately enables both services and thesmbmodule.
Both modules' config keys (portscan.iptables_path, smb.auditfile) are
already pointed at the right paths — enabling either module afterward
needs no Terminal-tab edit: once the device is a Honeypot, its Config
tab has a full module editor (every module, not just these two — see
Architecture) that ticks "<module>.enabled",
restarts opencanary, and (for Samba) starts smbd/nmbd.
-
Doesn't create a
Honeypotrow. Add the device separately afterward — once its host key is pinned, events start arriving with no further setup (see Honeypot Management). - Doesn't enable any OpenCanary module — see "Modules prepared, not enabled" above and the Config tab's module editor.