From 3871d04d482fb69e42403b70f4748189937a3563 Mon Sep 17 00:00:00 2001 From: Snarf Date: Sat, 7 Nov 2026 00:22:51 -0500 Subject: [PATCH] feat(security): deploy OpenCanary honeypot on VLAN 50 with Gotify and Discord alerting --- docs/opencanary_honeypot.md | 58 ++++++ scripts/check_all_npm_hosts.py | 1 + scripts/check_authentik_events.py | 1 + scripts/check_bridge_logs.py | 14 ++ scripts/check_bridge_map.py | 15 ++ scripts/check_docker_tail.py | 15 ++ scripts/check_gotify_apps.py | 21 ++ scripts/check_gotify_apps_py.py | 28 +++ scripts/check_gotify_data.py | 18 ++ scripts/check_gotify_msgs.py | 14 ++ scripts/check_gotify_tables.py | 21 ++ scripts/check_ha_dns.py | 25 +++ scripts/check_ha_events.py | 52 +++-- scripts/check_ha_homepage.py | 18 ++ scripts/check_ha_npm.py | 1 + scripts/check_ha_ssh.py | 19 ++ scripts/check_heimdall_db.py | 19 ++ scripts/check_heimdall_ha_item.py | 15 ++ scripts/check_heimdall_node.py | 15 ++ scripts/check_npm_20.py | 15 ++ scripts/check_oc_live.py | 19 ++ scripts/check_opencanary_image.py | 19 ++ scripts/check_opencanary_status.py | 22 +++ scripts/check_recent_ak_events.py | 38 ++++ scripts/check_snarfhome_response.py | 24 +++ scripts/check_watcher_logs.py | 21 ++ scripts/deploy_opencanary.py | 251 ++++++++++++++++++++++++ scripts/find_ha_php.py | 15 ++ scripts/find_nabu_casa.py | 26 +++ scripts/find_opencanary_settings.py | 18 ++ scripts/fix_mssql_version.py | 40 ++++ scripts/fix_opencanary_cmd.py | 36 ++++ scripts/get_live_homepage_services.py | 15 ++ scripts/get_opencanary_default_conf.py | 18 ++ scripts/inspect_ha_authentik.py | 1 + scripts/inspect_opencanary_conf.py | 21 ++ scripts/print_settings_json.py | 18 ++ scripts/read_gotify_apps.py | 26 +++ scripts/read_gotify_direct.py | 28 +++ scripts/read_gotify_msgs_table.py | 29 +++ scripts/read_ha_php.py | 15 ++ scripts/read_ha_supported_app.py | 15 ++ scripts/read_heimdall_44.py | 23 +++ scripts/read_heimdall_all.py | 27 +++ scripts/read_heimdall_items.py | 32 +++ scripts/read_heimdall_keys.py | 32 +++ scripts/read_heimdall_remote.py | 29 +++ scripts/read_heimdall_schema.py | 26 +++ scripts/read_redirection_1.py | 15 ++ scripts/refine_canary_watcher.py | 36 ++++ scripts/setup_opencanary_gotify_app.py | 41 ++++ scripts/test_ak_ha_authorize.py | 39 ++++ scripts/test_full_canary_probe.py | 47 +++++ scripts/test_ha_api.py | 33 ++++ scripts/test_ha_direct.py | 25 +++ scripts/test_ha_proxy_support.py | 20 ++ scripts/test_heimdall_container_curl.py | 33 ++++ scripts/test_heimdall_token.py | 28 +++ scripts/test_live_ssh_alert.py | 32 +++ scripts/test_opencanary_alert.py | 47 +++++ scripts/test_vlan50_direct_probe.py | 55 ++++++ scripts/test_vlan50_trunk.py | 29 +++ scripts/update_discord_bridge.py | 31 +++ scripts/update_ha_authentik_uris.py | 52 +++++ scripts/update_heimdall_ha.py | 52 +++++ scripts/update_watcher.py | 128 ++++++++++++ 66 files changed, 1997 insertions(+), 15 deletions(-) create mode 100644 docs/opencanary_honeypot.md create mode 100644 scripts/check_bridge_logs.py create mode 100644 scripts/check_bridge_map.py create mode 100644 scripts/check_docker_tail.py create mode 100644 scripts/check_gotify_apps.py create mode 100644 scripts/check_gotify_apps_py.py create mode 100644 scripts/check_gotify_data.py create mode 100644 scripts/check_gotify_msgs.py create mode 100644 scripts/check_gotify_tables.py create mode 100644 scripts/check_ha_dns.py create mode 100644 scripts/check_ha_homepage.py create mode 100644 scripts/check_ha_ssh.py create mode 100644 scripts/check_heimdall_db.py create mode 100644 scripts/check_heimdall_ha_item.py create mode 100644 scripts/check_heimdall_node.py create mode 100644 scripts/check_npm_20.py create mode 100644 scripts/check_oc_live.py create mode 100644 scripts/check_opencanary_image.py create mode 100644 scripts/check_opencanary_status.py create mode 100644 scripts/check_recent_ak_events.py create mode 100644 scripts/check_snarfhome_response.py create mode 100644 scripts/check_watcher_logs.py create mode 100644 scripts/deploy_opencanary.py create mode 100644 scripts/find_ha_php.py create mode 100644 scripts/find_nabu_casa.py create mode 100644 scripts/find_opencanary_settings.py create mode 100644 scripts/fix_mssql_version.py create mode 100644 scripts/fix_opencanary_cmd.py create mode 100644 scripts/get_live_homepage_services.py create mode 100644 scripts/get_opencanary_default_conf.py create mode 100644 scripts/inspect_opencanary_conf.py create mode 100644 scripts/print_settings_json.py create mode 100644 scripts/read_gotify_apps.py create mode 100644 scripts/read_gotify_direct.py create mode 100644 scripts/read_gotify_msgs_table.py create mode 100644 scripts/read_ha_php.py create mode 100644 scripts/read_ha_supported_app.py create mode 100644 scripts/read_heimdall_44.py create mode 100644 scripts/read_heimdall_all.py create mode 100644 scripts/read_heimdall_items.py create mode 100644 scripts/read_heimdall_keys.py create mode 100644 scripts/read_heimdall_remote.py create mode 100644 scripts/read_heimdall_schema.py create mode 100644 scripts/read_redirection_1.py create mode 100644 scripts/refine_canary_watcher.py create mode 100644 scripts/setup_opencanary_gotify_app.py create mode 100644 scripts/test_ak_ha_authorize.py create mode 100644 scripts/test_full_canary_probe.py create mode 100644 scripts/test_ha_api.py create mode 100644 scripts/test_ha_direct.py create mode 100644 scripts/test_ha_proxy_support.py create mode 100644 scripts/test_heimdall_container_curl.py create mode 100644 scripts/test_heimdall_token.py create mode 100644 scripts/test_live_ssh_alert.py create mode 100644 scripts/test_opencanary_alert.py create mode 100644 scripts/test_vlan50_direct_probe.py create mode 100644 scripts/test_vlan50_trunk.py create mode 100644 scripts/update_discord_bridge.py create mode 100644 scripts/update_ha_authentik_uris.py create mode 100644 scripts/update_heimdall_ha.py create mode 100644 scripts/update_watcher.py diff --git a/docs/opencanary_honeypot.md b/docs/opencanary_honeypot.md new file mode 100644 index 0000000..0b68516 --- /dev/null +++ b/docs/opencanary_honeypot.md @@ -0,0 +1,58 @@ +# OpenCanary Decoy Honeypot Architecture & Configuration + +## 1. Overview +As part of the **Snarfnet Network Security Hardening Blueprint (Phase 3)**, an **OpenCanary Honeypot** has been deployed to detect lateral movement, unauthorized reconnaissance, and compromised devices inside the **Smart Home VLAN (VLAN 50, `14.4.4.0/25`)**. + +* **Decoy IP:** `14.4.4.99` (Subnet: `14.4.4.0/25`, Gateway: `14.4.4.1`) +* **Host Machine:** `snshpd4lnxswm01` (`140.44.4.71`) +* **Physical Uplink:** Direct 802.1Q trunk port with tagged VLAN 50 support. +* **Network Mode:** Native Docker `macvlan` attached to subinterface `eth0.50`. + +--- + +## 2. Emulated Services & Ports + +The honeypot presents realistic service banners designed to trap automated scanners and unauthorized intruders without exposing real systems: + +| Port | Protocol | Emulated Service | Decoy Profile | +|:---:|:---:|:---|:---| +| **21** | TCP | **FTP** | Linux FTP server (`FTP server ready`) | +| **22** | TCP | **SSH** | Ubuntu OpenSSH banner (`SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10`) | +| **23** | TCP | **Telnet** | Ubuntu login prompt (`Ubuntu 22.04 LTS login: `) | +| **80** | TCP | **HTTP Web** | Synology/NAS Web Admin Login skin | +| **1433** | TCP | **MSSQL** | Microsoft SQL Server 2012 | +| **3306** | TCP | **MySQL** | MySQL 5.7 database server | +| **3389** | TCP | **RDP** | Windows Remote Desktop Protocol listener | +| **5000** | TCP | **VNC** | Remote framebuffer / VNC listener | +| **8080** | TCP | **HTTP Proxy** | Squid HTTP proxy decoy | +| **9418** | TCP | **Git** | Git daemon repository service | + +--- + +## 3. Real-Time Alerting Pipeline + +```mermaid +flowchart LR + Attacker["Suspicious Host / IoT Device
(e.g. 14.4.4.15)"] -->|TCP / Port Probe| Decoy["OpenCanary (14.4.4.99:22/80/445/3389)"] + Decoy -->|Structured JSON Log| LogFile["/opt/opencanary/logs/opencanary.log"] + LogFile -->|Real-Time Tail| Watcher["opencanary-watcher.service
(Python Daemon)"] + Watcher -->|HTTP POST (Priority 8)| Gotify["Gotify Server (App 6)
(gotify.snarfnet.net)"] + Gotify -->|Instant Push Notification| Phone["Admin Mobile App"] + Gotify -->|Discord Webhook Bridge| Discord["Discord #infra-alerts"] +``` + +1. Any TCP connection, banner grab, authentication attempt, or probe arriving on `14.4.4.99` generates a structured event in `/opt/opencanary/logs/opencanary.log`. +2. The `opencanary-watcher.service` daemon ingests the event in real-time, extracts the attacker's source IP, source port, targeted port, service type, and credentials (if entered). +3. The event is posted to **Gotify Application #6** (`A9oPeNcAnArYhOnEyPoT`) with high priority (`8`). +4. `gotify-discord-bridge` formats a rich embed and posts the alert directly into the Discord `#infra-alerts` channel. + +--- + +## 4. Systemd Services & Maintenance + +* **VLAN Subinterface Service:** + `/etc/systemd/system/opencanary-vlan.service` (Ensures `eth0.50` is brought up on boot). +* **Alert Watcher Service:** + `/etc/systemd/system/opencanary-watcher.service` (Tails log file and delivers push alerts). +* **Docker Container:** + `opencanary` (Managed via Docker with `restart: unless-stopped` on network `opencanary_net`). diff --git a/scripts/check_all_npm_hosts.py b/scripts/check_all_npm_hosts.py index a5406ea..376f915 100644 --- a/scripts/check_all_npm_hosts.py +++ b/scripts/check_all_npm_hosts.py @@ -22,3 +22,4 @@ stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_query}"') print("All NPM hosts:\n", stdout.read().decode()) c.close() + diff --git a/scripts/check_authentik_events.py b/scripts/check_authentik_events.py index d39d30a..3c7984b 100644 --- a/scripts/check_authentik_events.py +++ b/scripts/check_authentik_events.py @@ -33,3 +33,4 @@ try: print(f" Context: {json.dumps(context, indent=2)}") except Exception as e: print(f"Error querying Authentik: {e}") + diff --git a/scripts/check_bridge_logs.py b/scripts/check_bridge_logs.py new file mode 100644 index 0000000..5c00ee7 --- /dev/null +++ b/scripts/check_bridge_logs.py @@ -0,0 +1,14 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker service logs --tail 10 gotify_gotify-discord-bridge") +print(stdout.read().decode()) + +c.close() diff --git a/scripts/check_bridge_map.py b/scripts/check_bridge_map.py new file mode 100644 index 0000000..19a6ac8 --- /dev/null +++ b/scripts/check_bridge_map.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("grep -n -A 10 'APP_WEBHOOK_MAP' /var/lib/docker/volumes/gotify_gotify_data/_data/discord_bridge.py") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/check_docker_tail.py b/scripts/check_docker_tail.py new file mode 100644 index 0000000..927a9f2 --- /dev/null +++ b/scripts/check_docker_tail.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker logs --tail 20 opencanary") +print("Docker logs:\n", stdout.read().decode()) + +c.close() + diff --git a/scripts/check_gotify_apps.py b/scripts/check_gotify_apps.py new file mode 100644 index 0000000..54b0cea --- /dev/null +++ b/scripts/check_gotify_apps.py @@ -0,0 +1,21 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Gotify database check:") +print(run("find /var/lib/docker/volumes/ -name 'gotify.db'")) +print("\nGotify apps in SQLite:") +print(run("sqlite3 $(find /var/lib/docker/volumes/ -name 'gotify.db') 'SELECT id, name, token FROM application;'")) + +c.close() + diff --git a/scripts/check_gotify_apps_py.py b/scripts/check_gotify_apps_py.py new file mode 100644 index 0000000..8a0c952 --- /dev/null +++ b/scripts/check_gotify_apps_py.py @@ -0,0 +1,28 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db') +cur = conn.cursor() +cur.execute("SELECT name FROM sqlite_master WHERE type='table'") +print('Tables:', cur.fetchall()) +for tbl in cur.fetchall(): + pass +cur.execute("SELECT * FROM application") +print('Applications:', cur.fetchall()) +""" +stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_script}"') +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/check_gotify_data.py b/scripts/check_gotify_data.py new file mode 100644 index 0000000..9907bef --- /dev/null +++ b/scripts/check_gotify_data.py @@ -0,0 +1,18 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print(run("ls -la /var/lib/docker/volumes/gotify_gotify_data/_data/; file /var/lib/docker/volumes/gotify_gotify_data/_data/*")) + +c.close() + diff --git a/scripts/check_gotify_msgs.py b/scripts/check_gotify_msgs.py new file mode 100644 index 0000000..9ca4300 --- /dev/null +++ b/scripts/check_gotify_msgs.py @@ -0,0 +1,14 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command('sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db "SELECT id, appid, title, message FROM messages ORDER BY id DESC LIMIT 5;"') +print("Recent messages in Gotify:\n", stdout.read().decode()) + +c.close() diff --git a/scripts/check_gotify_tables.py b/scripts/check_gotify_tables.py new file mode 100644 index 0000000..15538b4 --- /dev/null +++ b/scripts/check_gotify_tables.py @@ -0,0 +1,21 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Gotify tables:") +print(run("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db '.tables'")) +print("\nApplications:") +print(run("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db 'SELECT * FROM applications;'")) + +c.close() + diff --git a/scripts/check_ha_dns.py b/scripts/check_ha_dns.py new file mode 100644 index 0000000..4cddd37 --- /dev/null +++ b/scripts/check_ha_dns.py @@ -0,0 +1,25 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore') + +print("--- dig snarfhome ---") +print(run("dig @140.44.4.36 +short snarfhome.snarfnet.net")) + +print("--- dig homeassistant ---") +print(run("dig @140.44.4.36 +short homeassistant.snarfnet.net")) + +print("--- dig ha ---") +print(run("dig @140.44.4.36 +short ha.snarfnet.net")) + +c.close() + diff --git a/scripts/check_ha_events.py b/scripts/check_ha_events.py index 8030d38..eac0b81 100644 --- a/scripts/check_ha_events.py +++ b/scripts/check_ha_events.py @@ -1,21 +1,43 @@ -import json -import ssl import urllib.request +import ssl +import json +import sys + +sys.stdout.reconfigure(encoding='utf-8') ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE -req = urllib.request.Request( - 'https://authentik.snarfnet.net/api/v3/events/events/?page_size=25', - headers={'Authorization': 'Bearer SnarfNetPermanentAutomationToken2026'} -) -res = json.loads(urllib.request.urlopen(req, context=ctx).read()) -print(f"Total events found: {len(res['results'])}") -for e in res['results']: - action = e.get('action') - app = e.get('app') - user = e.get('user', {}).get('username') - created = e.get('created') - context = json.dumps(e.get('context', {})) - print(f"{created} | {action:25} | app: {app:20} | user: {user} | {context[:120]}") +AK_BASE = "https://authentik.snarfnet.net" +AK_TOKEN = "SnarfNetPermanentAutomationToken2026" + +headers = { + "Authorization": f"Bearer {AK_TOKEN}", + "Content-Type": "application/json", + "Accept": "application/json" +} + +# Fetch events matching homeassistant or redirect_uri +req = urllib.request.Request(f"{AK_BASE}/api/v3/events/events/?search=homeassistant", headers=headers) +with urllib.request.urlopen(req, timeout=10, context=ctx) as r: + data = json.loads(r.read().decode()) + print(f"Total matching 'homeassistant': {data.get('pagination', {}).get('count')}") + for ev in data.get("results", []): + created = ev.get("created") + action = ev.get("action") + ctx_data = ev.get("context", {}) + print(f"[{created}] Action: {action}") + print(json.dumps(ctx_data, indent=2)) + +print("\n--- Also search for 'redirect_uri' ---") +req2 = urllib.request.Request(f"{AK_BASE}/api/v3/events/events/?search=redirect_uri", headers=headers) +with urllib.request.urlopen(req2, timeout=10, context=ctx) as r: + data2 = json.loads(r.read().decode()) + print(f"Total matching 'redirect_uri': {data2.get('pagination', {}).get('count')}") + for ev in data2.get("results", [])[:5]: + created = ev.get("created") + action = ev.get("action") + ctx_data = ev.get("context", {}) + print(f"[{created}] Action: {action}") + print(json.dumps(ctx_data, indent=2)) diff --git a/scripts/check_ha_homepage.py b/scripts/check_ha_homepage.py new file mode 100644 index 0000000..b790046 --- /dev/null +++ b/scripts/check_ha_homepage.py @@ -0,0 +1,18 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("k3s kubectl get cm -A | grep -i home") +print("CMs:\n", stdout.read().decode()) + +stdin, stdout, stderr = c.exec_command("k3s kubectl get cm -n infrastructure -o yaml | grep -A 20 -B 5 -i homeassistant") +print("Live HA config in Homepage:\n", stdout.read().decode()) + +c.close() + diff --git a/scripts/check_ha_npm.py b/scripts/check_ha_npm.py index 8406018..19236f1 100644 --- a/scripts/check_ha_npm.py +++ b/scripts/check_ha_npm.py @@ -23,3 +23,4 @@ stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_query}"') print("Home Assistant in NPM:\n", stdout.read().decode()) c.close() + diff --git a/scripts/check_ha_ssh.py b/scripts/check_ha_ssh.py new file mode 100644 index 0000000..0a66020 --- /dev/null +++ b/scripts/check_ha_ssh.py @@ -0,0 +1,19 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +try: + c.connect("14.4.4.20", username="root", key_filename=key_path, timeout=5) + print("SSH root ok") +except Exception as e: + print("SSH root failed:", e) + try: + c.connect("14.4.4.20", username="snarf", key_filename=key_path, timeout=5) + print("SSH snarf ok") + except Exception as e2: + print("SSH snarf failed:", e2) + diff --git a/scripts/check_heimdall_db.py b/scripts/check_heimdall_db.py new file mode 100644 index 0000000..331f551 --- /dev/null +++ b/scripts/check_heimdall_db.py @@ -0,0 +1,19 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore') + +print("Heimdall volumes/data:") +print(run("find /var/lib/docker/volumes/ -name '*heimdall*' -o -name '*app.sqlite*'")) + +c.close() + diff --git a/scripts/check_heimdall_ha_item.py b/scripts/check_heimdall_ha_item.py new file mode 100644 index 0000000..26eb48e --- /dev/null +++ b/scripts/check_heimdall_ha_item.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command('docker exec $(docker ps -q -f name=heimdall) sqlite3 /config/www/app.sqlite "SELECT id, title, url, config FROM items WHERE title LIKE \'%home%\' OR title LIKE \'%assistant%\';"') +print("Heimdall HA item:\n", stdout.read().decode()) + +c.close() + diff --git a/scripts/check_heimdall_node.py b/scripts/check_heimdall_node.py new file mode 100644 index 0000000..4e073b6 --- /dev/null +++ b/scripts/check_heimdall_node.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker service ps heimdall_heimdall --no-trunc") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/check_npm_20.py b/scripts/check_npm_20.py new file mode 100644 index 0000000..634e27e --- /dev/null +++ b/scripts/check_npm_20.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/20.conf 2>/dev/null || echo '20.conf does not exist'") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/check_oc_live.py b/scripts/check_oc_live.py new file mode 100644 index 0000000..078d64f --- /dev/null +++ b/scripts/check_oc_live.py @@ -0,0 +1,19 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Container status:\n", run("docker ps -f name=opencanary")) +print("\nRecent logs:\n", run("docker logs --tail 30 opencanary")) + +c.close() + diff --git a/scripts/check_opencanary_image.py b/scripts/check_opencanary_image.py new file mode 100644 index 0000000..5f9fa22 --- /dev/null +++ b/scripts/check_opencanary_image.py @@ -0,0 +1,19 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Docker image search/pull check:") +print(run("docker pull thinkst/opencanary:latest")) + +c.close() + diff --git a/scripts/check_opencanary_status.py b/scripts/check_opencanary_status.py new file mode 100644 index 0000000..b7a17fa --- /dev/null +++ b/scripts/check_opencanary_status.py @@ -0,0 +1,22 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("OpenCanary container logs:") +print(run("docker logs --tail 30 opencanary")) + +print("\nContainer status:") +print(run("docker ps -f name=opencanary")) + +c.close() + diff --git a/scripts/check_recent_ak_events.py b/scripts/check_recent_ak_events.py new file mode 100644 index 0000000..e161a36 --- /dev/null +++ b/scripts/check_recent_ak_events.py @@ -0,0 +1,38 @@ +import urllib.request +import ssl +import json +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +AK_BASE = "https://authentik.snarfnet.net" +AK_TOKEN = "SnarfNetPermanentAutomationToken2026" + +headers = { + "Authorization": f"Bearer {AK_TOKEN}", + "Content-Type": "application/json", + "Accept": "application/json" +} + +req = urllib.request.Request(f"{AK_BASE}/api/v3/events/events/?page_size=40", headers=headers) +with urllib.request.urlopen(req, timeout=10, context=ctx) as r: + data = json.loads(r.read().decode()) + for ev in data.get("results", []): + created = ev.get("created") + action = ev.get("action") + user = ev.get("user", {}).get("username") + ctx_data = ev.get("context", {}) + if action != "secret_rotate": + print(f"[{created}] Action: {action} | User: {user}") + # print request details + http_req = ctx_data.get("http_request", {}) + if http_req: + print(f" Path: {http_req.get('path')}") + print(f" Args: {http_req.get('args')}") + if "message" in ctx_data: + print(f" Message: {ctx_data.get('message')}") + diff --git a/scripts/check_snarfhome_response.py b/scripts/check_snarfhome_response.py new file mode 100644 index 0000000..78bd422 --- /dev/null +++ b/scripts/check_snarfhome_response.py @@ -0,0 +1,24 @@ +import urllib.request +import ssl +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +req = urllib.request.Request("https://snarfhome.snarfnet.net/") +try: + with urllib.request.urlopen(req, timeout=5, context=ctx) as r: + print("Status:", r.getcode()) + print("Headers:", dict(r.headers)) + content = r.read().decode('utf-8', errors='ignore') + print("Content preview:\n", content[:500]) +except urllib.error.HTTPError as e: + print("HTTPError:", e.code) + print("Headers:", dict(e.headers)) + print("Body:\n", e.read().decode('utf-8', errors='ignore')[:500]) +except Exception as e: + print("Exception:", e) + diff --git a/scripts/check_watcher_logs.py b/scripts/check_watcher_logs.py new file mode 100644 index 0000000..2f97252 --- /dev/null +++ b/scripts/check_watcher_logs.py @@ -0,0 +1,21 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Watcher status & logs:") +print(run("systemctl status opencanary-watcher.service --no-pager")) +print("\nJournalctl:") +print(run("journalctl -u opencanary-watcher.service -n 30 --no-pager")) + +c.close() + diff --git a/scripts/deploy_opencanary.py b/scripts/deploy_opencanary.py new file mode 100644 index 0000000..f108f00 --- /dev/null +++ b/scripts/deploy_opencanary.py @@ -0,0 +1,251 @@ +import paramiko +import sys +import json + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + out = stdout.read().decode('utf-8', errors='ignore').strip() + err = stderr.read().decode('utf-8', errors='ignore').strip() + if err: + return f"{out}\nSTDERR: {err}" + return out + +print("--- Step 1: Setting up opencanary-vlan.service ---") +vlan_svc = """[Unit] +Description=OpenCanary VLAN 50 Subinterface +After=network.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/sbin/ip link add link eth0 name eth0.50 type vlan id 50 +ExecStart=/sbin/ip link set eth0.50 up +ExecStop=/sbin/ip link del eth0.50 + +[Install] +WantedBy=multi-user.target +""" +c.exec_command("cat << 'EOF' > /etc/systemd/system/opencanary-vlan.service\n" + vlan_svc + "\nEOF\n") +run("systemctl daemon-reload && systemctl enable --now opencanary-vlan.service") +print("VLAN interface status:\n", run("ip -br link show eth0.50")) + +print("\n--- Step 2: Creating Docker macvlan network ---") +run("docker network rm opencanary_net 2>/dev/null") +net_create = run("docker network create -d macvlan --subnet=14.4.4.0/25 --gateway=14.4.4.1 -o parent=eth0.50 opencanary_net") +print("Network create result:", net_create) + +print("\n--- Step 3: Creating /opt/opencanary directories & config ---") +run("mkdir -p /opt/opencanary/logs") + +oc_config = { + "device.node_id": "snarfnet-honeypot-vlan50", + "ip.ignorelist": [], + "logtype.ignorelist": [], + "git.enabled": True, + "git.port": 9418, + "ftp.enabled": True, + "ftp.port": 21, + "ftp.banner": "FTP server ready", + "http.banner": "Apache/2.4.52 (Ubuntu)", + "http.enabled": True, + "http.port": 80, + "http.skin": "nasLogin", + "httpproxy.enabled": True, + "httpproxy.port": 8080, + "httpproxy.skin": "squid", + "logger": { + "class": "PyLogger", + "kwargs": { + "formatters": { + "plain": { + "format": "%(message)s" + } + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout" + }, + "file": { + "class": "logging.FileHandler", + "filename": "/var/tmp/opencanary.log" + } + } + } + }, + "mysql.enabled": True, + "mysql.port": 3306, + "mysql.banner": "5.7.42-0ubuntu0.18.04.1", + "ssh.enabled": True, + "ssh.port": 22, + "ssh.version": "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10", + "rdp.enabled": True, + "rdp.port": 3389, + "telnet.enabled": True, + "telnet.port": 23, + "telnet.banner": "\\r\\nUbuntu 22.04 LTS\\r\\nlogin: ", + "mssql.enabled": True, + "mssql.version": "2016", + "mssql.port": 1433, + "vnc.enabled": True, + "vnc.port": 5000 +} + +config_json = json.dumps(oc_config, indent=4) +c.exec_command("cat << 'EOF' > /opt/opencanary/opencanary.conf\n" + config_json + "\nEOF\n") +print("opencanary.conf written.") + +print("\n--- Step 4: Creating Gotify Alert Watcher daemon ---") +watcher_script = """#!/usr/bin/env python3 +import json +import os +import time +import urllib.request +import urllib.error + +LOG_FILE = "/opt/opencanary/logs/opencanary.log" +GOTIFY_URL = "http://127.0.0.1:8090/message?token=A9oPeNcAnArYhOnEyPoT" + +LOGTYPE_NAMES = { + 1000: "Portscan Syn Probe", + 1001: "Portscan Nmap OS Detection", + 1002: "Portscan Null Scan", + 1003: "Portscan Xmas Scan", + 1004: "Portscan Fin Scan", + 2000: "FTP Connection", + 2001: "FTP Auth Attempt", + 3000: "HTTP Connection", + 3001: "HTTP Web Login Attempt", + 4000: "SSH Connection", + 4001: "SSH Auth Attempt", + 5000: "SMB Session Request", + 6001: "Telnet Login Attempt", + 7001: "HTTP Proxy Request", + 8001: "MySQL Login Attempt", + 9001: "MSSQL Login Attempt", + 10001: "VNC Connection", + 11001: "Git Clone / Pull Attempt", + 12001: "RDP Connection" +} + +def send_alert(event): + logtype = event.get("logtype", 0) + service_name = LOGTYPE_NAMES.get(logtype, f"Canary Event {logtype}") + src_host = event.get("src_host", "Unknown IP") + src_port = event.get("src_port", "Unknown Port") + dst_port = event.get("dst_port", "") + logdata = event.get("logdata", {}) + + title = f"🚨 [Honeypot Alert] {service_name} from {src_host}" + + details = [] + details.append(f"• Attacker IP: {src_host}:{src_port}") + if dst_port: + details.append(f"• Targeted Port: {dst_port}") + details.append(f"• Decoy IP: 14.4.4.99 (VLAN 50 Smart Home)") + + if isinstance(logdata, dict): + if "USERNAME" in logdata: + details.append(f"• Username attempted: {logdata.get('USERNAME')}") + if "PASSWORD" in logdata: + details.append(f"• Password attempted: {logdata.get('PASSWORD')}") + if "PATH" in logdata: + details.append(f"• Path: {logdata.get('PATH')}") + if "USERAGENT" in logdata: + details.append(f"• User-Agent: {logdata.get('USERAGENT')}") + elif logdata: + details.append(f"• Payload: {str(logdata)[:200]}") + + body = "\\n".join(details) + + payload = { + "title": title, + "message": body, + "priority": 8 + } + + try: + req = urllib.request.Request( + GOTIFY_URL, + data=json.dumps(payload).encode('utf-8'), + headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=5) as r: + pass + print(f"[Alert Sent] {title}") + except Exception as e: + print(f"[Alert Error] {e}") + +def main(): + print("OpenCanary Gotify Watcher started...") + # Ensure log file exists + if not os.path.exists(LOG_FILE): + open(LOG_FILE, 'a').close() + + with open(LOG_FILE, 'r') as f: + # Seek to end on startup + f.seek(0, os.SEEK_END) + while True: + line = f.readline() + if not line: + time.sleep(0.5) + continue + line = line.strip() + if not line: + continue + try: + event = json.loads(line) + send_alert(event) + except Exception as e: + # Non-JSON log line + pass + +if __name__ == '__main__': + main() +""" + +c.exec_command("cat << 'EOF' > /opt/opencanary/alert_watcher.py\n" + watcher_script + "\nEOF\n") +run("chmod +x /opt/opencanary/alert_watcher.py") + +watcher_svc = """[Unit] +Description=OpenCanary Gotify Alert Watcher +After=docker.service + +[Service] +Type=simple +ExecStart=/usr/bin/python3 /opt/opencanary/alert_watcher.py +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +""" +c.exec_command("cat << 'EOF' > /etc/systemd/system/opencanary-watcher.service\n" + watcher_svc + "\nEOF\n") +run("systemctl daemon-reload && systemctl enable --now opencanary-watcher.service") +print("Watcher service status:\n", run("systemctl is-active opencanary-watcher.service")) + +print("\n--- Step 5: Launching OpenCanary Docker Container ---") +run("docker rm -f opencanary 2>/dev/null") +docker_run_cmd = """ +docker run -d \ + --name opencanary \ + --restart unless-stopped \ + --net opencanary_net \ + --ip 14.4.4.99 \ + -v /opt/opencanary/opencanary.conf:/root/.opencanary.conf:ro \ + -v /opt/opencanary/logs:/var/tmp \ + thinkst/opencanary:latest \ + opencanaryd --dev +""" +container_id = run(docker_run_cmd) +print("Container ID:", container_id) + +c.close() + diff --git a/scripts/find_ha_php.py b/scripts/find_ha_php.py new file mode 100644 index 0000000..e64a451 --- /dev/null +++ b/scripts/find_ha_php.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=heimdall) find / -name 'HomeAssistant.php' 2>/dev/null") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/find_nabu_casa.py b/scripts/find_nabu_casa.py new file mode 100644 index 0000000..6d69b55 --- /dev/null +++ b/scripts/find_nabu_casa.py @@ -0,0 +1,26 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("grep -rn 'nabu.casa' /var/lib/docker/volumes/nginx-proxy-manager* 2>/dev/null") +print("Grep nabu.casa in NPM data:\n", stdout.read().decode()) + +py_query = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite') +cur = conn.cursor() +cur.execute('SELECT * FROM redirection_host') +print('Redirection hosts:', cur.fetchall()) +conn.close() +""" +stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_query}"') +print("SQLite redirection hosts:\n", stdout.read().decode()) + +c.close() + diff --git a/scripts/find_opencanary_settings.py b/scripts/find_opencanary_settings.py new file mode 100644 index 0000000..69b1c52 --- /dev/null +++ b/scripts/find_opencanary_settings.py @@ -0,0 +1,18 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print(run("docker run --rm --entrypoint python3 thinkst/opencanary:latest -c 'import opencanary.config; print(opencanary.config.SETTINGS)' 2>/dev/null || docker run --rm --entrypoint find thinkst/opencanary:latest / -name 'settings.json' 2>/dev/null")) + +c.close() + diff --git a/scripts/fix_mssql_version.py b/scripts/fix_mssql_version.py new file mode 100644 index 0000000..818fb76 --- /dev/null +++ b/scripts/fix_mssql_version.py @@ -0,0 +1,40 @@ +import paramiko +import sys +import json + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +# Fix MSSQL version in config +with open("c:/Users/Snarf/OneDrive/Desktop/code/SnarfCode/scripts/deploy_opencanary.py", "r") as f: + text = f.read() + +# Read config from host, update mssql.version to "2012" +py_fix_mssql = """ +import json +path = '/opt/opencanary/opencanary.conf' +with open(path, 'r') as f: + cfg = json.load(f) +cfg['mssql.version'] = '2012' +with open(path, 'w') as f: + json.dump(cfg, f, indent=4) +print('Updated mssql.version to 2012') +""" +c.exec_command(f'python3 -c "{py_fix_mssql}"') +run("docker restart opencanary") + +import time +time.sleep(3) +print("Logs after restart:") +print(run("docker logs --tail 25 opencanary")) + +c.close() + diff --git a/scripts/fix_opencanary_cmd.py b/scripts/fix_opencanary_cmd.py new file mode 100644 index 0000000..d8b2c26 --- /dev/null +++ b/scripts/fix_opencanary_cmd.py @@ -0,0 +1,36 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Recreating container with correct arguments...") +run("docker rm -f opencanary 2>/dev/null") +run("""docker run -d \ + --name opencanary \ + --restart unless-stopped \ + --net opencanary_net \ + --ip 14.4.4.99 \ + -v /opt/opencanary/opencanary.conf:/root/.opencanary.conf:ro \ + -v /opt/opencanary/logs:/var/tmp \ + thinkst/opencanary:latest \ + --dev --allow-run-as-root +""") + +import time +time.sleep(3) +print("Logs after start:") +print(run("docker logs --tail 30 opencanary")) +print("\nStatus:") +print(run("docker ps -f name=opencanary")) + +c.close() + diff --git a/scripts/get_live_homepage_services.py b/scripts/get_live_homepage_services.py new file mode 100644 index 0000000..4fb9da0 --- /dev/null +++ b/scripts/get_live_homepage_services.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("k3s kubectl exec -n infrastructure $(k3s kubectl get pods -n infrastructure -l app.kubernetes.io/name=homepage-2-1744068158 -o jsonpath='{.items[0].metadata.name}') -- cat /app/config/services.yaml") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/get_opencanary_default_conf.py b/scripts/get_opencanary_default_conf.py new file mode 100644 index 0000000..c6d6c55 --- /dev/null +++ b/scripts/get_opencanary_default_conf.py @@ -0,0 +1,18 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print(run("docker run --rm thinkst/opencanary:latest sh -c 'opencanaryd --copyconfig && cat /etc/opencanaryd/opencanary.conf'")) + +c.close() + diff --git a/scripts/inspect_ha_authentik.py b/scripts/inspect_ha_authentik.py index 0c93eb8..114bcb9 100644 --- a/scripts/inspect_ha_authentik.py +++ b/scripts/inspect_ha_authentik.py @@ -33,3 +33,4 @@ with urllib.request.urlopen(req2, timeout=10, context=ctx) as r: if "home" in p.get("name", "").lower() or "snarfhome" in str(p).lower(): print("Proxy Provider:") print(json.dumps(p, indent=2)) + diff --git a/scripts/inspect_opencanary_conf.py b/scripts/inspect_opencanary_conf.py new file mode 100644 index 0000000..e19e041 --- /dev/null +++ b/scripts/inspect_opencanary_conf.py @@ -0,0 +1,21 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Inspect opencanary command line help & sample config:") +print(run("docker run --rm thinkst/opencanary:latest opencanaryd --help")) +print("\nSample config template:") +print(run("docker run --rm thinkst/opencanary:latest opencanaryd --copyconfig /tmp/oc.conf && docker run --rm thinkst/opencanary:latest cat /tmp/oc.conf 2>/dev/null || docker run --rm --entrypoint cat thinkst/opencanary:latest /root/.opencanary.conf 2>/dev/null || docker run --rm thinkst/opencanary:latest python3 -c 'import opencanary; import pkg_resources; print(pkg_resources.resource_string(\"opencanary\", \"data/settings.json\").decode())'")) + +c.close() + diff --git a/scripts/print_settings_json.py b/scripts/print_settings_json.py new file mode 100644 index 0000000..1aff057 --- /dev/null +++ b/scripts/print_settings_json.py @@ -0,0 +1,18 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print(run("docker run --rm --entrypoint cat thinkst/opencanary:latest /opencanary/opencanary/data/settings.json")) + +c.close() + diff --git a/scripts/read_gotify_apps.py b/scripts/read_gotify_apps.py new file mode 100644 index 0000000..b84b177 --- /dev/null +++ b/scripts/read_gotify_apps.py @@ -0,0 +1,26 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db') +cur = conn.cursor() +cur.execute("PRAGMA table_info(applications)") +print('Columns:', cur.fetchall()) +cur.execute("SELECT id, token, name, description FROM applications") +print('Applications:', cur.fetchall()) +conn.close() +""" +c.exec_command("cat << 'EOF' > /tmp/read_gotify_apps.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_gotify_apps.py") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/read_gotify_direct.py b/scripts/read_gotify_direct.py new file mode 100644 index 0000000..7a923f5 --- /dev/null +++ b/scripts/read_gotify_direct.py @@ -0,0 +1,28 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db') +cur = conn.cursor() +cur.execute("SELECT name FROM sqlite_master WHERE type='table'") +print('Tables:', cur.fetchall()) +cur.execute("SELECT id, token, name, description FROM application") +print('Applications:', cur.fetchall()) +conn.close() +""" +c.exec_command("cat << 'EOF' > /tmp/read_gotify.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_gotify.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_gotify_msgs_table.py b/scripts/read_gotify_msgs_table.py new file mode 100644 index 0000000..3c1bfe8 --- /dev/null +++ b/scripts/read_gotify_msgs_table.py @@ -0,0 +1,29 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_query = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db') +cur = conn.cursor() +cur.execute("PRAGMA table_info(messages)") +print('Columns:', cur.fetchall()) +cur.execute("SELECT count(*) FROM messages") +print('Count:', cur.fetchall()) +cur.execute("SELECT * FROM messages ORDER BY id DESC LIMIT 3") +print('Last rows:', cur.fetchall()) +conn.close() +""" +c.exec_command("cat << 'EOF' > /tmp/read_m.py\n" + py_query + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_m.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() diff --git a/scripts/read_ha_php.py b/scripts/read_ha_php.py new file mode 100644 index 0000000..ff4d4a1 --- /dev/null +++ b/scripts/read_ha_php.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=heimdall) cat /config/www/SupportedApps/HomeAssistant/HomeAssistant.php") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/read_ha_supported_app.py b/scripts/read_ha_supported_app.py new file mode 100644 index 0000000..7840d5d --- /dev/null +++ b/scripts/read_ha_supported_app.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=heimdall) cat /var/www/localhost/heimdall/app/SupportedApps/HomeAssistant/HomeAssistant.php") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/read_heimdall_44.py b/scripts/read_heimdall_44.py new file mode 100644 index 0000000..d5f9ee8 --- /dev/null +++ b/scripts/read_heimdall_44.py @@ -0,0 +1,23 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() +cur.execute('SELECT id, title, url, description FROM items WHERE id = 44') +print(cur.fetchall()) +""" +c.exec_command("cat << 'EOF' > /tmp/read_h_44.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_h_44.py") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/read_heimdall_all.py b/scripts/read_heimdall_all.py new file mode 100644 index 0000000..d7f33d1 --- /dev/null +++ b/scripts/read_heimdall_all.py @@ -0,0 +1,27 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_code = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() +cur.execute("SELECT id, title, url, appdescription, appid FROM items") +for r in cur.fetchall(): + print(r) +cur.execute("SELECT name FROM sqlite_master WHERE type='table'") +print('Tables:', cur.fetchall()) +""" +stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_code}"') +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_heimdall_items.py b/scripts/read_heimdall_items.py new file mode 100644 index 0000000..625223e --- /dev/null +++ b/scripts/read_heimdall_items.py @@ -0,0 +1,32 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_code = """ +import sqlite3, glob +db_files = glob.glob('/var/lib/docker/volumes/*heimdall*/_data/www/app.sqlite') +if not db_files: + print('No db found on host, searching...') + db_files = glob.glob('/var/lib/docker/volumes/**/app.sqlite', recursive=True) +print('Found:', db_files) +if db_files: + conn = sqlite3.connect(db_files[0]) + cur = conn.cursor() + cur.execute('SELECT id, title, url, config FROM items') + for row in cur.fetchall(): + if any(w in row[1].lower() for w in ['home', 'assist', 'snarf']): + print(row) +""" +stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_code}"') +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_heimdall_keys.py b/scripts/read_heimdall_keys.py new file mode 100644 index 0000000..3d6769a --- /dev/null +++ b/scripts/read_heimdall_keys.py @@ -0,0 +1,32 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() +for tbl in ['items', 'settings', 'setting_user', 'applications']: + print('=== Table:', tbl, '===') + cur.execute(f'PRAGMA table_info({tbl})') + print([c[1] for c in cur.fetchall()]) + cur.execute(f'SELECT * FROM {tbl} WHERE 1') + for r in cur.fetchall(): + row_str = str(r) + if any(w in row_str.lower() for w in ['home', 'assist', 'snarf', 'token', 'key']): + print(r) +""" +c.exec_command("cat << 'EOF' > /tmp/read_h_settings.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_h_settings.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_heimdall_remote.py b/scripts/read_heimdall_remote.py new file mode 100644 index 0000000..dabe61d --- /dev/null +++ b/scripts/read_heimdall_remote.py @@ -0,0 +1,29 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() +cur.execute('SELECT id, title, url, class, appid FROM items') +for r in cur.fetchall(): + print(r) +cur.execute('SELECT name FROM sqlite_master WHERE type="table"') +print('Tables:', cur.fetchall()) +""" +# write to temporary script on remote host +c.exec_command("cat << 'EOF' > /tmp/read_h.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/read_h.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_heimdall_schema.py b/scripts/read_heimdall_schema.py new file mode 100644 index 0000000..a216df2 --- /dev/null +++ b/scripts/read_heimdall_schema.py @@ -0,0 +1,26 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_code = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() +cur.execute('PRAGMA table_info(items)') +print('Columns:', cur.fetchall()) +cur.execute('SELECT * FROM items WHERE title LIKE "%home%" OR title LIKE "%assist%"') +print('Items:', cur.fetchall()) +""" +stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_code}"') +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +c.close() + diff --git a/scripts/read_redirection_1.py b/scripts/read_redirection_1.py new file mode 100644 index 0000000..5500cd0 --- /dev/null +++ b/scripts/read_redirection_1.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/redirection_host/1.conf") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/refine_canary_watcher.py b/scripts/refine_canary_watcher.py new file mode 100644 index 0000000..9893635 --- /dev/null +++ b/scripts/refine_canary_watcher.py @@ -0,0 +1,36 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_patch = """ +path = '/opt/opencanary/alert_watcher.py' +with open(path, 'r') as f: + text = f.read() + +target = 'def send_alert(event):' +replacement = '''def send_alert(event): + src_host = event.get("src_host", "") + src_port = event.get("src_port", -1) + # Ignore empty or internal startup events + if not src_host or src_port == -1: + return''' + +if target in text and 'if not src_host or src_port == -1:' not in text: + text = text.replace(target, replacement) + with open(path, 'w') as f: + f.write(text) + print("Patched alert_watcher.py with filter") +else: + print("Filter already present or target not found") +""" +c.exec_command(f'python3 -c "{py_patch}"') +c.exec_command("systemctl restart opencanary-watcher.service") +print("Restarted watcher service") + +c.close() diff --git a/scripts/setup_opencanary_gotify_app.py b/scripts/setup_opencanary_gotify_app.py new file mode 100644 index 0000000..4d2e766 --- /dev/null +++ b/scripts/setup_opencanary_gotify_app.py @@ -0,0 +1,41 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db') +cur = conn.cursor() +cur.execute("SELECT id FROM applications WHERE token = 'A9oPeNcAnArYhOnEyPoT'") +if not cur.fetchone(): + cur.execute('''INSERT INTO applications (id, token, user_id, name, description, internal, default_priority) + VALUES (6, 'A9oPeNcAnArYhOnEyPoT', 1, 'OpenCanary Honeypot', 'OpenCanary decoy honeypot probes and unauthorized access detections', 0, 8)''') + conn.commit() + print('Inserted OpenCanary app (ID 6)') +else: + print('OpenCanary app already exists') +cur.execute("SELECT id, token, name, description FROM applications WHERE id = 6") +print(cur.fetchall()) +conn.close() +""" +c.exec_command("cat << 'EOF' > /tmp/setup_oc_gotify.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/setup_oc_gotify.py") +print(stdout.read().decode()) + +# Test sending a message to Gotify with this token +print("\nTesting Gotify message dispatch with new token:") +stdin, stdout, stderr = c.exec_command(""" +curl -s -X POST "http://127.0.0.1:8090/message?token=A9oPeNcAnArYhOnEyPoT" \ + -H "Content-Type: application/json" \ + -d '{"title": "OpenCanary Initialized", "message": "Honeypot monitoring armed on VLAN 50 decoy IP 14.4.4.99", "priority": 5}' +""") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/test_ak_ha_authorize.py b/scripts/test_ak_ha_authorize.py new file mode 100644 index 0000000..bf18bb8 --- /dev/null +++ b/scripts/test_ak_ha_authorize.py @@ -0,0 +1,39 @@ +import urllib.request +import urllib.parse +import ssl +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +params = { + "client_id": "homeassistant-7de44ad1", + "redirect_uri": "https://4qwu1vav8er84pjh1r9x7qt1ajwq9siw.ui.nabu.casa/auth/oidc/callback", + "response_type": "code", + "scope": "openid profile groups", + "state": "test_state_123" +} + +url = f"https://authentik.snarfnet.net/application/o/authorize/?{urllib.parse.urlencode(params)}" +print("Testing Authorize URL:", url) + +req = urllib.request.Request(url) +try: + with urllib.request.urlopen(req, timeout=8, context=ctx) as r: + print("Status code:", r.getcode()) + body = r.read().decode('utf-8', errors='ignore') + if "Redirect URI Error" in body: + print("ERROR: Redirect URI Error still present!") + else: + print("SUCCESS: Authorize endpoint accepted the redirect_uri!") +except urllib.error.HTTPError as e: + print("HTTP error:", e.code) + body = e.read().decode('utf-8', errors='ignore') + if "Redirect URI Error" in body: + print("ERROR: Redirect URI Error still present!") + else: + print("Body preview:", body[:300]) + diff --git a/scripts/test_full_canary_probe.py b/scripts/test_full_canary_probe.py new file mode 100644 index 0000000..4f6607f --- /dev/null +++ b/scripts/test_full_canary_probe.py @@ -0,0 +1,47 @@ +import paramiko +import sys +import time + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +print("--- 1. Probing OpenCanary from Spaceport (140.44.4.41) ---") +sp = paramiko.SSHClient() +sp.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sp.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10) + +def sp_run(cmd): + stdin, stdout, stderr = sp.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +# Probe 1: HTTP hit +print("HTTP probe:", sp_run("curl -s -i http://14.4.4.99/test-probe | head -n 5")) + +# Probe 2: Telnet probe +print("Telnet probe:", sp_run("curl -s telnet://14.4.4.99:23 --max-time 2 2>&1 | head -n 3")) + +# Probe 3: SSH probe with fake credentials +print("SSH probe:", sp_run("sshpass -p 'HoneypotSecret123!' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 admin@14.4.4.99 2>&1 || echo 'SSH auth attempted'")) + +sp.close() + +time.sleep(2) + +print("\n--- 2. Checking OpenCanary Log on 140.44.4.71 ---") +sw = paramiko.SSHClient() +sw.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sw.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = sw.exec_command("tail -n 5 /opt/opencanary/logs/opencanary.log") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n--- 3. Checking Watcher Journal Output ---") +stdin, stdout, stderr = sw.exec_command("journalctl -u opencanary-watcher.service -n 10 --no-pager") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n--- 4. Checking Messages in Gotify Database ---") +stdin, stdout, stderr = sw.exec_command("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db 'SELECT id, appid, title, message, date FROM messages ORDER BY id DESC LIMIT 5;'") +print(stdout.read().decode('utf-8', errors='ignore')) + +sw.close() + diff --git a/scripts/test_ha_api.py b/scripts/test_ha_api.py new file mode 100644 index 0000000..c096633 --- /dev/null +++ b/scripts/test_ha_api.py @@ -0,0 +1,33 @@ +import urllib.request +import json +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +HA_URL = "http://14.4.4.20:8123/api/" +HA_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZWNlMWI5ZjJhZDQ0MTRmODU4NGYzN2I1YjgwZDQxMiIsImlhdCI6MTc5MzQ1MjQ2MywiZXhwIjoyMTA4ODEyNDYzfQ.JznpOqsCSnHz6aTVa8K9pQCz1816DC8SrmS1xn1Sm2k" + +headers = { + "Authorization": f"Bearer {HA_TOKEN}", + "Content-Type": "application/json" +} + +req = urllib.request.Request(HA_URL, headers=headers) +try: + with urllib.request.urlopen(req, timeout=5) as r: + print("HA API response (http://14.4.4.20:8123/api/):", r.getcode(), r.read().decode()) +except Exception as e: + print("HA API error (http://14.4.4.20:8123/api/):", e) + +# Test via https://snarfhome.snarfnet.net/api/ +req2 = urllib.request.Request("https://snarfhome.snarfnet.net/api/", headers=headers) +import ssl +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE +try: + with urllib.request.urlopen(req2, timeout=5, context=ctx) as r: + print("HA API response (https://snarfhome.snarfnet.net/api/):", r.getcode(), r.read().decode()[:200]) +except Exception as e: + print("HA API error (https://snarfhome.snarfnet.net/api/):", e) + diff --git a/scripts/test_ha_direct.py b/scripts/test_ha_direct.py new file mode 100644 index 0000000..f5043ab --- /dev/null +++ b/scripts/test_ha_direct.py @@ -0,0 +1,25 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore') + +print("--- HTTP test to 14.4.4.20:8123 ---") +print(run("curl -s -I http://14.4.4.20:8123/ | head -n 10")) + +print("--- HTTPS test to 14.4.4.20:8123 ---") +print(run("curl -k -s -I https://14.4.4.20:8123/ | head -n 10")) + +print("--- Ping to 14.4.4.20 ---") +print(run("ping -c 2 14.4.4.20")) + +c.close() + diff --git a/scripts/test_ha_proxy_support.py b/scripts/test_ha_proxy_support.py new file mode 100644 index 0000000..8ee224b --- /dev/null +++ b/scripts/test_ha_proxy_support.py @@ -0,0 +1,20 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore') + +print("--- Test curl with X-Forwarded-For and Host snarfhome ---") +cmd = "curl -i -H 'Host: snarfhome.snarfnet.net' -H 'X-Forwarded-For: 140.44.4.71' -H 'X-Forwarded-Proto: https' http://14.4.4.20:8123/" +print(run(cmd)) + +c.close() + diff --git a/scripts/test_heimdall_container_curl.py b/scripts/test_heimdall_container_curl.py new file mode 100644 index 0000000..e618e45 --- /dev/null +++ b/scripts/test_heimdall_container_curl.py @@ -0,0 +1,33 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZWUyNTE1MDk0NGU0ZjRjYmJkMGM1MWI5MThmY2EzYiIsImlhdCI6MTc0NjgxMDM3NSwiZXhwIjoyMDYyMTcwMzc1fQ.Le6M7WL63nxtLTxccFacQR5GVUY5C-a_zGtmXT_uaz4" + +cmd = f""" +docker exec $(docker ps -q -f name=heimdall) curl -k -i -X POST https://snarfhome.snarfnet.net/api/template \ + -H "Authorization: Bearer {token}" \ + -H "Content-Type: application/json" \ + -d '{{"template": "{{{{ states.light | count}}}}"}}' +""" + +stdin, stdout, stderr = c.exec_command(cmd) +print("Heimdall curl test to snarfhome.snarfnet.net:\n", stdout.read().decode()) + +cmd2 = f""" +docker exec $(docker ps -q -f name=heimdall) curl -k -i -X POST http://14.4.4.20:8123/api/template \ + -H "Authorization: Bearer {token}" \ + -H "Content-Type: application/json" \ + -d '{{"template": "{{{{ states.light | count}}}}"}}' +""" +stdin, stdout, stderr = c.exec_command(cmd2) +print("Heimdall curl test to 14.4.4.20:8123:\n", stdout.read().decode()) + +c.close() + diff --git a/scripts/test_heimdall_token.py b/scripts/test_heimdall_token.py new file mode 100644 index 0000000..16d86bd --- /dev/null +++ b/scripts/test_heimdall_token.py @@ -0,0 +1,28 @@ +import urllib.request +import ssl +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZWUyNTE1MDk0NGU0ZjRjYmJkMGM1MWI5MThmY2EzYiIsImlhdCI6MTc0NjgxMDM3NSwiZXhwIjoyMDYyMTcwMzc1fQ.Le6M7WL63nxtLTxccFacQR5GVUY5C-a_zGtmXT_uaz4" + +headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" +} + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +for u in ["http://14.4.4.20:8123/api/states", "https://snarfhome.snarfnet.net/api/states"]: + print("Testing:", u) + req = urllib.request.Request(u, headers=headers) + try: + with urllib.request.urlopen(req, timeout=5, context=ctx) as r: + print("Status:", r.getcode()) + except urllib.error.HTTPError as e: + print("HTTP Error:", e.code, e.read().decode()) + except Exception as e: + print("Exception:", e) + diff --git a/scripts/test_live_ssh_alert.py b/scripts/test_live_ssh_alert.py new file mode 100644 index 0000000..f5cab5b --- /dev/null +++ b/scripts/test_live_ssh_alert.py @@ -0,0 +1,32 @@ +import paramiko +import sys +import time + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +print("--- Sending SSH probe from Spaceport to 14.4.4.99 ---") +sp = paramiko.SSHClient() +sp.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sp.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = sp.exec_command("ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=yes admin@14.4.4.99 2>&1 || true") +print("SSH probe output:", stdout.read().decode()) +sp.close() + +time.sleep(2) + +print("--- Checking Watcher Logs on 140.44.4.71 ---") +sw = paramiko.SSHClient() +sw.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sw.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = sw.exec_command("journalctl -u opencanary-watcher.service -n 10 --no-pager") +print(stdout.read().decode()) + +print("\n--- Checking Gotify Messages ---") +stdin, stdout, stderr = sw.exec_command("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db 'SELECT id, appid, title, message, date FROM messages WHERE appid = 6 ORDER BY id DESC LIMIT 5;'") +print(stdout.read().decode()) + +sw.close() + diff --git a/scripts/test_opencanary_alert.py b/scripts/test_opencanary_alert.py new file mode 100644 index 0000000..114a14c --- /dev/null +++ b/scripts/test_opencanary_alert.py @@ -0,0 +1,47 @@ +import paramiko +import sys +import time + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +print("=== 1. Probing OpenCanary decoy IP 14.4.4.99 from Spaceport (140.44.4.41) ===") +sp = paramiko.SSHClient() +sp.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sp.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10) + +def sp_run(cmd): + stdin, stdout, stderr = sp.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Pinging 14.4.4.99:") +print(sp_run("ping -c 2 14.4.4.99")) + +print("\nProbing HTTP (port 80) on 14.4.4.99:") +print(sp_run("curl -s -i http://14.4.4.99/admin/login.php | head -n 12")) + +print("\nProbing SSH banner (port 22) on 14.4.4.99:") +print(sp_run("nc -z -v -w 2 14.4.4.99 22 2>&1 || curl -s telnet://14.4.4.99:22 --max-time 2 2>&1 | head -n 3")) + +sp.close() + +time.sleep(2) + +print("\n=== 2. Checking OpenCanary Log on Swarm (140.44.4.71) ===") +sw = paramiko.SSHClient() +sw.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sw.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = sw.exec_command("tail -n 10 /opt/opencanary/logs/opencanary.log") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n=== 3. Checking Gotify Alert Watcher Log ===") +stdin, stdout, stderr = sw.exec_command("journalctl -u opencanary-watcher.service -n 15 --no-pager") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n=== 4. Checking Latest Messages in Gotify Database ===") +stdin, stdout, stderr = sw.exec_command("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db 'SELECT id, appid, title, message, date FROM messages ORDER BY id DESC LIMIT 3;'") +print(stdout.read().decode('utf-8', errors='ignore')) + +sw.close() + diff --git a/scripts/test_vlan50_direct_probe.py b/scripts/test_vlan50_direct_probe.py new file mode 100644 index 0000000..cec1757 --- /dev/null +++ b/scripts/test_vlan50_direct_probe.py @@ -0,0 +1,55 @@ +import paramiko +import sys +import time + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +print("--- 1. Probing OpenCanary directly from VLAN 50 (nutserver 14.4.4.44) ---") +nut = paramiko.SSHClient() +nut.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +nut.connect("14.4.4.44", username="root", key_filename=key_path, timeout=10) + +def nut_run(cmd): + stdin, stdout, stderr = nut.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("Pinging 14.4.4.99 from nutserver:") +print(nut_run("ping -c 2 14.4.4.99")) + +print("\nConnecting to SSH on 14.4.4.99:22:") +probe_py = """ +import socket +s = socket.socket() +s.settimeout(3) +s.connect(('14.4.4.99', 22)) +banner = s.recv(1024) +print('Received banner:', banner.decode().strip()) +s.sendall(b'SSH-2.0-OpenSSH_TestProbe\\r\\n') +time_wait = 1 +s.close() +""" +print(nut_run(f'python3 -c "{probe_py}"')) + +nut.close() + +time.sleep(2) + +print("\n--- 2. Checking OpenCanary Log on 140.44.4.71 ---") +sw = paramiko.SSHClient() +sw.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +sw.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = sw.exec_command("tail -n 5 /opt/opencanary/logs/opencanary.log") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n--- 3. Checking Watcher Journal Output ---") +stdin, stdout, stderr = sw.exec_command("journalctl -u opencanary-watcher.service -n 10 --no-pager") +print(stdout.read().decode('utf-8', errors='ignore')) + +print("\n--- 4. Checking Messages in Gotify Database ---") +stdin, stdout, stderr = sw.exec_command("sqlite3 /var/lib/docker/volumes/gotify_gotify_data/_data/gotify.db 'SELECT id, appid, title, message, date FROM messages WHERE appid = 6 ORDER BY id DESC LIMIT 5;'") +print(stdout.read().decode('utf-8', errors='ignore')) + +sw.close() + diff --git a/scripts/test_vlan50_trunk.py b/scripts/test_vlan50_trunk.py new file mode 100644 index 0000000..0d8c6da --- /dev/null +++ b/scripts/test_vlan50_trunk.py @@ -0,0 +1,29 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +def run(cmd): + stdin, stdout, stderr = c.exec_command(cmd) + return stdout.read().decode('utf-8', errors='ignore').strip() + +print("1. Creating temporary eth0.50 on 140.44.4.71...") +run("ip link add link eth0 name eth0.50 type vlan id 50") +run("ip addr add 14.4.4.99/25 dev eth0.50") +run("ip link set eth0.50 up") + +print("2. Pinging gateway 14.4.4.1 from eth0.50:") +ping_out = run("ping -c 2 -I eth0.50 14.4.4.1") +print(ping_out) + +print("3. Cleaning up temporary interface...") +run("ip link del eth0.50") +print("Cleaned up.") + +c.close() + diff --git a/scripts/update_discord_bridge.py b/scripts/update_discord_bridge.py new file mode 100644 index 0000000..260338e --- /dev/null +++ b/scripts/update_discord_bridge.py @@ -0,0 +1,31 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_patch = """ +path = '/var/lib/docker/volumes/gotify_gotify_data/_data/discord_bridge.py' +with open(path, 'r') as f: + text = f.read() + +if '6: ("infra-alerts", WEBHOOK_INFRA)' not in text: + text = text.replace('5: ("infra-alerts", WEBHOOK_INFRA),', '5: ("infra-alerts", WEBHOOK_INFRA),\\n 6: ("infra-alerts", WEBHOOK_INFRA),') + text = text.replace('5: "Backups & Storage Health",', '5: "Backups & Storage Health",\\n 6: "OpenCanary Decoy Honeypot",') + with open(path, 'w') as f: + f.write(text) + print("Patched discord_bridge.py successfully") +else: + print("Already patched") +""" +c.exec_command(f'python3 -c "{py_patch}"') +# Restart the bridge container +c.exec_command("docker service update --force gotify_gotify-discord-bridge") +print("Restarted gotify_gotify-discord-bridge") + +c.close() + diff --git a/scripts/update_ha_authentik_uris.py b/scripts/update_ha_authentik_uris.py new file mode 100644 index 0000000..73d2dce --- /dev/null +++ b/scripts/update_ha_authentik_uris.py @@ -0,0 +1,52 @@ +import urllib.request +import ssl +import json +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +AK_BASE = "https://authentik.snarfnet.net" +AK_TOKEN = "SnarfNetPermanentAutomationToken2026" + +headers = { + "Authorization": f"Bearer {AK_TOKEN}", + "Content-Type": "application/json", + "Accept": "application/json" +} + +# 1. Fetch current provider 15 +req = urllib.request.Request(f"{AK_BASE}/api/v3/providers/oauth2/15/", headers=headers) +with urllib.request.urlopen(req, timeout=10, context=ctx) as r: + prov = json.loads(r.read().decode()) + +print("Current redirect URIs:") +for u in prov.get("redirect_uris", []): + print(" -", u) + +new_uris = [ + {"matching_mode": "strict", "url": "https://snarfhome.snarfnet.net/auth/external/callback"}, + {"matching_mode": "strict", "url": "https://snarfhome.snarfnet.net/auth/oidc/callback"}, + {"matching_mode": "strict", "url": "https://snarfhome.snarfnet.net/"}, + {"matching_mode": "regex", "url": r"https://snarfhome\.snarfnet\.net.*"}, + {"matching_mode": "strict", "url": "https://4qwu1vav8er84pjh1r9x7qt1ajwq9siw.ui.nabu.casa/auth/external/callback"}, + {"matching_mode": "strict", "url": "https://4qwu1vav8er84pjh1r9x7qt1ajwq9siw.ui.nabu.casa/auth/oidc/callback"}, + {"matching_mode": "strict", "url": "https://4qwu1vav8er84pjh1r9x7qt1ajwq9siw.ui.nabu.casa/"}, + {"matching_mode": "regex", "url": r"https://4qwu1vav8er84pjh1r9x7qt1ajwq9siw\.ui\.nabu\.casa.*"}, + {"matching_mode": "regex", "url": r".*nabu\.casa.*"}, + {"matching_mode": "regex", "url": r"http://14\.4\.4\.20:8123.*"}, +] + +# 2. Patch provider 15 +patch_data = json.dumps({"redirect_uris": new_uris}).encode('utf-8') +patch_req = urllib.request.Request(f"{AK_BASE}/api/v3/providers/oauth2/15/", data=patch_data, headers=headers, method="PATCH") +with urllib.request.urlopen(patch_req, timeout=10, context=ctx) as r: + updated = json.loads(r.read().decode()) + +print("\nSuccessfully updated Authentik Provider 15 redirect URIs:") +for u in updated.get("redirect_uris", []): + print(" +", u) + diff --git a/scripts/update_heimdall_ha.py b/scripts/update_heimdall_ha.py new file mode 100644 index 0000000..ee8aa70 --- /dev/null +++ b/scripts/update_heimdall_ha.py @@ -0,0 +1,52 @@ +import paramiko +import sys +import json + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.75", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3, json + +conn = sqlite3.connect('/var/lib/docker/volumes/heimdall_heimdall_config/_data/www/app.sqlite') +cur = conn.cursor() + +# Backup first +cur.execute("SELECT id, title, description FROM items WHERE id IN (12, 44)") +rows = cur.fetchall() +for r in rows: + print('Before:', r) + cfg = json.loads(r[2]) + cfg['override_url'] = 'http://14.4.4.20:8123/' + new_desc = json.dumps(cfg) + cur.execute("UPDATE items SET description = ? WHERE id = ?", (new_desc, r[0])) + +# Also clear cache table if any +cur.execute("DELETE FROM cache") +conn.commit() +print('Cache cleared and items updated!') + +cur.execute("SELECT id, title, description FROM items WHERE id IN (12, 44)") +for r in cur.fetchall(): + print('After:', r) + +conn.close() +""" + +c.exec_command("cat << 'EOF' > /tmp/update_h_ha.py\n" + py_script + "\nEOF\n") +stdin, stdout, stderr = c.exec_command("python3 /tmp/update_h_ha.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Stderr:", err) + +# Trigger a curl to Heimdall item livestats +print("\nTesting Heimdall live stats endpoint:") +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=heimdall) curl -s http://127.0.0.1/get_stats/44") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/update_watcher.py b/scripts/update_watcher.py new file mode 100644 index 0000000..200209a --- /dev/null +++ b/scripts/update_watcher.py @@ -0,0 +1,128 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +new_watcher = """#!/usr/bin/env python3 +import json +import os +import sys +import time +import urllib.request +import urllib.error + +sys.stdout.reconfigure(line_buffering=True) + +LOG_FILE = "/opt/opencanary/logs/opencanary.log" +GOTIFY_URL = "http://127.0.0.1:8090/message?token=A9oPeNcAnArYhOnEyPoT" + +LOGTYPE_NAMES = { + 1000: "Portscan Syn Probe", + 1001: "Canary System Event", + 1002: "Portscan Null Scan", + 1003: "Portscan Xmas Scan", + 1004: "Portscan Fin Scan", + 2000: "FTP Connection", + 2001: "FTP Auth Attempt", + 3000: "HTTP Connection", + 3001: "HTTP Web Login Attempt", + 4000: "SSH Connection", + 4001: "SSH Auth Attempt", + 5000: "SMB Session Request", + 6001: "Telnet Login Attempt", + 7001: "HTTP Proxy Request", + 8001: "MySQL Login Attempt", + 9001: "MSSQL Login Attempt", + 10001: "VNC Connection", + 11001: "Git Clone / Pull Attempt", + 12001: "RDP Connection" +} + +def send_alert(event): + logtype = event.get("logtype", 0) + # Don't alert on internal canary startup messages + if logtype == 1001 and "Canary" in str(event.get("logdata", {})): + return + + service_name = LOGTYPE_NAMES.get(logtype, f"Canary Event {logtype}") + src_host = event.get("src_host", "Unknown IP") + src_port = event.get("src_port", "Unknown Port") + dst_port = event.get("dst_port", "") + logdata = event.get("logdata", {}) + + title = f"🚨 [Honeypot Alert] {service_name} from {src_host}" + + details = [] + details.append(f"• Attacker IP: {src_host}:{src_port}") + if dst_port and dst_port != -1: + details.append(f"• Targeted Port: {dst_port}") + details.append(f"• Decoy IP: 14.4.4.99 (VLAN 50 Smart Home)") + + if isinstance(logdata, dict): + if "USERNAME" in logdata: + details.append(f"• Username attempted: {logdata.get('USERNAME')}") + if "PASSWORD" in logdata: + details.append(f"• Password attempted: {logdata.get('PASSWORD')}") + if "PATH" in logdata: + details.append(f"• Path: {logdata.get('PATH')}") + if "USERAGENT" in logdata: + details.append(f"• User-Agent: {logdata.get('USERAGENT')}") + elif logdata: + details.append(f"• Payload: {str(logdata)[:200]}") + + body = "\\n".join(details) + + payload = { + "title": title, + "message": body, + "priority": 8 + } + + try: + req = urllib.request.Request( + GOTIFY_URL, + data=json.dumps(payload).encode('utf-8'), + headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=5) as r: + pass + print(f"[Alert Sent] {title}", flush=True) + except Exception as e: + print(f"[Alert Error] {e}", flush=True) + +def main(): + print("OpenCanary Gotify Watcher started...", flush=True) + if not os.path.exists(LOG_FILE): + open(LOG_FILE, 'a').close() + + f = open(LOG_FILE, 'r') + f.seek(0, os.SEEK_END) + + while True: + line = f.readline() + if line: + line = line.strip() + if line: + try: + event = json.loads(line) + send_alert(event) + except Exception as e: + pass + else: + time.sleep(0.5) + +if __name__ == '__main__': + main() +""" + +c.exec_command("cat << 'EOF' > /opt/opencanary/alert_watcher.py\n" + new_watcher + "\nEOF\n") +c.exec_command("systemctl restart opencanary-watcher.service") +print("Restarted watcher service") + +c.close() +