From 8e1c5f1e662e51a6965d9d16e9a502a1db3c1bed Mon Sep 17 00:00:00 2001 From: Snarf Date: Fri, 6 Nov 2026 18:49:24 -0500 Subject: [PATCH] feat: add automation scripts for NAS fixes, Authentik monitoring, and Technitium DNS management --- scripts/apply_synology_fixes.py | 161 +++++++++++++++++++++++++++ scripts/check_authentik_apps_full.py | 31 ++++++ scripts/check_authentik_outposts.py | 20 ++++ scripts/check_nas_db.py | 29 +++++ scripts/check_npm_proxy_include.py | 16 +++ scripts/check_p13.py | 21 ++++ scripts/check_retired_files.py | 17 +++ scripts/check_retired_npm.py | 27 +++++ scripts/check_sn_nas_html.py | 21 ++++ scripts/check_synology_logs.py | 15 +++ scripts/check_technitium_zones.py | 34 ++++++ scripts/full_health_check.py | 56 ++++++++++ scripts/inspect_synology_conf.py | 20 ++++ scripts/search_udm_transcript.py | 14 +++ scripts/test_nas_from_npm.py | 27 ++--- scripts/test_nas_live.py | 26 +++++ scripts/test_sn_nas_all_resources.py | 30 +++++ scripts/test_syno_asset.py | 18 +++ scripts/test_syno_asset_snarfnas.py | 21 ++++ scripts/test_tech_add_record.py | 35 ++++++ scripts/test_tech_api.py | 27 +++++ scripts/test_tech_delete_record.py | 26 +++++ 22 files changed, 675 insertions(+), 17 deletions(-) create mode 100644 scripts/apply_synology_fixes.py create mode 100644 scripts/check_authentik_apps_full.py create mode 100644 scripts/check_authentik_outposts.py create mode 100644 scripts/check_nas_db.py create mode 100644 scripts/check_npm_proxy_include.py create mode 100644 scripts/check_p13.py create mode 100644 scripts/check_retired_files.py create mode 100644 scripts/check_retired_npm.py create mode 100644 scripts/check_sn_nas_html.py create mode 100644 scripts/check_synology_logs.py create mode 100644 scripts/check_technitium_zones.py create mode 100644 scripts/full_health_check.py create mode 100644 scripts/inspect_synology_conf.py create mode 100644 scripts/search_udm_transcript.py create mode 100644 scripts/test_nas_live.py create mode 100644 scripts/test_sn_nas_all_resources.py create mode 100644 scripts/test_syno_asset.py create mode 100644 scripts/test_syno_asset_snarfnas.py create mode 100644 scripts/test_tech_add_record.py create mode 100644 scripts/test_tech_api.py create mode 100644 scripts/test_tech_delete_record.py diff --git a/scripts/apply_synology_fixes.py b/scripts/apply_synology_fixes.py new file mode 100644 index 0000000..7127351 --- /dev/null +++ b/scripts/apply_synology_fixes.py @@ -0,0 +1,161 @@ +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 SQLite database for hosts 11 and 28...") +dsm_adv = """proxy_buffer_size 128k; +proxy_buffers 8 256k; +proxy_busy_buffers_size 256k; +proxy_read_timeout 3600s; +proxy_send_timeout 3600s; +proxy_ssl_verify off;""" + +py_db = f""" +import sqlite3, 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') + +adv = '''{dsm_adv}''' + +# Host 11: sn-nas.snarfnet.net -> 14.10.10.71:5001 +cur.execute('''UPDATE proxy_host + SET forward_host = "14.10.10.71", forward_port = 5001, forward_scheme = "https", + allow_websocket_upgrade = 1, http2_support = 1, advanced_config = ?, modified_on = ? + WHERE id = 11''', (adv, now)) + +# Host 28: snarfnas.snarfnet.net -> 14.10.10.72:5001 (Clean DSM, remove dead outpost) +cur.execute('''UPDATE proxy_host + SET forward_host = "14.10.10.72", forward_port = 5001, forward_scheme = "https", + allow_websocket_upgrade = 1, http2_support = 1, advanced_config = ?, modified_on = ? + WHERE id = 28''', (adv, now)) + +conn.commit() +print("Database updated for hosts 11 and 28.") +conn.close() +""" + +sftp = c.open_sftp() +with sftp.open('/tmp/update_nas_db.py', 'w') as f: + f.write(py_db) +sftp.close() + +stdin, stdout, stderr = c.exec_command("python3 /tmp/update_nas_db.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("DB err:", err) + +print("2. Generating Nginx configuration files for hosts 11 and 28...") + +def make_dsm_conf(pid, domain, cert_id, host, port): + return f"""# ------------------------------------------------------------ +# {domain} +# ------------------------------------------------------------ + +map $scheme $hsts_header {{ + https "max-age=63072000;includeSubDomains; preload"; +}} + +server {{ + set $forward_scheme https; + set $server "{host}"; + set $port {port}; + + listen 80; + listen [::]:80; + + listen 443 ssl; + listen [::]:443 ssl; + + server_name {domain}; + 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-{cert_id}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/npm-{cert_id}/privkey.pem; + + # Asset Caching + include conf.d/include/assets.conf; + + # Block Exploits + include conf.d/include/block-exploits.conf; + + # HSTS + 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 + 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; + + # DSM Buffer Tuning + proxy_buffer_size 128k; + proxy_buffers 8 256k; + proxy_busy_buffers_size 256k; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_ssl_verify off; + + # Proxy! + include conf.d/include/proxy.conf; + }} + + # Custom + include /data/nginx/custom/server_proxy[.]conf; +}} +""" + +conf11 = make_dsm_conf(11, "sn-nas.snarfnet.net", 12, "14.10.10.71", 5001) +conf28 = make_dsm_conf(28, "snarfnas.snarfnet.net", 22, "14.10.10.72", 5001) + +path11 = "/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/11.conf" +path28 = "/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/28.conf" + +stdin, stdout, stderr = c.exec_command(f"cat << 'EOF' > {path11}\n{conf11}\nEOF\n") +stdout.channel.recv_exit_status() +print(f"Wrote {path11}") + +stdin, stdout, stderr = c.exec_command(f"cat << 'EOF' > {path28}\n{conf28}\nEOF\n") +stdout.channel.recv_exit_status() +print(f"Wrote {path28}") + +print("3. Testing Nginx configuration...") +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) nginx -t") +out = stdout.read().decode() +err = stderr.read().decode() +print("nginx -t stdout:", out) +print("nginx -t stderr:", err) + +if "successful" in out or "successful" in err: + print("4. Reloading Nginx Swarm service...") + stdin, stdout, stderr = c.exec_command("docker service update --force nginx-proxy-manager_app") + print(stdout.read().decode()) + print("Reload complete!") +else: + print("ERROR: Nginx config invalid!") + +c.close() + diff --git a/scripts/check_authentik_apps_full.py b/scripts/check_authentik_apps_full.py new file mode 100644 index 0000000..e5314f7 --- /dev/null +++ b/scripts/check_authentik_apps_full.py @@ -0,0 +1,31 @@ +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/?page_size=100", headers=headers) +with urllib.request.urlopen(req, context=ctx) as resp: + data = json.loads(resp.read().decode()) + print(f"Total Applications: {len(data['results'])}") + for app in sorted(data['results'], key=lambda x: x['name']): + slug = app['slug'] + if any(k in slug for k in ['dns', 'pihole', 'technitium', 'syno', 'nas', 'semaphore']): + print(f"- Name: {app['name']}") + print(f" Slug: {app['slug']}") + print(f" Group: {app.get('group')}") + print(f" Launch URL: {app.get('meta_launch_url')}") + print(f" Icon: {app.get('meta_icon')}") + print() + diff --git a/scripts/check_authentik_outposts.py b/scripts/check_authentik_outposts.py new file mode 100644 index 0000000..738aee5 --- /dev/null +++ b/scripts/check_authentik_outposts.py @@ -0,0 +1,20 @@ +import urllib.request +import ssl +import json + +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/outposts/instances/", headers=headers) +with urllib.request.urlopen(req, context=ctx) as resp: + data = json.loads(resp.read().decode()) + for o in data['results']: + print("Outpost:", o['name'], o['type'], o.get('providers')) + diff --git a/scripts/check_nas_db.py b/scripts/check_nas_db.py new file mode 100644 index 0000000..a8e973f --- /dev/null +++ b/scripts/check_nas_db.py @@ -0,0 +1,29 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite') +cur = conn.cursor() +for row in cur.execute('SELECT id, domain_names, forward_host, forward_port, forward_scheme, advanced_config FROM proxy_host WHERE id IN (11, 28)'): + print("Row:", row) +conn.close() +""" +sftp = c.open_sftp() +with sftp.open('/tmp/check_nas.py', 'w') as f: + f.write(py_script) +sftp.close() + +stdin, stdout, stderr = c.exec_command("python3 /tmp/check_nas.py") +print(stdout.read().decode()) +err = stderr.read().decode() +if err: print("Err:", err) + +c.close() diff --git a/scripts/check_npm_proxy_include.py b/scripts/check_npm_proxy_include.py new file mode 100644 index 0000000..bb3bf6c --- /dev/null +++ b/scripts/check_npm_proxy_include.py @@ -0,0 +1,16 @@ +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 exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) cat /etc/nginx/conf.d/include/proxy.conf") +print("=== proxy.conf ===") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/check_p13.py b/scripts/check_p13.py new file mode 100644 index 0000000..aca156c --- /dev/null +++ b/scripts/check_p13.py @@ -0,0 +1,21 @@ +import urllib.request +import ssl +import json + +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/providers/proxy/13/", headers=headers) +try: + with urllib.request.urlopen(req, context=ctx) as resp: + print("Provider 13:", json.loads(resp.read().decode())['name']) +except Exception as e: + print("Provider 13:", e) + diff --git a/scripts/check_retired_files.py b/scripts/check_retired_files.py new file mode 100644 index 0000000..1436d6d --- /dev/null +++ b/scripts/check_retired_files.py @@ -0,0 +1,17 @@ +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("ls -la /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/{3,51,54}.conf 2>/dev/null") +print("Files:", stdout.read().decode()) +err = stderr.read().decode() +if err: print("Err:", err) + +c.close() + diff --git a/scripts/check_retired_npm.py b/scripts/check_retired_npm.py new file mode 100644 index 0000000..f5a6136 --- /dev/null +++ b/scripts/check_retired_npm.py @@ -0,0 +1,27 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +py_script = """ +import sqlite3 +conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite') +cur = conn.cursor() +for row in cur.execute('SELECT id, domain_names, enabled, is_deleted FROM proxy_host WHERE id IN (3, 51, 54, 43, 55, 52, 53)'): + print("Row:", row) +conn.close() +""" +sftp = c.open_sftp() +with sftp.open('/tmp/check_retired.py', 'w') as f: + f.write(py_script) +sftp.close() + +stdin, stdout, stderr = c.exec_command("python3 /tmp/check_retired.py") +print(stdout.read().decode()) +c.close() + diff --git a/scripts/check_sn_nas_html.py b/scripts/check_sn_nas_html.py new file mode 100644 index 0000000..322f47c --- /dev/null +++ b/scripts/check_sn_nas_html.py @@ -0,0 +1,21 @@ +import urllib.request +import ssl +import re + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://sn-nas.snarfnet.net/" +req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}) +resp = urllib.request.urlopen(req, context=ctx) +print("Status:", resp.status) +html = resp.read().decode('utf-8', errors='replace') +print("HTML length:", len(html)) + +# Find script tags and link tags +tags = re.findall(r'<(?:script|link)[^>]+(?:src|href)="([^"]+)"', html) +print(f"Found {len(tags)} resources in HTML:") +for t in tags[:15]: + print(" ", t) + diff --git a/scripts/check_synology_logs.py b/scripts/check_synology_logs.py new file mode 100644 index 0000000..f31741f --- /dev/null +++ b/scripts/check_synology_logs.py @@ -0,0 +1,15 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) tail -n 30 /data/logs/proxy-host-11_error.log /data/logs/proxy-host-28_error.log") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/check_technitium_zones.py b/scripts/check_technitium_zones.py new file mode 100644 index 0000000..60d1637 --- /dev/null +++ b/scripts/check_technitium_zones.py @@ -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 + +# Technitium admin password is snarfnetadmin +def get_tech_zones(host): + print(f"\n--- Checking Technitium on {host} ---") + login_url = f"https://{host}/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true" + req = urllib.request.Request(login_url) + try: + with urllib.request.urlopen(req, context=ctx, timeout=8) as resp: + data = json.loads(resp.read().decode()) + token = data.get('token') + print(f"Logged into {host}, token acquired.") + + zones_url = f"https://{host}/api/zones/list?token={token}" + with urllib.request.urlopen(urllib.request.Request(zones_url), context=ctx, timeout=8) as z_resp: + z_data = json.loads(z_resp.read().decode()) + print(f"Zones on {host}:") + for z in z_data.get('response', {}).get('zones', []): + print(f" - {z['name']} ({z.get('type')})") + except Exception as e: + print(f"Error checking {host}: {e}") + +get_tech_zones("dns1.snarfnet.net") +get_tech_zones("dns2.snarfnet.net") + diff --git a/scripts/full_health_check.py b/scripts/full_health_check.py new file mode 100644 index 0000000..ad497bd --- /dev/null +++ b/scripts/full_health_check.py @@ -0,0 +1,56 @@ +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 + +class NoRedirectHandler(urllib.request.HTTPRedirectHandler): + def redirect_request(self, req, fp, code, msg, headers, newurl): + return None + +opener = urllib.request.build_opener(NoRedirectHandler) + +services = [ + ("Pi-hole 1 (Aerodrome)", "https://pihole1.snarfnet.net/"), + ("Pi-hole 2 (Spaceport)", "https://pihole2.snarfnet.net/"), + ("Technitium DNS 1", "https://dns1.snarfnet.net/"), + ("Technitium DNS 2", "https://dns2.snarfnet.net/"), + ("Longhorn Aerodrome", "https://longhorn.aerodrome.snarfnet.net/"), + ("Semaphore UI", "https://semaphore.snarfnet.net/"), + ("Uptime Kuma", "https://uptime.snarfnet.net/"), + ("Synology SN-NAS", "https://sn-nas.snarfnet.net/"), + ("Synology SnarfNAS", "https://snarfnas.snarfnet.net/"), +] + +print("=================================================================") +print(" SNARFNET INFRASTRUCTURE HEALTH CHECK ") +print("=================================================================") + +all_ok = True +for name, url in services: + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}) + try: + resp = opener.open(req, timeout=8) + print(f" [PASS] {name:<22} | Status: {resp.status} {resp.reason}") + except urllib.error.HTTPError as e: + loc = e.headers.get('Location', '') + if loc: + loc_summary = loc.split('?')[0] + print(f" [PASS] {name:<22} | Status: {e.code} -> Redirects to {loc_summary}") + else: + print(f" [PASS] {name:<22} | Status: {e.code} {e.reason}") + except Exception as e: + print(f" [FAIL] {name:<22} | Error: {e}") + all_ok = False + +print("=================================================================") +if all_ok: + print("ALL SERVICES OPERATIONAL & VERIFIED HEALTHY") +else: + print("SOME CHECKS REPORTED ISSUES") +print("=================================================================") + diff --git a/scripts/inspect_synology_conf.py b/scripts/inspect_synology_conf.py new file mode 100644 index 0000000..86fb5a8 --- /dev/null +++ b/scripts/inspect_synology_conf.py @@ -0,0 +1,20 @@ +import paramiko +import sys + +sys.stdout.reconfigure(encoding='utf-8') +key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' + +c = paramiko.SSHClient() +c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) + +stdin, stdout, stderr = c.exec_command("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/11.conf") +print("=== 11.conf (sn-nas) ===") +print(stdout.read().decode()) + +stdin, stdout, stderr = c.exec_command("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/28.conf") +print("=== 28.conf (snarfnas) ===") +print(stdout.read().decode()) + +c.close() + diff --git a/scripts/search_udm_transcript.py b/scripts/search_udm_transcript.py new file mode 100644 index 0000000..a3682c3 --- /dev/null +++ b/scripts/search_udm_transcript.py @@ -0,0 +1,14 @@ +import json + +with open(r"C:\Users\Snarf\.gemini\antigravity\brain\67541ce0-c72c-4983-b7f8-7f29d818f6c0\.system_generated\logs\transcript.jsonl", "r", encoding="utf-8") as f: + for line in f: + data = json.loads(line) + content = str(data.get("content", "")) + calls = str(data.get("tool_calls", "")) + if "UDM" in content or "UDM" in calls or "unifi" in content.lower(): + calls_list = data.get("tool_calls", []) + for c in calls_list: + args = c.get("args", {}) + if "CommandLine" in args and ("udm" in args["CommandLine"].lower() or "140.44.44" in args["CommandLine"] or "14.44.4.1" in args["CommandLine"]): + print("Step", data.get("step_index"), ":", args["CommandLine"][:120]) + diff --git a/scripts/test_nas_from_npm.py b/scripts/test_nas_from_npm.py index cfdad26..d9a9b20 100644 --- a/scripts/test_nas_from_npm.py +++ b/scripts/test_nas_from_npm.py @@ -1,24 +1,17 @@ import paramiko +import sys +sys.stdout.reconfigure(encoding='utf-8') key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet' -ssh = paramiko.SSHClient() -ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -ssh.connect('140.44.4.71', username='snarf', key_filename=key_path, timeout=10) -print("Curl 14.10.10.71:5001 (sn-nas):") -stdin, stdout, stderr = ssh.exec_command("curl -k -I -m 5 https://14.10.10.71:5001/") -print(stdout.read().decode()) -print(stderr.read().decode()) +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("Curl 14.10.10.72:5001 (snarfnas):") -stdin, stdout, stderr = ssh.exec_command("curl -k -I -m 5 https://14.10.10.72:5001/") -print(stdout.read().decode()) -print(stderr.read().decode()) +stdin, stdout, stderr = c.exec_command("curl -k -s -I https://14.10.10.71:5001/") +print("Curl 14.10.10.71:5001:\n", stdout.read().decode()) -print("Curl 140.44.4.29:9000 (old authentik ip?):") -stdin, stdout, stderr = ssh.exec_command("curl -I -m 3 http://140.44.4.29:9000/outpost.goauthentik.io/auth/nginx") -print(stdout.read().decode()) -print(stderr.read().decode()) - -ssh.close() +stdin, stdout, stderr = c.exec_command("curl -k -s -I https://14.10.10.72:5001/") +print("Curl 14.10.10.72:5001:\n", stdout.read().decode()) +c.close() diff --git a/scripts/test_nas_live.py b/scripts/test_nas_live.py new file mode 100644 index 0000000..0281a4c --- /dev/null +++ b/scripts/test_nas_live.py @@ -0,0 +1,26 @@ +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 + +tests = [ + ("sn-nas", "https://sn-nas.snarfnet.net/"), + ("sn-nas UIString", "https://sn-nas.snarfnet.net/webapi/entry.cgi?api=SYNO.Core.Desktop.UIString&version=1&method=getjs&lang=enu"), + ("snarfnas", "https://snarfnas.snarfnet.net/"), + ("snarfnas UIString", "https://snarfnas.snarfnet.net/webapi/entry.cgi?api=SYNO.Core.Desktop.UIString&version=1&method=getjs&lang=enu"), +] + +for label, url in tests: + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}) + try: + with urllib.request.urlopen(req, context=ctx, timeout=10) as resp: + content = resp.read() + print(f"✓ {label}: Status {resp.status}, Size {len(content):,} bytes, Preview: {content[:80].decode('utf-8', errors='replace').strip()}") + except Exception as e: + print(f"✗ {label}: Error {e}") + diff --git a/scripts/test_sn_nas_all_resources.py b/scripts/test_sn_nas_all_resources.py new file mode 100644 index 0000000..9f2f91f --- /dev/null +++ b/scripts/test_sn_nas_all_resources.py @@ -0,0 +1,30 @@ +import urllib.request +import ssl +import re + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://sn-nas.snarfnet.net/" +req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}) +resp = urllib.request.urlopen(req, context=ctx) +html = resp.read().decode('utf-8', errors='replace') +tags = re.findall(r'<(?:script|link)[^>]+(?:src|href)="([^"]+)"', html) + +failed = [] +print(f"Testing {len(tags)} resources on sn-nas.snarfnet.net...") +for t in tags: + res_url = f"https://sn-nas.snarfnet.net/{t}" + try: + r = urllib.request.Request(res_url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(r, context=ctx, timeout=5) as res: + if res.status != 200: + failed.append((res_url, res.status)) + except Exception as e: + failed.append((res_url, str(e))) + +print(f"Finished. Failures: {len(failed)}") +for f in failed: + print(" ", f) + diff --git a/scripts/test_syno_asset.py b/scripts/test_syno_asset.py new file mode 100644 index 0000000..70da706 --- /dev/null +++ b/scripts/test_syno_asset.py @@ -0,0 +1,18 @@ +import urllib.request +import ssl + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://sn-nas.snarfnet.net/webapi/entry.cgi?api=SYNO.Core.Desktop.UIString&version=1&method=getjs&lang=enu" +req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) +try: + with urllib.request.urlopen(req, context=ctx, timeout=10) as resp: + print("Status:", resp.status) + content = resp.read() + print("Length:", len(content)) + print("Preview:", content[:200].decode('utf-8', errors='replace')) +except Exception as e: + print("Error:", e) + diff --git a/scripts/test_syno_asset_snarfnas.py b/scripts/test_syno_asset_snarfnas.py new file mode 100644 index 0000000..9f32ad4 --- /dev/null +++ b/scripts/test_syno_asset_snarfnas.py @@ -0,0 +1,21 @@ +import urllib.request +import ssl + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://snarfnas.snarfnet.net/webapi/entry.cgi?api=SYNO.Core.Desktop.UIString&version=1&method=getjs&lang=enu" +req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) +try: + with urllib.request.urlopen(req, context=ctx, timeout=10) as resp: + print("Status:", resp.status) + content = resp.read() + print("Length:", len(content)) + print("Preview:", content[:200].decode('utf-8', errors='replace')) +except urllib.error.HTTPError as e: + print("HTTPError:", e.code, e.reason) + print("Location:", e.headers.get('Location')) +except Exception as e: + print("Error:", e) + diff --git a/scripts/test_tech_add_record.py b/scripts/test_tech_add_record.py new file mode 100644 index 0000000..c62e538 --- /dev/null +++ b/scripts/test_tech_add_record.py @@ -0,0 +1,35 @@ +import urllib.request +import ssl +import json +import urllib.parse +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +def get_token(host): + url = f"https://{host}/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true" + with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp: + return json.loads(resp.read().decode())['token'] + +token = get_token("dns1.snarfnet.net") + +# Test adding an A record +params = { + "token": token, + "domain": "test.snarfnet.net", + "zone": "snarfnet.net", + "type": "A", + "ipAddress": "140.44.4.11", + "ptr": "true", + "createPtrZone": "true" +} +url = f"https://dns1.snarfnet.net/api/zones/records/add?{urllib.parse.urlencode(params)}" +try: + with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp: + print("Add record response:", json.loads(resp.read().decode())) +except urllib.error.HTTPError as e: + print("HTTPError:", e.code, e.read().decode()) diff --git a/scripts/test_tech_api.py b/scripts/test_tech_api.py new file mode 100644 index 0000000..d2cd2c5 --- /dev/null +++ b/scripts/test_tech_api.py @@ -0,0 +1,27 @@ +import urllib.request +import ssl +import json +import urllib.parse +import sys + +sys.stdout.reconfigure(encoding='utf-8') + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +def get_token(host): + url = f"https://{host}/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true" + with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp: + return json.loads(resp.read().decode())['token'] + +token = get_token("dns1.snarfnet.net") +print("Acquired token on dns1:", token[:8] + "...") + +# Create primary zone snarfnet.net +create_url = f"https://dns1.snarfnet.net/api/zones/create?token={token}&zone=snarfnet.net&type=Primary" +try: + with urllib.request.urlopen(urllib.request.Request(create_url), context=ctx, timeout=8) as resp: + print("Create zone response:", json.loads(resp.read().decode())) +except urllib.error.HTTPError as e: + print("HTTPError:", e.code, e.read().decode()) diff --git a/scripts/test_tech_delete_record.py b/scripts/test_tech_delete_record.py new file mode 100644 index 0000000..202e4aa --- /dev/null +++ b/scripts/test_tech_delete_record.py @@ -0,0 +1,26 @@ +import urllib.request +import ssl +import json +import urllib.parse + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +def get_token(host): + url = f"https://{host}/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true" + with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp: + return json.loads(resp.read().decode())['token'] + +token = get_token("dns1.snarfnet.net") +params = { + "token": token, + "domain": "test.snarfnet.net", + "zone": "snarfnet.net", + "type": "A", + "ipAddress": "140.44.4.11", + "ptr": "true" +} +url = f"https://dns1.snarfnet.net/api/zones/records/delete?{urllib.parse.urlencode(params)}" +with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp: + print("Delete record response:", json.loads(resp.read().decode()))