feat: add various infrastructure and service diagnostic scripts

This commit is contained in:
2026-11-06 09:02:19 -05:00
parent 00f22f33af
commit 1b8c862b10
59 changed files with 1499 additions and 58 deletions

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- curl -s -X POST "http://localhost/api/auth" -H "Content-Type: application/json" -d \'{"password":"snarfnetadmin"}\'')
print("Aerodrome API auth test:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/status"')
print("api/status response:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

11
scripts/check_apiurl.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- curl -s "http://localhost/admin/login" | grep -i "data-apiurl"')
print("data-apiurl:")
print(stdout.read().decode())
ssh.close()

16
scripts/check_app_dll.py Normal file
View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ls -la /app')
print("/app contents:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Search for AddAuthentication in dlls
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- strings /app/DnsServerApp.dll /app/DnsServerCore.dll 2>/dev/null | grep -i -C 3 "AddAuthentication"')
print("AddAuthentication strings:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

14
scripts/check_cert1.py Normal file
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=5)
stdin, stdout, stderr = c.exec_command("ls -la /var/lib/docker/volumes/nginx-proxy-manager_nginx-letsencrypt/_data/live/npm-1/")
print(stdout.read().decode('utf-8'))
c.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- grep -rn "divLoginSso" /opt/technitium/ 2>/dev/null || true')
print("grep divLoginSso:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -1,20 +1,44 @@
import paramiko
import sys
sys.stdout.reconfigure(encoding='utf-8')
def check_cluster(host, name):
print(f"=== Checking {name} ({host}) ===")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
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.11", username="root", key_filename=key_path, timeout=5)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl get pods -n dns -o wide')
print("Pods:")
print(stdout.read().decode())
stdin, stdout, stderr = c.exec_command("k3s kubectl -n dns get pods,svc")
print("=== PODS & SVCS ===")
print(stdout.read().decode('utf-8'))
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole -v')
print("Pihole version:")
print(stdout.read().decode())
stdin, stdout, stderr = c.exec_command("k3s kubectl -n dns describe svc pihole-dns-vip | grep -E 'Events:|Message|Failed'")
print("=== SERVICE EVENTS ===")
print(stdout.read().decode('utf-8'))
# Set password explicitly
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole setpassword snarfnetadmin')
out = stdout.read().decode()
err = stderr.read().decode()
print("Set password with pihole setpassword:")
print(out)
if err:
print("ERR:", err)
c.close()
if "Unknown" in out or "invalid" in out.lower() or err:
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole -a -p snarfnetadmin')
print("Fallback pihole -a -p:")
print(stdout.read().decode())
# Check Technitium login via API
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/user/login?user=admin&pass=snarfnetadmin"')
login_res = stdout.read().decode()
print("Technitium API Login:", login_res)
# Check Technitium SSO config
stdin, stdout, stderr = ssh.exec_command("k3s kubectl exec -n dns deployment/technitium-dns -- cat /etc/dns/auth.config 2>/dev/null || echo 'No auth.config'")
print("Technitium auth.config:")
print(stdout.read().decode())
ssh.close()
check_cluster('140.44.4.11', 'Aerodrome')
check_cluster('140.44.4.41', 'Spaceport')

11
scripts/check_etc_dns.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ls -la /etc/dns/')
print("/etc/dns listing:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole-FTL --config webserver.api.password')
print("Current config webserver.api.password:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check pihole-FTL --config --help or options
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole-FTL --config 2>&1 || true')
print("pihole-FTL --config:")
print(stdout.read().decode('utf-8', errors='ignore')[:1500])
ssh.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- sed -n "20,60p" /var/www/html/admin/scripts/js/login.js')
print("login.js lines 20-60:")
print(stdout.read().decode())
ssh.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- sed -n "60,110p" /var/www/html/admin/scripts/js/login.js')
print("login.js lines 60-110:")
print(stdout.read().decode())
ssh.close()

11
scripts/check_mainjs.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- sed -n "40,65p" /opt/technitium/dns/www/js/main.js')
print("main.js lines 40-65:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

21
scripts/check_max_id.py Normal file
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=5)
py_cmd = """python3 -c "
import sqlite3
conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite')
c = conn.cursor()
c.execute('SELECT MAX(id) FROM proxy_host')
print('MAX ID:', c.fetchone()[0])
" """
stdin, stdout, stderr = c.exec_command(py_cmd)
print(stdout.read().decode('utf-8'))
c.close()

View File

@@ -1,14 +1,13 @@
import paramiko
import sys
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.71', username='snarf', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
sys.stdout.reconfigure(encoding='utf-8')
def run(cmd):
print(f"=== {cmd} ===")
stdin, stdout, stderr = ssh.exec_command(cmd)
print(stdout.read().decode())
run("sudo docker service ps nginx-proxy-manager_app --filter 'desired-state=running'")
ssh.close()
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=5)
stdin, stdout, stderr = c.exec_command("docker service ps nginx-proxy-manager_app")
print(stdout.read().decode('utf-8'))
c.close()

View File

@@ -0,0 +1,20 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -rn "setFTLConfigValue" /opt/pihole/')
print("grep setFTLConfigValue in /opt/pihole/:")
print(stdout.read().decode('utf-8', errors='ignore'))
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- sed -n "1,50p" /opt/pihole/COL_TABLE')
print("head /opt/pihole/COL_TABLE or similar scripts:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check files in /opt/pihole/
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- ls -la /opt/pihole/')
print("ls /opt/pihole/:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,12 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Check pihole-FTL options for hashing password
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole-FTL --help')
print("pihole-FTL --help:")
print(stdout.read().decode('utf-8', errors='ignore')[:1000])
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -rn "Wrong password" /var/www/ 2>/dev/null || true')
print("Wrong password grep in /var/www/:")
print(stdout.read().decode())
# Check html/login.js
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- find / -name "*login*.js" 2>/dev/null || true')
print("login.js locations:")
print(stdout.read().decode())
ssh.close()

View File

@@ -0,0 +1,18 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Check how pihole setpassword works
# Let's inspect /usr/local/bin/pihole around setFTLConfigValue
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -n -A 20 "setFTLConfigValue()" /usr/local/bin/pihole')
print("setFTLConfigValue function:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check pihole-FTL CLI options for setting or hashing password
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- pihole-FTL --help 2>&1')
print("pihole-FTL help:")
print(stdout.read().decode('utf-8', errors='ignore')[:1500])
ssh.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- sed -n "200,280p" /usr/local/bin/pihole')
print("pihole script lines 200-280:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -rn "setpassword" /opt/pihole/ /usr/local/bin/ 2>/dev/null || true')
print("setpassword references:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check how WEBPASSWORD or FTLCONF is handled during container start in entrypoint
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- cat /entrypoint.sh 2>/dev/null || cat /start.sh 2>/dev/null || ls -l /')
print("Entrypoint check:")
print(stdout.read().decode('utf-8', errors='ignore')[:1500])
ssh.close()

View File

@@ -0,0 +1,25 @@
import paramiko
def check_pihole_toml(host, name):
print(f"=== {name} ({host}) ===")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -A 10 "\[webserver" /etc/pihole/pihole.toml 2>/dev/null || true')
print("webserver section in pihole.toml:")
print(stdout.read().decode('utf-8', errors='ignore'))
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -rn "pwhash" /etc/pihole/ 2>/dev/null || true')
print("pwhash matches:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Let's test logging into pihole API with curl using password "snarfnetadmin"
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- curl -s -X POST "http://localhost/api/auth" -H "Content-Type: application/json" -d \'{"password":"snarfnetadmin"}\'')
print("API auth test:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()
check_pihole_toml('140.44.4.11', 'Aerodrome')
check_pihole_toml('140.44.4.41', 'Spaceport')

View File

@@ -1,14 +1,11 @@
import paramiko
import sys
sys.stdout.reconfigure(encoding='utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
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.11", username="root", key_filename=key_path, timeout=5)
stdin, stdout, stderr = c.exec_command("ps aux | grep kubectl")
print(stdout.read().decode('utf-8'))
c.close()
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ps aux')
print("ps aux:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -n -A 30 "SetWebPassword()" /usr/local/bin/pihole')
print("SetWebPassword implementation:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

11
scripts/check_sso_err.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 25 /var/log/technitium/dns/2026-11-06.log')
print("Recent logs:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,17 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Test SSO login redirect
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -I "http://localhost:5380/sso/login"')
print("SSO login redirect response:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check index.html or what API the frontend calls
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- grep -n -C 5 "divLoginSso" /app/www/index.html 2>/dev/null || grep -n -C 5 "divLoginSso" /app/www/js/app.js 2>/dev/null || true')
print("divLoginSso in app:")
print(stdout.read().decode('utf-8', errors='ignore')[:1500])
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- cat /etc/dns/log.config')
print("log.config:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check where logs are saved or what log.config says
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- python3 -c "import json; print(open(\"/etc/dns/auth.config\", \"rb\").read())" 2>/dev/null || od -c /etc/dns/auth.config | head -n 30')
print("auth.config raw:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- strings /opt/technitium/dns/DnsServerApp.dll /opt/technitium/dns/DnsServerCore.dll | grep -i -C 3 "AddAuthentication"')
print("AddAuthentication:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Also search for "ssoEnabled" or how SSO config is checked during startup
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- strings /opt/technitium/dns/DnsServerCore.dll | grep -i -C 3 "AddOpenIdConnect"')
print("AddOpenIdConnect:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl logs -n dns deployment/technitium-dns --tail=100')
print("Technitium container logs:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check /etc/dns/logs if any
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ls -la /etc/dns/logs 2>/dev/null || true')
print("/etc/dns/logs:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,15 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ls -la /var/log/technitium/ 2>/dev/null || true')
print("ls /var/log/technitium:")
print(stdout.read().decode('utf-8', errors='ignore'))
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 50 /var/log/technitium/dns.log 2>/dev/null || tail -n 50 /var/log/technitium/* 2>/dev/null || true')
print("logs:")
print(stdout.read().decode('utf-8', errors='ignore')[:2000])
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- ls -la /var/log/technitium/dns/ 2>/dev/null || true')
print("ls /var/log/technitium/dns:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Also let's check what SSO error occurred by querying the web service or error log
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 50 /var/log/technitium/dns/* 2>/dev/null || true')
print("files:")
print(stdout.read().decode('utf-8', errors='ignore')[:2000])
ssh.close()

11
scripts/check_utils.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- sed -n "130,175p" /opt/pihole/utils.sh')
print("utils.sh lines 130-175:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,50 @@
import paramiko
import json
import time
def configure_spaceport_technitium():
print("=== Configuring Spaceport Technitium SSO ===")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Login to get token
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/user/login?user=admin&pass=snarfnetadmin"')
data = json.loads(stdout.read().decode())
token = data['token']
print("Spaceport Technitium Token:", token)
url = (
f'"http://localhost:5380/api/admin/sso/set?token={token}'
'&ssoEnabled=true'
'&ssoAuthority=https://authentik.snarfnet.net/application/o/dns2/'
'&ssoClientId=technitium2-dns-oidc'
'&ssoClientSecret=SnarfNetTechnitiumOIDC2026!'
'&ssoMetadataAddress=https://authentik.snarfnet.net/application/o/dns2/.well-known/openid-configuration'
'&ssoScopes=openid,profile,email'
'&ssoAllowSignup=true'
'&ssoAllowSignupOnlyForMappedUsers=false'
'&ssoGroupMap=authentik%20Admins|Administrators"'
)
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s {url}')
print("SSO set response:", stdout.read().decode())
print("Restarting deployment/technitium-dns on Spaceport...")
ssh.exec_command('k3s kubectl rollout restart deployment/technitium-dns -n dns')
time.sleep(12)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl wait --for=condition=Ready pod -l app=technitium-dns -n dns --timeout=30s')
print("Pod wait:", stdout.read().decode())
# Check api/status
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/status"')
print("api/status:", stdout.read().decode())
# Check SSO login redirect
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -v -H "Host: dns2.snarfnet.net" -H "X-Forwarded-Proto: https" "http://localhost:5380/sso/login"')
print("SSO redirect test:")
print(stderr.read().decode('utf-8', errors='ignore')[:1000])
ssh.close()
configure_spaceport_technitium()

View File

@@ -0,0 +1,88 @@
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
token = "SnarfNetPermanentAutomationToken2026"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
def api_call(url, method="GET", payload=None):
data = json.dumps(payload).encode('utf-8') if payload else None
req = urllib.request.Request(url, headers=headers, data=data, method=method)
try:
with urllib.request.urlopen(req, context=ctx, timeout=8) as resp:
return json.loads(resp.read().decode('utf-8'))
except urllib.error.HTTPError as e:
err = e.read().decode('utf-8')
print(f"API Error {e.code} on {method} {url}: {err}")
return None
print("1. Creating OAuth2 Provider for Technitium DNS 2...")
p25_payload = {
"name": "Technitium DNS 2 SSO (Spaceport)",
"authorization_flow": "38a86ef3-f43a-4756-8f54-ca008a8eef6e",
"invalidation_flow": "7a8145b8-2476-4636-9901-825f826df805",
"client_type": "confidential",
"client_id": "technitium2-dns-oidc",
"client_secret": "SnarfNetTechnitiumOIDC2026!",
"redirect_uris": [
{"url": "https://dns2.snarfnet.net/sso/callback", "matching_mode": "strict"},
{"url": "https://dns2.snarfnet.net/", "matching_mode": "strict"}
],
"signing_key": "6d48ba88-4b51-428f-903c-93334988a941",
"property_mappings": [
"f521356c-080d-4bd6-b19a-68ab159a5aef", # openid
"8395b909-8e8d-4fc1-a620-5592e10df8fe", # email
"85b89b43-92d8-425c-b9cc-8e00038cad37", # profile
"d2248142-6be2-4833-94f8-e02f5d293b02" # groups
],
"sub_mode": "hashed_user_id",
"issuer_mode": "per_provider",
"include_claims_in_id_token": True
}
prov = api_call("https://authentik.snarfnet.net/api/v3/providers/oauth2/", method="POST", payload=p25_payload)
if not prov:
# Check if exists
res = api_call("https://authentik.snarfnet.net/api/v3/providers/oauth2/?search=Technitium+DNS+2")
prov_pk = res['results'][0]['pk']
else:
prov_pk = prov['pk']
print(f"Provider created/found: PK {prov_pk}")
print("\n2. Creating Application 'Technitium DNS 2 (Spaceport)'...")
app2_payload = {
"name": "Technitium DNS 2 (Spaceport)",
"slug": "dns2",
"provider": prov_pk,
"group": "Networking & Edge",
"meta_launch_url": "https://dns2.snarfnet.net",
"meta_description": "Authoritative & Recursive DNS Server with DoH & Local Resolution (Spaceport Secondary)",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/technitium.png",
"open_in_new_tab": True
}
app = api_call("https://authentik.snarfnet.net/api/v3/core/applications/", method="POST", payload=app2_payload)
if app:
print(f"Created Application: {app.get('name')} (slug: {app.get('slug')})")
else:
api_call("https://authentik.snarfnet.net/api/v3/core/applications/dns2/", method="PATCH", payload=app2_payload)
print("Updated Application dns2")
print("\n3. Listing all DNS applications in Authentik App Library:")
final_apps = api_call("https://authentik.snarfnet.net/api/v3/core/applications/?page_size=100")
for a in sorted(final_apps.get('results', []), key=lambda x: x['name']):
if a['slug'] in ['pihole1', 'pihole2', 'dns1', 'dns2']:
print(f" [{a['group']}] {a['name']}")
print(f" Slug: {a['slug']} | URL: {a['meta_launch_url']} | Provider PK: {a['provider']}")

View File

@@ -0,0 +1,34 @@
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
token = "SnarfNetPermanentAutomationToken2026"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
req = urllib.request.Request("https://authentik.snarfnet.net/api/v3/core/applications/", headers=headers)
with urllib.request.urlopen(req, context=ctx, timeout=8) as resp:
data = json.loads(resp.read().decode('utf-8'))
print("Applications matching pihole / technitium / dns:")
for app in data.get('results', []):
name = app.get('name', '').lower()
slug = app.get('slug', '').lower()
if 'pihole' in name or 'pihole' in slug or 'technitium' in name or 'technitium' in slug or 'dns' in name or 'dns' in slug:
print(f" Name: {app.get('name')}")
print(f" Slug: {app.get('slug')}")
print(f" PK: {app.get('pk')}")
print(f" Provider: {app.get('provider')}")
print(f" Launch URL: {app.get('meta_launch_url')}")
print(f" Group: {app.get('group')}")
print(f" Icon: {app.get('meta_icon')}")
print()

View File

@@ -1,30 +1,33 @@
import paramiko
import sys
sys.stdout.reconfigure(encoding='utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
key_path = r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet"
# Check pihole config in /etc/pihole
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -i password /etc/pihole/pihole.toml 2>/dev/null || true')
print("pihole.toml password:")
print(stdout.read().decode('utf-8', errors='ignore'))
def check(host, ns, name):
print(f"\n=================== {name} ({host}) ===================")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(host, username="root", key_filename=key_path, timeout=10)
print("\n--- Pods in ns " + ns + " ---")
stdin, stdout, stderr = c.exec_command(f"k3s kubectl -n {ns} get pods -o wide")
print(stdout.read().decode('utf-8'))
print("\n--- Pihole Pod describe/logs ---")
stdin, stdout, stderr = c.exec_command(f"k3s kubectl -n {ns} describe deploy/pihole | grep -E 'Image:|Environment|Mounts|Volumes'")
print(stdout.read().decode('utf-8'))
print("\n--- Technitium Pod describe/logs ---")
stdin, stdout, stderr = c.exec_command(f"k3s kubectl -n {ns} describe deploy/technitium-dns | grep -E 'Image:|Environment|Mounts|Volumes'")
print(stdout.read().decode('utf-8'))
c.close()
# Check pihole env
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- env | grep -E "WEBPASSWORD|FTLCONF"')
print("pihole env:")
print(stdout.read().decode('utf-8', errors='ignore'))
check("140.44.4.11", "infra", "Aerodrome")
check("140.44.4.41", "infrastructure", "Spaceport")
# Let's test pihole web login via curl inside the pod or to localhost:80
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- curl -s -I http://localhost/admin/')
print("curl localhost/admin/ header:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check Technitium SSO settings via API using the valid token
token = "8d22b3ee9fe8ccbb317679afdbc86959d0ceea8261e0f0c7957d4325a7987d78"
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/get?token={token}"')
print("Technitium SSO get:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check Technitium info or settings
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/settings/get?token={token}"')
print("Technitium settings get (sample):")
print(stdout.read().decode('utf-8', errors='ignore')[:300])
ssh.close()

View File

@@ -0,0 +1,17 @@
import urllib.request
import re
url = "http://140.44.4.36:5380/"
with urllib.request.urlopen(url, timeout=5) as resp:
html = resp.read().decode('utf-8')
# Search for login modal or sso in html
lines = html.splitlines()
for i, line in enumerate(lines):
if 'modalLogin' in line or 'btnSSO' in line or 'btnModalLogin' in line or 'Single Sign-On' in line or 'sso' in line.lower():
start = max(0, i-5)
end = min(len(lines), i+15)
print(f"--- Around line {i} ---")
for j in range(start, end):
print(f"{j:4}: {lines[j]}")
print()

View File

@@ -24,3 +24,4 @@ if data:
print("Mounts:", [m.get('Source') + " -> " + m.get('Destination') for m in c_info.get('Mounts', [])])
c.close()

View File

@@ -14,3 +14,4 @@ for cid in [3, 43, 52, 53]:
print(stdout.read().decode('utf-8'))
c.close()

11
scripts/read_today_log.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 50 /var/log/technitium/dns/2026-11-06.log')
print("2026-11-06.log:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,51 @@
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)
print("1. Updating database.sqlite: removing ID 3, 51, 54...")
py_script = """import sqlite3
conn = sqlite3.connect("/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite")
cur = conn.cursor()
# Remove proxy hosts 3, 51, 54
cur.execute("DELETE FROM proxy_host WHERE id IN (3, 51, 54)")
print(f"Deleted {cur.rowcount} rows from proxy_host (IDs 3, 51, 54)")
conn.commit()
conn.close()
"""
cmd_db = "cat << 'EOF' > /tmp/remove_npm.py\n" + py_script + "\nEOF\npython3 /tmp/remove_npm.py\n"
stdin, stdout, stderr = c.exec_command(cmd_db)
print(stdout.read().decode('utf-8'))
err = stderr.read().decode('utf-8')
if err: print("DB err:", err)
print("2. Removing unused configuration files...")
remove_files_cmd = "rm -f /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/3.conf /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/51.conf /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/54.conf"
stdin, stdout, stderr = c.exec_command(remove_files_cmd)
stdout.channel.recv_exit_status()
print("Removed 3.conf, 51.conf, 54.conf")
print("3. Testing Nginx configuration syntax inside container...")
test_cmd = "docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) nginx -t"
stdin, stdout, stderr = c.exec_command(test_cmd)
test_out = stdout.read().decode('utf-8')
test_err = stderr.read().decode('utf-8')
print("nginx -t:\n", test_out, test_err)
if "successful" in test_err or "successful" in test_out:
print("4. Reloading Nginx...")
stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) nginx -s reload")
stdout.channel.recv_exit_status()
print("Nginx reloaded successfully!")
c.close()

View File

@@ -0,0 +1,29 @@
import paramiko
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Delete the pod so deployment restarts it
stdin, stdout, stderr = ssh.exec_command('k3s kubectl rollout restart deployment/technitium-dns -n dns')
print("Restart technitium:", stdout.read().decode())
time.sleep(10)
# Check pods
stdin, stdout, stderr = ssh.exec_command('k3s kubectl get pods -n dns -l app=technitium-dns')
print("Pods:", stdout.read().decode())
# Test GET /sso/login
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -v -I "http://localhost:5380/sso/login"')
print("GET /sso/login response:")
print(stderr.read().decode('utf-8', errors='ignore'))
print(stdout.read().decode('utf-8', errors='ignore'))
# Check logs for startup
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 30 /var/log/technitium/dns/2026-11-06.log 2>/dev/null || true')
print("Recent logs:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,27 @@
import paramiko
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
print("Restarting deployment/technitium-dns...")
ssh.exec_command('k3s kubectl rollout restart deployment/technitium-dns -n dns')
time.sleep(12)
# Wait for pod ready
stdin, stdout, stderr = ssh.exec_command('k3s kubectl wait --for=condition=Ready pod -l app=technitium-dns -n dns --timeout=30s')
print("Wait condition:", stdout.read().decode())
# Test GET /sso/login
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -v "http://localhost:5380/sso/login"')
print("GET /sso/login result:")
print(stderr.read().decode('utf-8', errors='ignore'))
print(stdout.read().decode('utf-8', errors='ignore'))
# Check recent logs
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- tail -n 25 /var/log/technitium/dns/2026-11-06.log 2>/dev/null || true')
print("Recent logs:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

98
scripts/run_db_update.py Normal file
View File

@@ -0,0 +1,98 @@
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)
remote_script = """import sqlite3, json, datetime
conn = sqlite3.connect("/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite")
cur = conn.cursor()
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
updates = [
(3, json.dumps(["pihole.snarfnet.net"]), "140.44.4.34", 443, "https"),
(43, json.dumps(["pihole2.snarfnet.net"]), "140.44.4.62", 443, "https"),
(51, json.dumps(["pihole3.snarfnet.net"]), "140.44.4.62", 443, "https"),
(52, json.dumps(["dns1.snarfnet.net"]), "140.44.4.34", 443, "https"),
(53, json.dumps(["dns2.snarfnet.net"]), "140.44.4.62", 443, "https"),
]
for pid, dnames, host, port, scheme in updates:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = ?, forward_port = ?, forward_scheme = ?,
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, advanced_config = "", modified_on = ?
WHERE id = ?''', (dnames, host, port, scheme, now, pid))
print(f"Updated proxy host {pid} -> {host}:{port} ({scheme})")
meta_json = json.dumps({"nginx_online": True, "nginx_err": None})
cur.execute("SELECT id FROM proxy_host WHERE id = 54")
if not cur.fetchone():
cur.execute('''INSERT INTO proxy_host (
id, created_on, modified_on, owner_user_id, is_deleted,
domain_names, forward_host, forward_port, access_list_id,
certificate_id, ssl_forced, caching_enabled, block_exploits,
advanced_config, meta, allow_websocket_upgrade, http2_support,
forward_scheme, enabled, locations, hsts_enabled, hsts_subdomains,
trust_forwarded_proto
) VALUES (
54, ?, ?, 1, 0,
?, "140.44.4.34", 443, 0,
1, 1, 0, 1,
"", ?, 1, 1,
"https", 1, "[]", 1, 1,
0
)''', (now, now, json.dumps(["technitium.snarfnet.net"]), meta_json))
print("Inserted proxy host 54: technitium.snarfnet.net -> 140.44.4.34:443")
else:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https",
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, modified_on = ?
WHERE id = 54''', (json.dumps(["technitium.snarfnet.net"]), now))
print("Updated proxy host 54: technitium.snarfnet.net")
cur.execute("SELECT id FROM proxy_host WHERE id = 55")
if not cur.fetchone():
cur.execute('''INSERT INTO proxy_host (
id, created_on, modified_on, owner_user_id, is_deleted,
domain_names, forward_host, forward_port, access_list_id,
certificate_id, ssl_forced, caching_enabled, block_exploits,
advanced_config, meta, allow_websocket_upgrade, http2_support,
forward_scheme, enabled, locations, hsts_enabled, hsts_subdomains,
trust_forwarded_proto
) VALUES (
55, ?, ?, 1, 0,
?, "140.44.4.34", 443, 0,
1, 1, 0, 1,
"", ?, 1, 1,
"https", 1, "[]", 1, 1,
0
)''', (now, now, json.dumps(["pihole1.snarfnet.net"]), meta_json))
print("Inserted proxy host 55: pihole1.snarfnet.net -> 140.44.4.34:443")
else:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https",
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, modified_on = ?
WHERE id = 55''', (json.dumps(["pihole1.snarfnet.net"]), now))
print("Updated proxy host 55: pihole1.snarfnet.net")
conn.commit()
conn.close()
"""
# Write script to /tmp/update_npm_db.py
cmd = "cat << 'EOF' > /tmp/update_npm_db.py\n" + remote_script + "\nEOF\npython3 /tmp/update_npm_db.py\n"
stdin, stdout, stderr = c.exec_command(cmd)
print("Output:\n", stdout.read().decode('utf-8'))
err = stderr.read().decode('utf-8')
if err: print("Err:\n", err)
c.close()

View File

@@ -0,0 +1,126 @@
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
token = "SnarfNetPermanentAutomationToken2026"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
def api_call(url, method="GET", payload=None):
data = json.dumps(payload).encode('utf-8') if payload else None
req = urllib.request.Request(url, headers=headers, data=data, method=method)
try:
with urllib.request.urlopen(req, context=ctx, timeout=8) as resp:
return json.loads(resp.read().decode('utf-8'))
except urllib.error.HTTPError as e:
err = e.read().decode('utf-8')
print(f"API Error {e.code} on {method} {url}: {err}")
return None
print("1. Updating OAuth2 Provider 23 (Technitium)...")
p23_payload = {
"name": "Technitium DNS SSO",
"redirect_uris": [
{"url": "https://dns1.snarfnet.net/sso/callback", "matching_mode": "strict"},
{"url": "https://dns2.snarfnet.net/sso/callback", "matching_mode": "strict"},
{"url": "https://dns1.snarfnet.net/", "matching_mode": "strict"},
{"url": "https://dns2.snarfnet.net/", "matching_mode": "strict"},
{"url": "https://dns.*\\.snarfnet\\.net.*", "matching_mode": "regex"}
]
}
api_call("https://authentik.snarfnet.net/api/v3/providers/oauth2/23/", method="PATCH", payload=p23_payload)
print("Updated Provider 23 redirect URIs.")
print("\n2. Updating Applications in Authentik App Library...")
# Target applications definition:
apps_target = [
{
"name": "Pi-hole 1 (Aerodrome)",
"slug": "pihole1",
"provider": 24,
"group": "Networking & Edge",
"meta_launch_url": "https://pihole1.snarfnet.net",
"meta_description": "Network-wide Ad & Tracker Blocking (Aerodrome Primary)",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/pi-hole.png",
"open_in_new_tab": True
},
{
"name": "Pi-hole 2 (Spaceport)",
"slug": "pihole2",
"provider": 12,
"group": "Networking & Edge",
"meta_launch_url": "https://pihole2.snarfnet.net",
"meta_description": "Network-wide Ad & Tracker Blocking (Spaceport Secondary)",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/pi-hole.png",
"open_in_new_tab": True
},
{
"name": "Technitium DNS 1 (Aerodrome)",
"slug": "dns1",
"provider": 23,
"group": "Networking & Edge",
"meta_launch_url": "https://dns1.snarfnet.net",
"meta_description": "Authoritative & Recursive DNS Server with DoH & Local Resolution (Aerodrome Primary)",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/technitium.png",
"open_in_new_tab": True
},
{
"name": "Technitium DNS 2 (Spaceport)",
"slug": "dns2",
"provider": 23,
"group": "Networking & Edge",
"meta_launch_url": "https://dns2.snarfnet.net",
"meta_description": "Authoritative & Recursive DNS Server with DoH & Local Resolution (Spaceport Secondary)",
"meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/technitium.png",
"open_in_new_tab": True
}
]
# Fetch current applications
current_apps = api_call("https://authentik.snarfnet.net/api/v3/core/applications/?page_size=100")
current_slugs = {a['slug']: a for a in current_apps.get('results', [])}
# Delete obsolete slugs if they exist (e.g., 'technitium' or old 'pihole' if we recreate as 'pihole2')
if 'technitium' in current_slugs and 'dns1' not in current_slugs:
print("Renaming app 'technitium' to 'dns1'...")
api_call("https://authentik.snarfnet.net/api/v3/core/applications/technitium/", method="PATCH", payload=apps_target[2])
elif 'technitium' in current_slugs:
print("Removing obsolete 'technitium' app...")
api_call("https://authentik.snarfnet.net/api/v3/core/applications/technitium/", method="DELETE")
if 'pihole' in current_slugs:
print("Renaming app 'pihole' to 'pihole2'...")
api_call("https://authentik.snarfnet.net/api/v3/core/applications/pihole/", method="PATCH", payload=apps_target[1])
# Refresh apps list
current_apps = api_call("https://authentik.snarfnet.net/api/v3/core/applications/?page_size=100")
current_slugs = {a['slug']: a for a in current_apps.get('results', [])}
for target in apps_target:
slug = target['slug']
if slug in current_slugs:
print(f"Updating app '{target['name']}' ({slug})...")
res = api_call(f"https://authentik.snarfnet.net/api/v3/core/applications/{slug}/", method="PATCH", payload=target)
if res: print(f" ✓ Updated {slug}")
else:
print(f"Creating app '{target['name']}' ({slug})...")
res = api_call("https://authentik.snarfnet.net/api/v3/core/applications/", method="POST", payload=target)
if res: print(f" ✓ Created {slug}")
print("\n3. Verifying final state in Authentik App Library...")
final_apps = api_call("https://authentik.snarfnet.net/api/v3/core/applications/?page_size=100")
for a in sorted(final_apps.get('results', []), key=lambda x: x['name']):
if a['slug'] in ['pihole1', 'pihole2', 'dns1', 'dns2', 'pihole', 'technitium']:
print(f" [{a['group']}] {a['name']}")
print(f" Slug: {a['slug']} | URL: {a['meta_launch_url']} | Provider PK: {a['provider']}")

View File

@@ -0,0 +1,18 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Curl authentik from inside technitium pod
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -v https://authentik.snarfnet.net/application/o/dns1/.well-known/openid-configuration')
print("Curl from technitium pod:")
print(stderr.read().decode('utf-8', errors='ignore'))
print(stdout.read().decode('utf-8', errors='ignore'))
# Check DNS resolution inside technitium pod
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- cat /etc/resolv.conf')
print("/etc/resolv.conf inside technitium pod:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -22,3 +22,4 @@ for host, ip, path in [
print(f"{host} -> {ip}: {status_line} {loc}")
c.close()

View File

@@ -23,3 +23,4 @@ for host in ['140.44.4.70', '140.44.4.71']:
except Exception as e:
print(f" Conn error on {host}: {e}")
break

View File

@@ -0,0 +1,21 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Test login to pihole web interface via curl from the host
cmd = 'curl -s -c /tmp/cookies.txt -b /tmp/cookies.txt -X POST "http://140.44.4.65/api/auth" -H "Content-Type: application/json" -d \'{"password":"snarfnetadmin"}\''
stdin, stdout, stderr = ssh.exec_command(cmd)
print("Spaceport /api/auth via 140.44.4.65:", stdout.read().decode())
cmd = 'curl -s -c /tmp/cookies.txt -b /tmp/cookies.txt "http://140.44.4.65/api/auth"'
stdin, stdout, stderr = ssh.exec_command(cmd)
print("Spaceport check session /api/auth:", stdout.read().decode())
# Check pihole FTL log for any error
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- tail -n 25 /var/log/pihole/FTL.log 2>/dev/null || true')
print("FTL.log:")
print(stdout.read().decode())
ssh.close()

View File

@@ -0,0 +1,17 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -v "http://localhost:5380/sso/login"')
print("GET /sso/login header/body:")
print(stderr.read().decode('utf-8', errors='ignore'))
print(stdout.read().decode('utf-8', errors='ignore'))
# Also check index.html from curl to see if sso button is shown
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/" | grep -C 5 "divLoginSso"')
print("divLoginSso in index.html:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,24 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- grep -n -A 25 "setFTLConfigValue()" /usr/local/bin/pihole')
print("setFTLConfigValue:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Also let's check what Technitium API call sets SSO
# In Technitium: /api/admin/sso/set
# Let's inspect /api/admin/sso/set with token
token = "8d22b3ee9fe8ccbb317679afdbc86959d0ceea8261e0f0c7957d4325a7987d78"
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/set?token={token}&ssoEnabled=true&ssoClientId=technitium-dns-oidc&ssoClientSecret=SnarfNetTechnitiumOIDC2026!&ssoMetadataAddress=https://authentik.snarfnet.net/application/o/dns1/.well-known/openid-configuration&ssoAllowSignup=true&ssoAllowSignupOnlyForMappedUsers=false"')
print("Technitium SSO set test:")
print(stdout.read().decode('utf-8', errors='ignore'))
# Check get after set
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/get?token={token}"')
print("Technitium SSO get after set:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

View File

@@ -0,0 +1,39 @@
import paramiko
import json
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Check Technitium local DNS resolution for authentik.snarfnet.net
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- dig @127.0.0.1 authentik.snarfnet.net +short')
print("Dig authentik.snarfnet.net from Technitium DNS:")
print(stdout.read().decode())
# Get fresh token
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/user/login?user=admin&pass=snarfnetadmin"')
data = json.loads(stdout.read().decode())
token = data['token']
print("Fresh token:", token)
# Now call api/admin/sso/set with ssoAuthority set as well!
url = (
f'"http://localhost:5380/api/admin/sso/set?token={token}'
'&ssoEnabled=true'
'&ssoAuthority=https://authentik.snarfnet.net/application/o/dns1/'
'&ssoClientId=technitium-dns-oidc'
'&ssoClientSecret=SnarfNetTechnitiumOIDC2026!'
'&ssoMetadataAddress=https://authentik.snarfnet.net/application/o/dns1/.well-known/openid-configuration'
'&ssoScopes=openid,profile,email'
'&ssoAllowSignup=true'
'&ssoAllowSignupOnlyForMappedUsers=false'
'&ssoGroupMap=authentik%20Admins|Administrators"'
)
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s {url}')
print("SSO set result:", stdout.read().decode())
# Check get after set
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/get?token={token}"')
print("SSO get after set:", stdout.read().decode())
ssh.close()

View File

@@ -0,0 +1,16 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -v -H "Host: dns1.snarfnet.net" -H "X-Forwarded-Proto: https" "http://localhost:5380/sso/login"')
print("SSO login with Host and X-Forwarded-Proto:")
print(stderr.read().decode('utf-8', errors='ignore'))
# Also check index.html with Host: dns1.snarfnet.net
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -H "Host: dns1.snarfnet.net" "http://localhost:5380/" | grep -C 5 "divLoginSso"')
print("divLoginSso check:")
print(stdout.read().decode('utf-8', errors='ignore'))
ssh.close()

13
scripts/test_sso_get.py Normal file
View File

@@ -0,0 +1,13 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# GET sso/login
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s -v -L "http://localhost:5380/sso/login"')
print("SSO login GET:")
print(stderr.read().decode('utf-8', errors='ignore'))
print(stdout.read().decode('utf-8', errors='ignore')[:1000])
ssh.close()

View File

@@ -0,0 +1,18 @@
import urllib.request
import json
import sys
sys.stdout.reconfigure(encoding='utf-8')
for host, label in [('140.44.4.36', 'Aerodrome (dns1)'), ('140.44.4.65', 'Spaceport (dns2)')]:
print(f"=== Testing {label} ({host}:5380) ===")
for pwd in ['snarfnetadmin', 'admin', 'password']:
url = f"http://{host}:5380/api/user/login?user=admin&pass={pwd}"
try:
with urllib.request.urlopen(url, timeout=4) as resp:
data = json.loads(resp.read().decode('utf-8'))
tok = data.get('token')
tok_str = tok[:10] if tok else None
print(f" pass={pwd}: status={data.get('status')}, msg={data.get('errorMessage')}, token={tok_str}")
except Exception as e:
print(f" pass={pwd}: error={e}")

View File

@@ -0,0 +1,38 @@
import paramiko
import json
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
# Get fresh token
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/user/login?user=admin&pass=snarfnetadmin"')
data = json.loads(stdout.read().decode())
token = data['token']
print("Fresh token:", token)
# Now call api/admin/sso/get
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/get?token={token}"')
get_res = stdout.read().decode()
print("SSO get:", get_res)
# Now call api/admin/sso/set
url = (
f'"http://localhost:5380/api/admin/sso/set?token={token}'
'&ssoEnabled=true'
'&ssoClientId=technitium-dns-oidc'
'&ssoClientSecret=SnarfNetTechnitiumOIDC2026!'
'&ssoMetadataAddress=https://authentik.snarfnet.net/application/o/dns1/.well-known/openid-configuration'
'&ssoScopes=openid,profile,email'
'&ssoAllowSignup=true'
'&ssoAllowSignupOnlyForMappedUsers=false'
'&ssoGroupMap=authentik%20Admins:Administrators"'
)
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s {url}')
print("SSO set result:", stdout.read().decode())
# Check get again
stdin, stdout, stderr = ssh.exec_command(f'k3s kubectl exec -n dns deployment/technitium-dns -- curl -s "http://localhost:5380/api/admin/sso/get?token={token}"')
print("SSO get after set:", stdout.read().decode())
ssh.close()

11
scripts/test_totp_null.py Normal file
View File

@@ -0,0 +1,11 @@
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
stdin, stdout, stderr = ssh.exec_command('k3s kubectl exec -n dns deployment/pihole -- curl -s -X POST "http://localhost/api/auth" -H "Content-Type: application/json" -d \'{"password":"snarfnetadmin","totp":null}\'')
print("Auth with totp null:")
print(stdout.read().decode())
ssh.close()

View File

@@ -0,0 +1,208 @@
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)
print("1. Creating safe backups of database and config files...")
backup_cmd = """
cp /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite.bak.dns
cp -r /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host_bak_dns
"""
stdin, stdout, stderr = c.exec_command(backup_cmd)
stdout.channel.recv_exit_status()
print("Backups created successfully.")
print("2. Updating database.sqlite...")
py_script = """
import sqlite3, json, datetime
conn = sqlite3.connect("/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite")
cur = conn.cursor()
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
updates = [
(3, json.dumps(["pihole.snarfnet.net"]), "140.44.4.34", 443, "https"),
(43, json.dumps(["pihole2.snarfnet.net"]), "140.44.4.62", 443, "https"),
(51, json.dumps(["pihole3.snarfnet.net"]), "140.44.4.62", 443, "https"),
(52, json.dumps(["dns1.snarfnet.net"]), "140.44.4.34", 443, "https"),
(53, json.dumps(["dns2.snarfnet.net"]), "140.44.4.62", 443, "https"),
]
for pid, dnames, host, port, scheme in updates:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = ?, forward_port = ?, forward_scheme = ?,
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, advanced_config = "", modified_on = ?
WHERE id = ?''', (dnames, host, port, scheme, now, pid))
print(f"Updated proxy host {pid} -> {host}:{port} ({scheme})")
cur.execute("SELECT id FROM proxy_host WHERE id = 54")
if not cur.fetchone():
cur.execute('''INSERT INTO proxy_host (
id, created_on, modified_on, owner_user_id, is_deleted,
domain_names, forward_host, forward_port, access_list_id,
certificate_id, ssl_forced, caching_enabled, block_exploits,
advanced_config, meta, allow_websocket_upgrade, http2_support,
forward_scheme, enabled, locations, hsts_enabled, hsts_subdomains,
trust_forwarded_proto
) VALUES (
54, ?, ?, 1, 0,
?, "140.44.4.34", 443, 0,
1, 1, 0, 1,
"", "{\\"nginx_online\\":true,\\"nginx_err\\":null}", 1, 1,
"https", 1, "[]", 1, 1,
0
)''', (now, now, json.dumps(["technitium.snarfnet.net"])))
print("Inserted proxy host 54: technitium.snarfnet.net -> 140.44.4.34:443")
else:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https",
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, modified_on = ?
WHERE id = 54''', (json.dumps(["technitium.snarfnet.net"]), now))
print("Updated proxy host 54: technitium.snarfnet.net")
cur.execute("SELECT id FROM proxy_host WHERE id = 55")
if not cur.fetchone():
cur.execute('''INSERT INTO proxy_host (
id, created_on, modified_on, owner_user_id, is_deleted,
domain_names, forward_host, forward_port, access_list_id,
certificate_id, ssl_forced, caching_enabled, block_exploits,
advanced_config, meta, allow_websocket_upgrade, http2_support,
forward_scheme, enabled, locations, hsts_enabled, hsts_subdomains,
trust_forwarded_proto
) VALUES (
55, ?, ?, 1, 0,
?, "140.44.4.34", 443, 0,
1, 1, 0, 1,
"", "{\\"nginx_online\\":true,\\"nginx_err\\":null}", 1, 1,
"https", 1, "[]", 1, 1,
0
)''', (now, now, json.dumps(["pihole1.snarfnet.net"])))
print("Inserted proxy host 55: pihole1.snarfnet.net -> 140.44.4.34:443")
else:
cur.execute('''UPDATE proxy_host
SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https",
certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1,
hsts_enabled = 1, hsts_subdomains = 1, modified_on = ?
WHERE id = 55''', (json.dumps(["pihole1.snarfnet.net"]), now))
print("Updated proxy host 55: pihole1.snarfnet.net")
conn.commit()
conn.close()
"""
stdin, stdout, stderr = c.exec_command(f"python3 -c '{py_script}'")
print(stdout.read().decode('utf-8'))
err = stderr.read().decode('utf-8')
if err: print("DB Update Err:", err)
print("3. Generating Nginx configuration files in proxy_host/...")
conf_template = """# ------------------------------------------------------------
# {domains}
# ------------------------------------------------------------
map $scheme $hsts_header {{
https "max-age=63072000;includeSubDomains; preload";
}}
server {{
set $forward_scheme {scheme};
set $server "{host}";
set $port {port};
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name {domains};
http2 on;
# Let's Encrypt SSL
include conf.d/include/letsencrypt-acme-challenge.conf;
include conf.d/include/ssl-cache.conf;
include conf.d/include/ssl-ciphers.conf;
ssl_certificate /etc/letsencrypt/live/npm-1/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/npm-1/privkey.pem;
# Asset Caching
include conf.d/include/assets.conf;
# Block Exploits
include conf.d/include/block-exploits.conf;
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
add_header Strict-Transport-Security $hsts_header always;
# Force SSL
set $trust_forwarded_proto "F";
include conf.d/include/force-ssl.conf;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
access_log /data/logs/proxy-host-{pid}_access.log proxy;
error_log /data/logs/proxy-host-{pid}_error.log warn;
location / {{
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
add_header Strict-Transport-Security $hsts_header always;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
# Proxy!
include conf.d/include/proxy.conf;
}}
# Custom
include /data/nginx/custom/server_proxy[.]conf;
}}
"""
configs = [
(3, "pihole.snarfnet.net", "140.44.4.34", 443, "https"),
(43, "pihole2.snarfnet.net", "140.44.4.62", 443, "https"),
(51, "pihole3.snarfnet.net", "140.44.4.62", 443, "https"),
(52, "dns1.snarfnet.net", "140.44.4.34", 443, "https"),
(53, "dns2.snarfnet.net", "140.44.4.62", 443, "https"),
(54, "technitium.snarfnet.net", "140.44.4.34", 443, "https"),
(55, "pihole1.snarfnet.net", "140.44.4.34", 443, "https"),
]
for pid, dom, host, port, scheme in configs:
cfg = conf_template.format(pid=pid, domains=dom, host=host, port=port, scheme=scheme)
remote_path = f"/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/{pid}.conf"
cmd_write = f"cat << 'EOF' > {remote_path}\n{cfg}\nEOF\n"
stdin, stdout, stderr = c.exec_command(cmd_write)
stdout.channel.recv_exit_status()
print(f"Wrote {remote_path} ({dom} -> {host}:{port})")
print("\n4. Testing Nginx configuration syntax inside container...")
test_cmd = "docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) nginx -t"
stdin, stdout, stderr = c.exec_command(test_cmd)
test_out = stdout.read().decode('utf-8')
test_err = stderr.read().decode('utf-8')
print("nginx -t stdout:", test_out)
print("nginx -t stderr:", test_err)
if "successful" in test_err or "successful" in test_out:
print("\n5. Reloading Nginx across Swarm service...")
reload_cmd = "docker service update --force nginx-proxy-manager_app"
stdin, stdout, stderr = c.exec_command(reload_cmd)
print(stdout.read().decode('utf-8'))
print("NPM service reloaded successfully!")
else:
print("\nWARNING: Nginx test failed! Please check configuration.")
c.close()

View File

@@ -0,0 +1,58 @@
import ssl
import socket
import sys
sys.stdout.reconfigure(encoding='utf-8')
active_hosts = [
'pihole1.snarfnet.net',
'pihole2.snarfnet.net',
'dns1.snarfnet.net',
'dns2.snarfnet.net',
]
removed_hosts = [
'pihole.snarfnet.net',
'pihole3.snarfnet.net',
'technitium.snarfnet.net',
]
npm_vip = '140.44.4.70'
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
print(f"=== Active Endpoints via NPM VIP ({npm_vip}:443) ===")
for host in active_hosts:
path = '/admin/' if 'pihole' in host else '/'
try:
sock = socket.create_connection((npm_vip, 443), timeout=5)
ssock = ctx.wrap_socket(sock, server_hostname=host)
req = f"GET {path} HTTP/1.1\r\nHost: {host}\r\nUser-Agent: Mozilla/5.0\r\nConnection: close\r\n\r\n"
ssock.sendall(req.encode('utf-8'))
resp = ssock.recv(4096).decode('utf-8', errors='replace')
ssock.close()
status_line = resp.splitlines()[0] if resp else "No response"
loc = [l for l in resp.splitlines() if 'location:' in l.lower()]
loc_str = loc[0][:75] + "..." if loc else ""
print(f" ✓ {host:25} -> {status_line:15} {loc_str}")
except Exception as e:
print(f" ✗ {host:25} -> ERROR: {e}")
print(f"\n=== Removed Endpoints via NPM VIP ({npm_vip}:443) ===")
for host in removed_hosts:
path = '/admin/' if 'pihole' in host else '/'
try:
sock = socket.create_connection((npm_vip, 443), timeout=5)
ssock = ctx.wrap_socket(sock, server_hostname=host)
req = f"GET {path} HTTP/1.1\r\nHost: {host}\r\nUser-Agent: Mozilla/5.0\r\nConnection: close\r\n\r\n"
ssock.sendall(req.encode('utf-8'))
resp = ssock.recv(4096).decode('utf-8', errors='replace')
ssock.close()
status_line = resp.splitlines()[0] if resp else "No response"
print(f" - {host:25} -> {status_line:15} (Default host/unrouted)")
except Exception as e:
print(f" - {host:25} -> {e}")