Repository navigation
Releases: selfhostr/suckerpot
Release list
v0.9.0
Participant installs are now safe to run on a home machine: the installer checks that the internet reaches the sensors, offers a host firewall, and every internal network is isolated from the host.
Updating an existing install: Docker cannot change an existing network, so the first update recreates the stack once (
down, never-v; data volumes are kept). Docker Engine 28 or newer is needed for the network isolation. See Updates.
Added
./scripts/participant.sh ports-check [--wait SECONDS] [--since ISO | --since-install]tells whether the internet reaches your sensors, and the participant installer runs it at the end and offers to re-check after you open the ports on your router. It makes no outside connection: it counts sessions from public addresses that your sensors already recorded, prints no address, reads the database in a read-only transaction, and exits 0 (reached) or 3 (nothing yet).status.shpoints to it.
Changed
- The participant installer no longer prints a fixed "14 days" date. The hub decides the probation period;
./scripts/status.shshows the date from which the sensor counts. - Participant docs and installer summary: use port forwards rather than a DMZ, explain the DMZ risk, and say precisely when the route-out restriction holds (host firewall loaded) and that the firewall does not filter internet traffic to the machine's own services.
Security
- Host firewall for participant mode:
scripts/host/firewall-participant.nftloads with no placeholder other than the admin SSH port, rendered byscripts/host/render-firewall.shand installed withscripts/host/install-firewall.sh(systemd unit, survives a reboot,--uninstallto undo). In its own nft table, it drops everything new, invalid or untracked from the sensors' uplink to the host and to the internet or the LAN, and limitshubproxy's uplink to TCP 443 on public addresses plus UDP DNS to the resolvers Docker uses. The full-mode template's DNS rule is narrowed the same way. The renderer refuses a host bridge that is not Docker's but matches the rule names, to avoid a lockout. - Every internal network has an isolated gateway (
gateway_mode_ipv4: isolated), a fixed bridge name and IPv6 off, so a compromised container no longer reaches services on the host through its bridge. This needs Docker Engine 28 or newer, and the first update recreates the stack once (seedocs/updates.md). - A guardrail checks the hardening of every service of every supported stack: read-only root, all capabilities dropped, no-new-privileges, a non-root
uid:gid, memory, CPU and process limits, a healthcheck,noexec,nosuid,nodevtmpfs, no host namespaces, devices, Docker socket or unlisted bind mounts, isolated internal networks, and third-party images pinned by digest. - The TimescaleDB image is pinned by digest.
- The installers offer to install the host firewall at the end (participant mode; asks before using sudo, never blocks, prints the two commands otherwise;
participant.sh vpnreminds you to re-run it), and./scripts/participant.sh firewall [install|uninstall|status]does it later. - Updates and rollbacks recreate the stack once (
down --remove-orphans, never-v) when compose says an existing network cannot be reused (matched on its wording, not on any mention of a network); a foreign container attached to one of our networks stops the run with instructions instead of being disconnected. - CI pins the TimescaleDB test image by digest, like
docker-compose.yml. - Automatic updates: the minimum release age is now at least 48 h whatever the release's own manifest says;
AUTO_UPDATE_MIN_AGE_Hin.envcan raise it. - Automatic updates: a release that is unsigned, does not match its tag or has a signature cosign rejects is marked bad with its reason (network, Docker, Sigstore or disk problems are never held against a release) and the next newest release of the same major version is tried (3 at most per run), so one bad tag no longer blocks updates.
- The installer warns when Docker Engine is older than 28, where internal networks are not isolated from the host.
Verify: scripts/verify-release.sh v0.9.0 and just verify-images v0.9.0.
Full Changelog: v0.8.0...v0.9.0
v0.8.0
SuckerPot now lives in the Selfhostr organisation: github.com/selfhostr/suckerpot, images at ghcr.io/selfhostr/suckerpot-*, site, docs and demo at https://suckerpot.selfhostr.io/.
Updating from v0.7.1 or earlier: set
SUCKERPOT_IMAGE_PREFIX=ghcr.io/selfhostr/suckerpotandUPDATE_CHECK_REPO=selfhostr/suckerpotin.env, rungit remote set-url origin https://github.com/selfhostr/suckerpot.git, thenjust update. The signature check refuses the new release until you do: see Moving from the old repository.
Changed
- The offline mock-data demo moved to https://suckerpot.selfhostr.io/demo/. GitHub Pages and
site/are removed from the repo (the product site is maintained separately). - Documentation moved from Read the Docs to https://suckerpot.selfhostr.io/docs/ (Selfhostr docs kit, Starlight).
.readthedocs.yaml,docs/conf.py,docs/requirements.txtand the Sphinx CI job are removed;docs/stays the source. - The project moved from
github.com/Theroxat/suckerpottogithub.com/selfhostr/suckerpot. Release images are now published asghcr.io/selfhostr/suckerpot-*and signed by the release workflow of the new repository, and the documentation is athttps://suckerpot.selfhostr.io/docs/.scripts/env-migrate.shrewrites the old defaultSUCKERPOT_IMAGE_PREFIXandUPDATE_CHECK_REPOin an existing.env; the signature checks are as strict as before (exact repository,release.yml, exact tag). An install on v0.7.1 verifies releases against the old repository, so its first update needsSUCKERPOT_IMAGE_PREFIX=ghcr.io/selfhostr/suckerpotset in.envby hand; seedocs/updates.md. - The network is now called the SuckerPot network, and the blocklists the SuckerPot blocklists. Selfhostr is the small organisation that makes SuckerPot, no longer the name of the product or the network.
- New addresses: the site is
https://suckerpot.selfhostr.io/(hosted on Cloudflare Pages), the hub ishttps://suckerpot-hub.selfhostr.ioand the feeds arehttps://suckerpot-feeds.selfhostr.io/v1. The oldselfhostr.iosite,hub.selfhostr.ioandfeeds.selfhostr.ioare gone. Nodes pick up the new hub address with the next release; the hub keys are unchanged.abuse@selfhostr.iostays.
Verify: scripts/verify-release.sh v0.8.0 and just verify-images v0.8.0.
Full Changelog: v0.7.1...v0.8.0
v0.7.1
v0.7.0
v0.6.0
SuckerPot v0.3.0
Changed
just test-integrationusesdocker-compose.it.yml, an overlay that keepsredison the backend network only, so the throwaway project fits in Docker's default address pools next to the live stack.- The sidebar is grouped into ten entries. Related pages sit behind one entry with tabs: Attacks (Sessions, Attackers, Networks), Campaigns (Campaigns, Techniques), Tools (Scripts, Malware, Infrastructure), Trends (Activity, Patterns), Protection (Blocklists, Detections) and Exposure (Your exposure, CVE watch). Every old URL still works;
/attackers?view=asnsnow redirects to/networks. - Today is a visual story of the last 24 hours in plain words: a flow from country to service to how far each visit got, an hour-by-hour strip, and three to five stories of the day with their technical details one click away. Hard words have a short definition on hover.
Added
- VPN configuration upload (M23): Settings > VPN takes the WireGuard
.conffrom your VPN provider, parses it strictly (noPostUpand the like, IP endpoint only, full tunnel) after asking for the password again, and writes it to the newvpn-configvolume. Thevpnservice notices the new file and restarts on it; nothing leaves outside the tunnel during the switch, and the API gets no Docker socket. A new exit address stays blocked until the owner trusts the /24 the gate observed (tableegress_trusted_exits, migration0020); an exit inside the honeypot's own addresses or in an AS listed in the newEGRESS_DENY_ASNScan never be trusted, and the exit's AS is shown next to it./api/v1/settings/vpnendpoints, new guardrails. - Anonymous reporting (M22): an ingest worker (
ABUSE_REPORTS_ENABLED, off by default) finds abuse contacts over RDAP through the egress gate and prepares draft reports per recipient. The newreporting/package renders plain text, X-ARF v0.2,.eml, STIX 2.1 and MISP JSON and builds password-protected sample bundles. An identity guard checks every artefact before it is returned and fails closed. Learned self terms need the owner's confirmation. The package ships no wording: the owner writes it in Settings > Reporting. Nothing is ever sent by the stack. Migration0019,/api/v1/abuseendpoints, the Abuse reports page, Export for CERT, the sample bundle and the Reporting tab in Settings,just abuse-guard-checkand theABUSE_*settings. - Anonymous egress (M21): a
vpnservice (WireGuard through gluetun, profileegress) with a kill switch and an HTTP proxy is the only container with a route to the internet.ingestand the newcollectorgo through the proxy, an egress gate pauses outbound work when the tunnel or its exit address looks wrong, and the oldegressnetwork is removed. Newjust guardrailsandjust egress-check, new host firewall rules forsp-vpn,sp-egressandsp-samples. - Payload collection and static analysis (M21): the
collectorservice fetches captured download URLs through the VPN into a quarantine volume (SSRF validator, size, time and rate caps, never executed), and the network-lessanalyzerservice identifies, hashes, scans with YARA, deobfuscates shell scripts and extracts C2 addresses in a limited child process. Migration0018, rolessuckerpot_collectorandsuckerpot_analyzer, the/api/v1/samplesand/api/v1/egressendpoints, a Samples tab, a sample page and an Egress card in Settings.FETCH_PAYLOADS,PAYLOAD_MAX_BYTESandPAYLOAD_TIMEOUT_SECONDSare replaced byCOLLECTOR_ENABLEDand theCOLLECTOR_*andQUARANTINE_*settings. - Synology NAS sensor (M20): a fake unpatched Synology NAS on DSM 7.2.1 (Update 11), with the DSM login page and Web API on HTTP 5000 and HTTPS 5001. Logins always fail with real DSM error codes and are recorded. Requests that match the forged-session and SCGI shapes of CVE-2026-13639 and CVE-2026-13684 are tagged
synology-exploit, ransomware indicators of the C3RB3R campaign are taggedc3rb3r, and DSM API calls are taggedsynology-dsm. Migration0017adds the sensor to the inventory. - Probable C2 lookups ignore VirusTotal contacts in big shared networks (Google, Cloudflare, Microsoft, Akamai, Fastly, Meta, Apple, from the local ASN database) and CDN or update domains, and clean up already stored rows on start.
- Probable C2 addresses: for file hashes that VirusTotal or abuse.ch already know, ingest reads which addresses sandboxes saw the malware contact (VirusTotal relationships, ThreatFox
botnet_cc), with the same fixed hosts, quotas and 24 h cache as the other intel lookups and no contact with the addresses. New tableindicator_contacts(migration 0016),GET /api/v1/indicators/c2, and a blocklist reason "probable C2" (75 for ThreatFox, 40 for VirusTotal, capped at 45 when no sensor computed the hash and fewer than three networks dropped it). GET /api/v1/story, the plain-language summary behind Today.- A
stagefilter onGET /api/v1/sessionsand on the Attacks page (looked, guessed, got in, ran commands, tried to install, sent an exploit), plussinceanduntilin the Attacks URL.
Fixed
- The abuse, alerts and intel workers no longer wait up to an hour for their first run on a host that booted recently.
- CVE watch: the NVD window is 60 days (was 30) and is read in one-day chunks, oldest first, with a page budget, so a busy day no longer loses CVEs silently at the 10,000-result limit. A run that stops early resumes from its last complete chunk.
- CVE watch: GitHub advisories are read in seven-day chunks, oldest first. A one-time start-up rewind refills a shallow history, and watched CVEs that are missing from the table are fetched by id (at most 10 per run, once per id per 24 hours).
Docs
- A "Provider API keys" guide: what each optional key adds and how to get it, step by step.
- A Troubleshooting section in the deployment guide, and the upgrade command.
- Refreshed screenshots and a new README gallery for the three-space navigation.
Upgrade: git pull && just up (migrations 0017 to 0020 run automatically). See the deployment guide for the VPN, collector and reporting settings.
Full changelog: v0.2.0...v0.3.0
SuckerPot v0.2.0
Added
-
Seven more sensors (M9): Redis, Docker Engine API, MySQL, Microsoft SQL Server, Android ADB, SMB and SIP (UDP and TCP), each with real-client tests, per-IP fairness for CPU, memory and events, and a security review. SIP never sends a reply larger than its request.
-
Provider keys in Settings (M11): AbuseIPDB, GreyNoise and MaxMind keys stored encrypted (AES-256-GCM), write-only in the UI, with re-authentication and a "Test key" button.
-
CVE watch (M12): daily CISA KEV, NVD and GitHub advisory feeds ranked by relevance to the emulated services, with "seen in our traffic" counts and a trap-proposal sheet.
-
Indicators and threat intelligence (M13): URLs, domains, IPs and hashes extracted from events, enriched by URLhaus, ThreatFox and VirusTotal, shown on an Infrastructure page with CSV and STIX export. Passive only.
-
Users and roles (M14): admin and read-only viewer accounts, one-time temporary passwords, per-source lockout and an audit log. The
.envowner account stays the root of trust. -
Campaigns and MITRE ATT&CK (M15): sessions that share a script, payload host, hash, malware family, exploit or credential list are grouped into campaigns, with a Techniques heatmap. Off by default (
CAMPAIGNS_ENABLED,ATTACK_ENABLED). -
Defend (M16): scored blocklists, overrides, exports for OPNsense and pfSense aliases, an opt-in token feed (
FEEDS_ENABLED), Sigma and Suricata rules marked "review before deploying", and "Your exposure". -
Script library (M17): command scripts with plain-English explanations, and an optional LLM explainer (
LLM_ENABLED, off by default) that sees only normalised script text. -
Alerts and reports (M18): Discord, Microsoft Teams and e-mail (TLS) channels, alert rules that are off until switched on, a test message button, and a weekly report with a printable view. Off by default (
ALERTS_ENABLED,REPORTS_ENABLED). -
Today, search and research scanners (M19): a Today home page, a global search box, a glossary, and known research scanners labelled benign by network ownership.
-
Migrations
0011to0015add the campaign, ATT&CK, Defend, script, alert and search tables and columns. -
RDP sensor (M10): the NTLM challenge flow, so
nmap --script rdp-ntlm-inforeturns the full Windows Server 2022 identity and logins arrive asrdp-ntlmevents with the user name and domain. -
RDP decoy accounts (
RDP_ACCOUNTS): a login with a decoy password is verified against the NTLM response and taggedweak-credentials. Nothing else is ever checked. -
Optional NLA-off mode (
RDP_NLA_MODE=optional, off by default): records the cleartext user name and password from clients that skip NLA (rdp-nla-offevents), taggedrdp-cleartext. -
RDP client tool identification (nmap, FreeRDP, rdesktop, ncrack, mstsc), stored on sessions as
client_tooland shown on the session page. -
Tag
bluekeepfor RDP sessions that bind theMS_T120channel. -
RDP limit
RDP_MAX_TLS_PER_IP(default 4), so a few addresses cannot hold every TLS slot.
Changed
- Migration
0007adds the nullablesessions.client_toolcolumn. - Live page: the 3D globe is replaced by a flat world map (Equal Earth) with per-country arcs for the last 15 minutes, a stats strip, a ticker of the newest events and rankings of addresses, countries and networks.
SITE_HOME_LAT,SITE_HOME_LON,SITE_HOME_LABELandGET /api/v1/settings/siteare unchanged.
Upgrade: git pull --ff-only && docker compose up -d --build — migrations 0006 to 0015 run automatically. New features are switched on with the *_ENABLED variables in .env (see docs/configuration.md); the public blocklist feed and the AI script explainer stay off by default.
🤖 Generated with Claude Code
SuckerPot v0.1.0
Added
- Bootstrap (M0) and foundations (M1): MIT licence, project rules, roadmap, architecture and security docs, a gitleaks pre-commit hook and CI; the shared event schema, a Redis Streams pipeline, a TimescaleDB schema with Alembic migrations, an ingest consumer and an API with a live WebSocket feed.
- Demo data generator (
just demo): synthetic sessions across all four sensors with documentation IP ranges only, plus a live mode. - SSH sensor: OpenSSH 9.6 fingerprint, configurable weak logins, an emulated shell on a fake filesystem, download recording without fetching, and timed session recording for replay.
- Telnet sensor: BusyBox telnetd look-alike with Mirai-style flows.
- Web sensor: byte-identical Apache 2.4.58 with PHP 8.3, fake WordPress and phpMyAdmin pages, and PHP2Shell (upload-progress) detection.
- RDP sensor: Windows Server look-alike with NLA and username capture.
- Enrichment: GeoIP and ASN from DB-IP Lite (no key) or MaxMind GeoLite2, optional AbuseIPDB and GreyNoise Community reputation with a 24 h cache and enforced daily quotas.
- Tagging: a declarative YAML rules engine without regular expressions (
recon,mirai,crypto-miner,malware-drop,php2shell). - Dashboard: Live (3D globe and event feed), Overview (KPIs and auto-written insights), Attacks, Session detail with terminal replay, Attackers, Trends, Malware (defanged URLs) and Settings, in dark and light on desktop and mobile; a design system; a screenshot mode (
?mask=1); Playwright screenshots. - Dashboard login: a single admin account with an argon2 password hash, CSRF protection and a rate-limited login.
- Retention and storage: raw events kept 90 days, aggregates forever, compression, a disk guard, and
just backup/just restore. - Hardened Docker Compose stack with a host firewall template in
scripts/host/. - Documentation site (Sphinx, Read the Docs) and a project page.
- Repository meta: README,
CONTRIBUTING.md,SECURITY.md, issue and pull request templates, Dependabot and release-note configuration.
Security
- No attacker input is ever executed: every sensor passed several rounds of adversarial security review (the web sensor's final review is pending).
- Isolation: four networks with an internal backend, one Redis ACL user per sensor limited to
XADDon its own stream, least-privilege database roles, no outbound route for the API, non-root read-only containers with all capabilities dropped. - The first review of the M1 stack found unbounded stream growth, egress, poison-message and default-credential issues; all were fixed and the second pass passed.
Before exposing a host
Read the go-live checklist in the documentation (docs/deployment.md): apply the host firewall template, keep the dashboard port reachable only from your reverse tunnel, generate strong secrets, and run SuckerPot only on an isolated machine you are ready to lose.