feat(security): deploy OpenCanary honeypot on VLAN 50 with Gotify and Discord alerting

This commit is contained in:
2026-11-07 00:22:51 -05:00
parent 0ed293b6ea
commit 3871d04d48
66 changed files with 1997 additions and 15 deletions

View File

@@ -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<br>(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<br>(Python Daemon)"]
Watcher -->|HTTP POST (Priority 8)| Gotify["Gotify Server (App 6)<br>(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`).

View File

@@ -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()

View File

@@ -33,3 +33,4 @@ try:
print(f" Context: {json.dumps(context, indent=2)}")
except Exception as e:
print(f"Error querying Authentik: {e}")

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

25
scripts/check_ha_dns.py Normal file
View File

@@ -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()

View File

@@ -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))

View File

@@ -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()

View File

@@ -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()

19
scripts/check_ha_ssh.py Normal file
View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

15
scripts/check_npm_20.py Normal file
View File

@@ -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()

19
scripts/check_oc_live.py Normal file
View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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')}")

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

15
scripts/find_ha_php.py Normal file
View File

@@ -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()

26
scripts/find_nabu_casa.py Normal file
View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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))

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

15
scripts/read_ha_php.py Normal file
View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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])

View File

@@ -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()

33
scripts/test_ha_api.py Normal file
View File

@@ -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)

25
scripts/test_ha_direct.py Normal file
View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

128
scripts/update_watcher.py Normal file
View File

@@ -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()