Attacker identity engine for SSH honeypot telemetry. Aka: it makes the bots think they hit a real prod box, then puts their entire playbook on blast.
ShardLure clusters SSH bots by playbook fingerprint (OpenSSH journal lines) or HASSH (Cowrie sessions), not just IP. Same actor on three IPs? Still one actor — one live box has a single fingerprint spanning 784 addresses. Different actor with the same address? Different rows. HASSH clustering needs a completed SSH key exchange, so the dashboard shows you exactly what share of your traffic is fingerprinted rather than implying it's all of it (~93% of Cowrie events on a real honeypot; scans that never finish a handshake fall back to IP identity). The vibe is "username taste profile, not who's-at-the-door." It ships with a VPS installer, Cowrie integration, live ingest, a forensic TUI, and a web dashboard that spins a globe at you.
attacker -> port 22 (Cowrie) -> JSON/journal ingest -> SQLite actors -> dashboard
you -> port 2222 (SSH) -> real admin access via keys/Tailscale
- Features
- Setup Guide
- Local Development
- Commands
- Configuration
- Deployment
- Persona And Bait
- IP Reputation Enrichment
- Threat Intel Sharing (MalwareBazaar)
- AbuseIPDB Reporting
- Architecture
- Security Notes
- Troubleshooting
- Uninstall
- Roadmap
- Dual ingest: OpenSSH journal exports and Cowrie JSON logs. No vendor lock-in, no SaaS dashboard reading your shame.
- Actor clustering: journal actors by source IP, Cowrie actors by HASSH. Botnets get sorted by their vibe (HASSH + username corpus), not just where their NAT slingshot lands.
- Intent classification: probe, proxy, deploy, mixed, or unknown. The "deploy" ones are the spicy ones — that's curl-bash-into-tmp energy.
- Live mode: tails Cowrie + journal straight into a globe dashboard. Real-time slay.
- VPS bootstrap: installs Cowrie, moves real SSH to a private port, writes systemd units, starts everything. One command, no yak shaving.
- Stealth persona: Ubuntu-style banner, fake
prod-app-server-01hostname, regenerated host keys so you don't get fingerprinted as "obvious honeypot #4892." - Bait files: fake
.env, AWS creds, DB creds, deploy keys, nginx site. Looks legit, is poison. - Deploy-safe sync: tar-over-SSH because
scpof Go/Python sources mysteriously turns them into UTF-16. We do not gaslight you about this — see Troubleshooting. - Incremental Cowrie ingest: tracks file offset + inode, so a 100MB cowrie.json doesn't get re-scanned every 5 seconds. Your I/O thanks us.
- Idempotent everything: re-running ingest dedupes events instead of duping them. Past you can't bully present you.
- Themes: ships Signal (default — a near-black signal field with a light/dark toggle), plus Meridian and Sprite. Switchable live from the settings panel; the selected theme and Signal's mode persist in SQLite. One WebGL globe engine (Cobe) across all themes.
- 3D Cobe globe: WebGL globe with live arcs from attacker IPs to your home point, plus floating artifacts anchored to attacker coordinates — satellites, a LIVE badge at your origin, and per-actor analytics chips. Pointer-drag rotation, scroll zoom, double-click reset. Proper listener lifecycle (no leaks on theme switch).
- Two dashboards, split by job: the globe view (
/) is an ambient wall display — summary counts (events, actors, unique IPs, countries, fingerprint coverage, shell sessions), threat-level gauge, a Cowrie honeypot status panel, events/hour sparkline, live feed, and the globe with its artifacts. The intel console (/intel) is where you dig: attack geography, brute-force radar, top credentials, MITRE ATT&CK coverage, sessions, payloads, tunnel/proxy targets, IOC export. All fed by real-time API polling. - Command palette: the search box in the intel console is a real lookup, not a row filter — type any IP, actor, session id, command, or payload hash and get grouped, actionable results (jump to an actor, enrich an IP, open a session transcript or payload) with arrow-key navigation. It searches the API, so it finds records that aren't on the current tab.
- Fingerprint coverage, stated honestly: the globe's FINGERPRINTED tile is the share of Cowrie telemetry carrying a HASSH — event-weighted, because counting actor rows inverts the number (clustering collapses 784 IPs into one row, so successful clustering made coverage look worse). journald events are excluded from the denominator: sshd logs cannot carry a fingerprint at all.
- Honeypot status panel: Cowrie's real uptime read from systemd (
ActiveEnterTimestamp), plus seconds-since-last-event and total ingested. Strictly read-only — ShardLure never starts, stops, or reconfigures the Cowrie unit; the two systemd units stay independent with the JSON log as their only contract. On a host without systemd it reportsunknownrather than inventing a number, and the last-event signal still works (a Cowrie that is "up" but not logging is the real failure mode). - Dashboard settings panel: configure API keys, theme, and enrichment providers from the UI. Keys are masked on read and validated with a per-provider connection test before save. Home coordinates remain in YAML under
dashboard.home_*and are not edited by Settings. - One-click MalwareBazaar upload: share captured payloads to abuse.ch directly from the payload inspector modal. No CLI required.
- AbuseIPDB reporting: report confirmed brute-forcers back to AbuseIPDB from the dashboard (Report All) or CLI (
report abuseipdb). Vetting gate enforces probe-score floor, private/admin IP exclusion, and 24h re-report dedup. - Seven-provider IP enrichment: look up any attacker IP against AbuseIPDB, VirusTotal, GreyNoise, Shodan, AlienVault OTX, IPQualityScore, and IPinfo in parallel — normalized verdict + score + tags, cached 24h. Two work with no API key.
- Runtime keystore: API keys and appearance settings live in SQLite, editable from the settings panel. DB wins over env vars; env vars win over config file. No restart needed.
- Tunnel tracking: captures and aggregates
direct-tcpipforwarding attempts (attacker proxy pivots) — powers the "Proxy Targets" widget and tunnel IOC export. - Persistent geo cache: IP geolocation results are stored in SQLite and survive restarts. No more "resolving..." on every page load.
A complete walkthrough for standing up ShardLure on a fresh Ubuntu/Debian VPS (it also supports dnf/yum and pacman hosts). Budget ~10 minutes, most of it Cowrie's pip install.
Keep your current SSH session open until you've verified the new admin port works. The installer moves real SSH off port 22 so Cowrie can squat there.
You don't need to set up keys yourself first — the installer walks you through it. It is paranoid on your behalf:
- If no SSH key is found on the server, it pauses and lets you paste your public key in, installs it into the right account with correct permissions, and only then moves SSH. (No key, no paste → it aborts without touching sshd, so you can't lock yourself out.)
- After moving SSH it pauses at a verify gate: open a second terminal,
confirm
ssh -p <admin-port>works, and only then typeyesto continue. - The original sshd config is backed up and auto-rolled-back if the new one
fails
sshd -t.
Print your laptop's public key when prompted (make one first if needed):
ssh-keygen -t ed25519 # only if you don't already have a key
cat ~/.ssh/id_ed25519.pub # paste this when the installer asksgit clone https://github.com/hett-patell/shardlure.git
cd shardlureTransferring sources yourself? Use the tar-pipe deploy (
make deploy/bash scripts/push-sources.sh arm), notscp— somescp/editor pipelines silently re-encode Go/Python to UTF-16 and the build dies with "null bytes". See Troubleshooting.
sudo python3 scripts/shardlure.py runIt walks you through everything, in order:
- Intro + confirm — shows what it's about to change and waits for your OK.
- Install dependencies (git, python venv toolchain, authbind, Go) via your distro's package manager.
- Prompt for three ports (validated; defaults shown below).
- Detect your admin IPs — Tailscale IP + your current SSH client IP are
auto-added to
admin_ips(so you never classify yourself as an attacker); you can add more. - Check / install your SSH key — if the server has no key yet, it pauses and lets you paste your public key in, installing it with correct perms.
- Move real SSH to the admin port via a drop-in at
/etc/ssh/sshd_config.d/99-shardlure-admin.conf(key-only, root password login disabled). The original config is backed up to/etc/ssh/sshd_config.shardlure-bak. - Verify gate — pauses for you to confirm
ssh -p <admin-port>works in a second terminal before going further (typeyesto continue,abortto stop). - Create the
cowriesystem user, clone + build Cowrie into/var/lib/shardlure/cowrie, apply the stealth persona, regenerate host keys, and plant bait files. - Build the
shardlurebinary to/usr/local/bin/shardlure. - Write
/var/lib/shardlure/shardlure.yaml. - Open firewall ports (only if
ufwis already active). - Install + start two systemd services:
cowrie.serviceandshardlure-live.service.
| Setting | Default | Purpose |
|---|---|---|
| Honeypot port | 22 |
Cowrie listener for attackers |
| Admin SSH port | 2222 |
Real SSH, key-only |
| Dashboard port | 8080 |
Live dashboard |
From a second terminal, confirm the new admin port works:
ssh -p 2222 user@your-vps # use whatever admin port you choseOnly close your original session once that succeeds. (If something went wrong, the original sshd was auto-restored — but verify anyway.)
sudo python3 scripts/shardlure.py status
systemctl status cowrie shardlure-live
journalctl -u shardlure-live -f # watch live ingestOpen the dashboard at http://<tailscale-ip>:8080. Keep 8080 off the public
internet — port 22 is the bait, your dashboard is not bait, do not get those
confused. Tailscale or an SSH tunnel (ssh -L 8080:127.0.0.1:8080 ...) is the
right way to reach it.
For defense in depth, set a token the dashboard requires on every request:
# Add to the shardlure-live systemd unit's [Service] section, then daemon-reload + restart:
Environment=SHARDLURE_DASH_TOKEN=your-long-random-tokenThen reach the dashboard with ?token=… (page load) or an Authorization: Bearer …
header (API calls — the token is never accepted in an /api URL, to keep it out
of access logs).
Fail-closed bind: without a token, ShardLure refuses to start if the dashboard would bind a public address — loopback, private, and Tailscale (
100.64.0.0/10) binds are allowed (with a warning). Either keep it private or setSHARDLURE_DASH_TOKEN.
Add provider API keys as Environment= lines in the shardlure-live unit (see
IP Reputation Enrichment and
Threat Intel Sharing). Two enrichment
providers (Shodan, GreyNoise) work with no key at all.
If setup was interrupted after Cowrie/SSH changes:
sudo python3 scripts/shardlure.py finishsudo python3 scripts/shardlure.py plant-baitVerify from the honeypot shell:
ls -la /opt/app/
cat /opt/app/.envgit clone https://github.com/hett-patell/shardlure.git
cd shardlure
go mod tidy
make build
./shardlure ingest journal testdata/sample.journal --replace
./shardlure ingest cowrie /var/lib/shardlure/cowrie/var/log/cowrie/cowrie.json --replace
./shardlure actors
./shardlure actor show 188.84.0.25
./shardlure dashboard
./shardlure web :8080 --tailscale
./shardlure live :8080 --cowrie=/path/cowrie.json --tailscaleThe binary can also launch the VPS wrapper:
sudo ./shardlure run| Command | Description |
|---|---|
ingest journal <file> [--replace] |
Parse journal auth lines and build actors |
ingest cowrie <file> [--replace] |
Parse Cowrie JSON logs and build actors |
actors [--limit=N] |
List actors by last seen. CONF is an evidence tier (LOW/MEDIUM/HIGH/CONFIRMED), not a percentage — it is a coarse label chosen by source and signals, so showing it as 55% would imply a calibrated probability it does not have. probe (0-100) is the computed score. |
actor show <id|ip> |
Show one actor profile |
dashboard, dash, tui |
Open the forensic TUI |
web [:8080] [--tailscale] |
Serve the web dashboard |
live [:8080] [--cowrie=PATH] [--interval=5s] [--no-journal] [--tailscale] |
Run live ingest and dashboard |
run |
Start the VPS wrapper |
status |
Print event and actor counts |
ioc |
Export a small IOC slice |
share bazaar [--dry-run] [--limit N] [--sha SHA] [--since DURATION] [--anonymous] [--status] |
Upload captured payloads to MalwareBazaar (abuse.ch) |
report abuseipdb [--dry-run] [--limit N] [--min-probe N] [--rewindow N] [--status] |
Report confirmed brute-forcers to AbuseIPDB |
version |
Print version |
| Command | Description |
|---|---|
run, setup |
Full VPS bootstrap |
finish |
Resume after partial setup |
plant-bait, bait |
Inject bait files into Cowrie's virtual filesystem |
start, stop, status |
Manage services |
uninstall [--purge] |
Reverse the install: restore SSH, remove services + binary. --purge also deletes the data dir and cowrie user. See Uninstall. |
GitHub Actions runs on push and pull request:
go mod verifygo vet ./...go test -coverprofile=coverage.out ./...go build -o shardlure ./cmd/shardlure
The installer writes /var/lib/shardlure/shardlure.yaml. You can also copy shardlure.yaml.example:
data_dir: /var/lib/shardlure
admin_ips:
- 100.x.x.x # Tailscale IP or trusted admin workstation
ssh:
admin_port: 2222
honeypot_port: 22
dashboard:
port: 8080
# Change these to your VPS/operator location for the globe origin.
home_lat: 19.0760
home_lon: 72.8777
home_city: Mumbai
home_country: India
home_cc: IN
journal:
unit: ssh
cowrie:
home: /var/lib/shardlure/cowrie
json_log: /var/lib/shardlure/cowrie/var/log/cowrie/cowrie.json
unit: cowrie # systemd unit, read-only, for the honeypot uptime readout
capture:
enabled: true
evidence_dir: /var/lib/shardlure/evidence
quarantine_fetch: true
max_bytes: 52428800
timeout_sec: 45
intel:
bazaar:
api_key: ""
tags: ["shardlure", "honeypot"]
max_bytes: 33554432
freshness_days: 10
abuseipdb:
report_enabled: false
categories: [18, 22]
min_probe_score: 60
rewindow_hours: 24Use -config /path/shardlure.yaml or SHARDLURE_CONFIG to override the path. API keys can also be set from the dashboard settings panel (stored in SQLite, takes precedence over env/config).
Do not commit your real config. admin_ips may reveal private network details such as Tailscale IPs.
Use tar pipe deployment instead of direct scp for source files:
make deploy
# or
bash scripts/push-sources.sh armOn the VPS:
cd ~/ShardLure/shardlure
bash scripts/fix-go-sources.shRun the exact sudo install ... command printed by scripts/fix-go-sources.sh;
it includes the randomized build path created for that run. Then finish setup:
sudo python3 scripts/shardlure.py finishsudo journalctl -u ssh -S "30 days ago" -o short-iso --no-pager \
| grep -E 'Invalid user|Failed password|Failed publickey|Accepted ' \
> ~/journal-ssh-30d.log
shardlure ingest journal ~/journal-ssh-30d.log --replaceinstall/persona/ includes a simple production-server disguise and decoy files.
| Virtual path | Bait |
|---|---|
/opt/app/.env |
Fake DB, Redis, JWT, Stripe, and AWS values |
/opt/app/config/database.yml |
Fake Postgres credentials |
/home/ubuntu/.aws/credentials |
Fake AWS deploy profile |
/home/deploy/.ssh/id_rsa |
Fake deploy key |
/var/backups/nightly/db_credentials.txt |
Fake backup credentials |
All credentials are intentionally fake. Regenerate bait values per deployment so multiple honeypots are not fingerprinted by identical decoys.
The intel console's enrichment panel looks up any attacker IP against multiple threat-intel providers in parallel, normalizes each into a verdict (malicious / suspicious / benign / unknown) + score + tags, and caches results for 24h in SQLite so you don't burn free-tier rate limits. Every provider is opt-in via an environment variable; missing keys render as "not configured" rather than failing the panel. Two providers are keyless and work out of the box.
| Provider | Env var | Key? | Signal |
|---|---|---|---|
| AbuseIPDB | SHARDLURE_ABUSEIPDB_KEY |
yes | Abuse-confidence score + report history |
| VirusTotal | SHARDLURE_VT_KEY |
yes | Multi-engine detections |
| GreyNoise (community) | SHARDLURE_GREYNOISE_KEY |
no (optional) | Internet-noise classification |
| Shodan InternetDB | — | no | Open ports, CPEs, known CVEs, host tags |
| AlienVault OTX | SHARDLURE_OTX_KEY |
yes | Community pulse reputation + campaign tags |
| IPQualityScore | SHARDLURE_IPQS_KEY |
yes | Fraud score + proxy / VPN / TOR / bot flags |
| IPinfo | SHARDLURE_IPINFO_KEY |
yes | ASN / org / geo + privacy (hosting/proxy/vpn/tor) flags |
Set whichever keys you have (in your shell or the systemd unit) before running
web or live. Results are fail-open: a provider that errors or lacks a key
never blocks the others.
shardlure share bazaar submits captured payloads to abuse.ch MalwareBazaar. Each upload is automatically classified (ELF arch, static-vs-dynamic linkage, scripting language, and a short list of well-known family fingerprints — RedTail, Mirai, Komari, Traffmonetizer, XMRig, c3pool) and tagged. abuse.ch's server-side analysis (YARA, ClamAV, telfhash) then bolts on the heavy-duty signatures.
Before anything is sent, every candidate passes a submission-policy gate (internal/intel/bazaar/vet.go) that enforces MalwareBazaar's rules locally so a benign, junk, or stale file never touches the API (repeat violations get accounts banned). It hard-rejects: SSH keys and other benign content, files under 64 bytes, TTY transcripts, and anything older than 10 days (measured from first observation, not import time). The 10-day ceiling cannot be loosened by configuration or CLI flags; freshness_days may only tighten it. Executable structure (ELF/PE, architecture, and linkage) is classification metadata, not proof of malware. The gate accepts as confirmed malware on either a recognised family/behaviour tag or provenance (a non-benign script/binary the attacker fetched or uploaded in-session via curl|sh/wget/scp — malicious even with no known family, so novel droppers still get through). This same gate runs for both the CLI and the dashboard button; the dashboard also throttles submissions server-side (~2s apart) so a scripted caller can't spam the API.
Setup
-
Sign up at https://auth.abuse.ch/ and copy your Auth-Key.
-
Edit
shardlure.yaml:intel: bazaar: api_key: "your-auth-key-here" tags: ["shardlure", "honeypot"] max_bytes: 33554432 # 32 MiB freshness_days: 10 # 0 = default 10; valid range is 0..10
-
Dry-run first to inspect what would ship:
shardlure share bazaar --dry-run --limit 10
-
When the output looks right, drop
--dry-run:shardlure share bazaar --limit 10
Re-running is safe: every sha256 we successfully submit (including file_already_known responses) is recorded in bazaar_uploads and skipped on the next run.
You can also share payloads from the web dashboard: open the payload inspector modal on any captured artifact and click Share to MalwareBazaar. Set SHARDLURE_BAZAAR_KEY in your environment or systemd unit for this to work. The Red Team tab's MalwareBazaar panel shows upload history, family classification, and pending counts.
Flags
| Flag | Default | Meaning |
|---|---|---|
--dry-run |
false | print classification + destination without POSTing |
--limit N |
10 | cap per-run uploads (0 = unbounded) |
--sha SHA |
– | select only this sample; dedup and Vet still apply |
--since DUR |
freshness_days (10d by default) |
local candidate-selection window only |
--anonymous |
false | submit without attribution to your account |
--status |
– | list past uploads from bazaar_uploads instead of uploading |
Neither --since nor --sha bypasses Vet; they only choose which local artifacts enter the sharing pipeline.
Why MalwareBazaar? It's the de-facto sharing hub for honeypot-captured Linux malware. Their submission policy (confirmed malware only, no PUPs/adware, no file infectors, samples must not be older than 10 days) is enforced by the vetting gate described above before any upload, and again server-side by abuse.ch. Repeated violations get accounts banned — see internal/intel/bazaar/vet.go for the policy gate and internal/intel/bazaar/client.go for the fatal-status handling that halts the run on user_blacklisted. --since defaults from intel.bazaar.freshness_days.
shardlure report abuseipdb reports confirmed brute-force actors back to the AbuseIPDB community feed. This is the give-back half of enrichment — you consume the threat-intel feed via /check, and contribute back via /report.
Every candidate passes a vetting gate (internal/intel/abuseipdb/vet.go) before anything is sent:
- Actor must have a ProbeScore above the configured floor (default 60).
- Private, reserved, and admin IPs are never reported.
- A 24h re-report window prevents spamming the API (AbuseIPDB permits 15min, but 24h keeps us well within fair use).
- The generated comment carries session stats (event count, username count, MITRE TTPs) but NOTHING identifying the honeypot host.
The same gate runs for both the CLI and the dashboard's "Report All" button. The dashboard also prevents concurrent batch reports — a second click returns "batch already in progress" instead of deadlocking.
Setup
-
Ensure
SHARDLURE_ABUSEIPDB_KEYis set (same key as enrichment). -
Opt in via
shardlure.yaml:intel: abuseipdb: report_enabled: true categories: [18, 22] # 18=Brute-Force, 22=SSH min_probe_score: 60 rewindow_hours: 24
-
Dry-run first:
shardlure report abuseipdb --dry-run --limit 10
-
When the output looks right, drop
--dry-run:shardlure report abuseipdb --limit 25
Flags
| Flag | Default | Meaning |
|---|---|---|
--dry-run |
false | print what would be reported without contacting AbuseIPDB |
--limit N |
25 | cap per-run reports (0 = unbounded) |
--min-probe N |
60 | minimum actor ProbeScore to report (0-100) |
--rewindow N |
24 | hours before a reported IP may be reported again |
--status |
– | list past reports from abuseipdb_reports instead of reporting |
+-------------+ +--------------+ +-------------+
| Cowrie :22 | --> | cowrie.json | --> | |
+-------------+ +--------------+ | SQLite |
| actors | --> web :8080
+-------------+ +--------------+ | events | TUI
| journal ssh | --> | tail/seed | --> | |
+-------------+ +--------------+ +-------------+
- Journal actors:
journal:<src_ip>, clustered by username playbook (their attempted-username distribution is their personality). - Cowrie actors:
cowrie:<hassh>, clustered by HASSH fingerprint (TLS-but-for-SSH client identity hash). - Admin IPs: explicitly excluded from clustering so you don't accidentally classify yourself as a "fast_dictionary_spray" actor.
- Loopback default: The dashboard binds to
127.0.0.1:8080by default. Wildcard or public addresses require an explicitSHARDLURE_DASH_TOKEN— the server refuses to start with an unauthenticated listener on a non-loopback address. - Cookie auth: The
?token=query parameter is bootstrap-only — it sets anHttpOnly,SameSite=Strictsession cookie and redirects to a clean URL. Tokens never persist in browser history, referrer headers, or server access logs. - Security headers: Every response carries
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: no-referrer, and a restrictiveContent-Security-Policy(self-hosted assets only, frame-ancestors none). - Token rotation: Rotating the dashboard token via Settings automatically updates the session cookie so the operator's session survives.
- Verify
ssh -p 2222 user@hostin a second terminal before closing your original session. "I'll fix it in the morning" SSH stories never end well. - Keep the dashboard on Tailscale or another private network. Exposing the dashboard to the internet is what we call self-doxxing.
- Set
SHARDLURE_DASH_TOKENfor dashboard defense in depth (constant-time compared, sent asAuthorization: Bearer …orX-ShardLure-Token). - External geolocation is opt-in (
SHARDLURE_GEO_HTTP=1). UseSHARDLURE_IPAPI_KEYfor HTTPS lookups via ip-api Pro (recommended). Plaintexthttp://ip-api.comonly if you also setSHARDLURE_GEO_INSECURE_HTTP=1(leaks attacker IPs to the network path). - Cowrie SSH host keys are regenerated during install so you don't share a fingerprint with every other lazy honeypot on Shodan.
- Keep bait credentials fake. Yes really. Do not get clever and put "almost real" creds in there.
- The SQLite database is chmod'd to
0600automatically — it can contain attacker-supplied passwords, which sometimes overlap with their actually reused real ones. Treat the file like a loaded gun.
Symptoms (your editor/transfer pipeline silently re-encoded your source files):
SyntaxError: null bytescannot execute binary fileunexpected NUL in input
This is not a you-problem, it's a tooling-problem. Fix:
bash scripts/push-sources.sh armAvoid direct scp for Go/Python sources in this project. We are not in our scp era.
Ensure cowrie.cfg points at the real Cowrie data path:
data_path = /var/lib/shardlure/cowrie/src/cowrie/data
state_path = /var/lib/shardlure/cowrie/var/lib/cowrie
[shell]
filesystem = /var/lib/shardlure/cowrie/src/cowrie/data/fs.pickleThis is harmless on rerun. do_load indicates the file content was loaded.
fsctl does not implement ls. Verify bait through the honeypot shell or inspect honeyfs:
ls /var/lib/shardlure/cowrie/honeyfs/opt/app/
Tearing ShardLure down means undoing everything the installer did: restoring
real SSH to port 22, removing the two systemd services and the binary, and
(optionally) deleting Cowrie, the captured-intel database, and the cowrie
user. There are two ways to do it — the built-in command (recommended) and a
fully manual walkthrough if you'd rather see every step.
The same golden rule as install: SSH gets reconfigured. Keep a working session open and verify connectivity on the restored port before you log out. The uninstaller restores SSH first, before touching anything else, precisely so a half-finished teardown can't strand you.
# Stops + removes services and the binary, restores the original sshd_config,
# removes the authbind/ufw rules — but KEEPS your data (DB, evidence, config):
sudo python3 scripts/shardlure.py uninstall
# Same, but ALSO delete /var/lib/shardlure (Cowrie clone, captured payloads,
# the SQLite intel DB) and remove the 'cowrie' system user:
sudo python3 scripts/shardlure.py uninstall --purgeWhat it does, in order (SSH first, on purpose):
- Restore SSH — copy
/etc/ssh/sshd_config.shardlure-bakback over/etc/ssh/sshd_config, delete the99-shardlure-admin.confdrop-in, runsshd -t, and only then reload. If the validated config fails the test it aborts the reload rather than risk your running sshd. - Remove services — stop, disable, and delete
cowrie.serviceandshardlure-live.service;daemon-reload. - Remove the binary —
/usr/local/bin/shardlure. - Remove the authbind byport file (only created when the honeypot port is < 1024).
- Firewall — delete the honeypot and dashboard
ufw allowrules. The admin SSH rule is left in place on purpose so you can't lock yourself out. - Data — kept by default; deleted only with
--purge(along with thecowrieuser).
Custom ports? The firewall/authbind cleanup needs to know which ports you used. If you didn't install on the defaults (22/2222/8080), pass them via environment variables so the right rules are removed (SSH restore + service + binary removal are port-independent and always correct regardless):
sudo SHARDLURE_HONEYPOT_PORT=22 SHARDLURE_ADMIN_PORT=2222 SHARDLURE_DASH_PORT=8080 \ python3 scripts/shardlure.py uninstall --purge
After it finishes, verify SSH from a second terminal on the restored port (by default 22 once Cowrie is gone) before closing your session.
Do this if the script isn't available or you want to inspect each step. Run as root.
# 1. Restore real SSH FIRST (so you can't get locked out).
sudo cp /etc/ssh/sshd_config.shardlure-bak /etc/ssh/sshd_config # if the backup exists
sudo rm -f /etc/ssh/sshd_config.d/99-shardlure-admin.conf
sudo sshd -t && sudo systemctl reload ssh # only reload if -t passes
# No backup? Re-enable the original Port line by hand in /etc/ssh/sshd_config,
# then `sudo sshd -t && sudo systemctl reload ssh`. VERIFY in a 2nd terminal.
# 2. Stop, disable, and remove the systemd services.
sudo systemctl stop shardlure-live.service cowrie.service
sudo systemctl disable shardlure-live.service cowrie.service
sudo rm -f /etc/systemd/system/shardlure-live.service /etc/systemd/system/cowrie.service
sudo systemctl daemon-reload
sudo systemctl reset-failed
# 3. Remove the binary.
sudo rm -f /usr/local/bin/shardlure
# 4. Remove the authbind byport file (only if your honeypot port was < 1024).
sudo rm -f /etc/authbind/byport/22 # use your honeypot port
# 5. Revert firewall rules (skip the admin SSH port!).
sudo ufw delete allow 22/tcp # honeypot port
sudo ufw delete allow 8080/tcp # dashboard port
# Leave the admin SSH port (e.g. 2222) allowed until you've confirmed SSH on 22.
# 6. Delete Cowrie, captured intel, and the cowrie user (DESTRUCTIVE — skip to keep data).
sudo rm -rf /var/lib/shardlure
sudo userdel -r cowrie
apt/dnf/pacmanpackages installed as dependencies (git, python venv, authbind, Go, build tools) are not removed by either method — they're shared system packages and removing them could break unrelated things. Remove them yourself only if you're sure nothing else needs them.
- Installed OS packages (see above).
- Any extra
ufwrules you added by hand. - Without
--purge:/var/lib/shardlureand thecowrieuser — your captured intel survives so you can archive it. Treat the SQLite DB like a loaded gun (it holds attacker-supplied passwords) andshred/securely delete it when done.
- Cowrie JSON ingest (HASSH, commands, payloads)
- Stealth persona and bait file planting
- Live journal tail and Cowrie append ingest
- Configurable dashboard home point via YAML (
dashboard.home_*) - Incremental Cowrie ingest (offset + inode tracking, no more O(file) per tick)
- Idempotent journal append (dedup against existing events)
- Graceful shutdown on SIGINT/SIGTERM (so Ctrl-C is no longer a war crime)
- DB chmod 0600 + sshd-config auto-rollback on failed reload
- MalwareBazaar payload sharing (CLI + one-click dashboard upload)
- Signal theme — full SOC dashboard redesign with sidebar nav and light/dark toggle
- Dashboard widgets: threat gauge, geography, credentials, brute-force radar, live timeline
- Persistent geo cache (SQLite-backed, survives restarts)
- MalwareBazaar dashboard widget (stats + upload history + family classification)
- Incremental cowrie actor rebuild (per-tick cost is O(touched), not O(all history))
- Seven IP-reputation providers (AbuseIPDB, VirusTotal, GreyNoise, Shodan, OTX, IPQualityScore, IPinfo)
- Dashboard auth token forwarded on every request + cross-page navigation
- One-command uninstall (
uninstall [--purge]) with SSH-restore-first safety - Full-window analytics — MITRE/TTP/IOC/graph/deobf cover the whole selected window (not a recent sample), with capped widgets disclosing "N of M"
- Multi-theme support (Signal, Meridian, Sprite) with live switching
- 3D Cobe globe with WebGL arcs, drag rotation, and proper listener lifecycle
- Dashboard settings panel (API keys and theme — masked + connection-tested; home remains YAML)
- Runtime keystore (DB > env > config precedence, no restart needed)
- AbuseIPDB outbound reporting (CLI + dashboard Report All, vetting gate, 24h dedup)
- Tunnel/proxy target tracking (
direct-tcpipaggregation + red-team widget) - Globe artifacts on every theme (satellites, live badge, per-actor analytics chips)
- Ambient globe dashboard (analyst widgets consolidated in
/intel) - Intel command palette (actors / IPs / sessions / commands / payloads)
- GeoLite2 MMDB enrichment (escape the ip-api.com rate limits arc)
- Real-time WebSocket feed (current dashboard polls every 5s, which is fine but mid)
Q: Does this make my server safer? A: Marginally. It moves real SSH to a private port (good) and runs a fake one (interesting). The main value is intel: you learn what botnets are doing to boxes that look like yours.
Q: Will I get cool maps? A: Yes. There is a globe. It rotates. Chartreuse arcs converge on your home point like you're in a 2007 hacker movie. The intel console has a threat gauge, brute-force radar, and attack geography panel.
Q: Is this production-ready? A: It's "single-VPS, one-operator, runs-on-my-laptop" ready. If you want a fleet, you'll want to front the SQLite with something less single-writer. PRs welcome.
Q: Why Go + Python? A: Cowrie is Python. The ingest, classifier, and dashboard are Go because parsing a million journal lines in Python on a 1-vCPU droplet is suffering. The Python is only the installer.
Q: The bait files. Are they convincing? A: They're convincing to bots. A human auditing them for 30 seconds would clock the fake Stripe keys. That's fine — bots are the customer here.
GPL-3.0 - see LICENSE.
| Repo | What it does |
|---|---|
| ShardLure | SSH honeypot + threat-intel dashboard |
| ShardC2 | Red-team C2 framework in Go |
| ShardFlow | Layer-2 LAN workbench (ARP, drop, throttle) |
| ShardShell | PHP post-exploitation shell |
| ShardPass | Minimal TOTP authenticator (Chrome MV3) |
| ShardPet | Pixel-Pokémon browser extension |
| ShardTune | Spotify controller + listening analytics (Chrome/Brave) |