feat: add diagnostic and management scripts for DNS, NPM, and cluster infrastructure
This commit is contained in:
16
scripts/check_dns_pods_live.py
Normal file
16
scripts/check_dns_pods_live.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
def check_cluster(ip, name):
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect(ip, username="root", key_filename=key_path, timeout=5)
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("k3s kubectl get pods -n dns -o wide")
|
||||||
|
print(f"Pods on {name} ({ip}):\n", stdout.read().decode())
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
check_cluster("140.44.4.11", "Aerodrome")
|
||||||
|
check_cluster("140.44.4.41", "Spaceport")
|
||||||
21
scripts/check_dns_resolutions.py
Normal file
21
scripts/check_dns_resolutions.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
c = paramiko.SSHClient()
|
||||||
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
stdin, stdout, stderr = c.exec_command(cmd)
|
||||||
|
return stdout.read().decode('utf-8', errors='ignore') + stderr.read().decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
print("--- DNS check via ns1 (140.44.4.36) ---")
|
||||||
|
print(run("dig @140.44.4.36 +short pihole1.snarfnet.net pihole2.snarfnet.net dns1.snarfnet.net dns2.snarfnet.net"))
|
||||||
|
|
||||||
|
print("--- DNS check via ns2 (140.44.4.65) ---")
|
||||||
|
print(run("dig @140.44.4.65 +short pihole1.snarfnet.net pihole2.snarfnet.net dns1.snarfnet.net dns2.snarfnet.net"))
|
||||||
|
|
||||||
|
c.close()
|
||||||
18
scripts/check_node_subnets.py
Normal file
18
scripts/check_node_subnets.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect("140.44.4.11", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("ip -br a")
|
||||||
|
print("IP Aerodrome (140.44.4.11):\n", stdout.read().decode())
|
||||||
|
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("k3s kubectl get nodes -o wide")
|
||||||
|
print("Aerodrome nodes:\n", stdout.read().decode())
|
||||||
|
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
16
scripts/check_npm_docker.py
Normal file
16
scripts/check_npm_docker.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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.71', username='snarf', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=5)
|
||||||
|
|
||||||
|
stdin, stdout, stderr = ssh.exec_command('docker ps')
|
||||||
|
print("Docker ps:\n", stdout.read().decode('utf-8', errors='ignore'))
|
||||||
|
|
||||||
|
stdin, stdout, stderr = ssh.exec_command('docker exec -i $(docker ps -q -f name=app -f name=npm) find /data -name "*.sqlite* -o -name "*.conf" 2>/dev/null')
|
||||||
|
print("NPM find:\n", stdout.read().decode('utf-8', errors='ignore'))
|
||||||
|
|
||||||
|
ssh.close()
|
||||||
29
scripts/check_npm_pihole.py
Normal file
29
scripts/check_npm_pihole.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
c = paramiko.SSHClient()
|
||||||
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
|
py_query = """
|
||||||
|
import sqlite3
|
||||||
|
conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite')
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('SELECT id, domain_names, forward_host, forward_port, forward_scheme, enabled FROM proxy_host')
|
||||||
|
rows = cur.fetchall()
|
||||||
|
for r in rows:
|
||||||
|
domains = r[1]
|
||||||
|
if any(k in domains for k in ['pihole', 'dns', 'ns']):
|
||||||
|
print(r)
|
||||||
|
conn.close()
|
||||||
|
"""
|
||||||
|
|
||||||
|
stdin, stdout, stderr = c.exec_command(f'python3 -c "{py_query}"')
|
||||||
|
print("SQLite Proxy Hosts:\n", stdout.read().decode())
|
||||||
|
err = stderr.read().decode()
|
||||||
|
if err: print("Stderr:", err)
|
||||||
|
|
||||||
|
c.close()
|
||||||
27
scripts/check_npm_sni.py
Normal file
27
scripts/check_npm_sni.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
c = paramiko.SSHClient()
|
||||||
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
stdin, stdout, stderr = c.exec_command(cmd)
|
||||||
|
return stdout.read().decode('utf-8', errors='ignore') + stderr.read().decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
print("--- Curl with --resolve pihole1 ---")
|
||||||
|
print(run("curl -k -I --resolve pihole1.snarfnet.net:443:140.44.4.70 https://pihole1.snarfnet.net/"))
|
||||||
|
|
||||||
|
print("--- Curl with --resolve pihole2 ---")
|
||||||
|
print(run("curl -k -I --resolve pihole2.snarfnet.net:443:140.44.4.70 https://pihole2.snarfnet.net/"))
|
||||||
|
|
||||||
|
print("--- Curl with --resolve dns1 ---")
|
||||||
|
print(run("curl -k -I --resolve dns1.snarfnet.net:443:140.44.4.70 https://dns1.snarfnet.net/"))
|
||||||
|
|
||||||
|
print("--- Curl with --resolve dns2 ---")
|
||||||
|
print(run("curl -k -I --resolve dns2.snarfnet.net:443:140.44.4.70 https://dns2.snarfnet.net/"))
|
||||||
|
|
||||||
|
c.close()
|
||||||
33
scripts/check_pihole_connectivity.py
Normal file
33
scripts/check_pihole_connectivity.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
c = paramiko.SSHClient()
|
||||||
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
stdin, stdout, stderr = c.exec_command(cmd)
|
||||||
|
return stdout.read().decode('utf-8', errors='ignore') + stderr.read().decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
print("--- 55.conf (pihole1) ---")
|
||||||
|
print(run("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/55.conf"))
|
||||||
|
|
||||||
|
print("\n--- 43.conf (pihole2) ---")
|
||||||
|
print(run("cat /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/43.conf"))
|
||||||
|
|
||||||
|
print("\n--- Curl test from NPM node to Aerodrome Traefik (140.44.4.34) for pihole1 ---")
|
||||||
|
print(run("curl -k -I -H 'Host: pihole1.snarfnet.net' https://140.44.4.34/"))
|
||||||
|
|
||||||
|
print("\n--- Curl test from NPM node to Spaceport Traefik (140.44.4.62) for pihole2 ---")
|
||||||
|
print(run("curl -k -I -H 'Host: pihole2.snarfnet.net' https://140.44.4.62/"))
|
||||||
|
|
||||||
|
print("\n--- Curl test from NPM node to local VIP (140.44.4.70) for pihole1 ---")
|
||||||
|
print(run("curl -k -I -H 'Host: pihole1.snarfnet.net' https://140.44.4.70/"))
|
||||||
|
|
||||||
|
print("\n--- Curl test from NPM node to local VIP (140.44.4.70) for pihole2 ---")
|
||||||
|
print(run("curl -k -I -H 'Host: pihole2.snarfnet.net' https://140.44.4.70/"))
|
||||||
|
|
||||||
|
c.close()
|
||||||
22
scripts/check_pihole_npm.py
Normal file
22
scripts/check_pihole_npm.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
try:
|
||||||
|
ssh.connect('140.44.4.71', username='snarf', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=5)
|
||||||
|
except Exception:
|
||||||
|
ssh.connect('140.44.4.71', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=5)
|
||||||
|
|
||||||
|
stdin, stdout, stderr = ssh.exec_command('grep -r -i "pihole" /data/nginx/proxy_host/ 2>/dev/null')
|
||||||
|
out = stdout.read().decode('utf-8', errors='ignore')
|
||||||
|
print("Grep pihole in proxy_host:\n", out)
|
||||||
|
|
||||||
|
# Also let's inspect sqlite database in NPM
|
||||||
|
stdin, stdout, stderr = ssh.exec_command('sqlite3 /data/database.sqlite "SELECT id, domain_names, forward_host, forward_port, forward_scheme, enabled FROM proxy_host WHERE domain_names LIKE \'%pihole%\' OR domain_names LIKE \'%dns%\';"')
|
||||||
|
out = stdout.read().decode('utf-8', errors='ignore')
|
||||||
|
print("\nProxy hosts in SQLite:\n", out)
|
||||||
|
|
||||||
|
ssh.close()
|
||||||
21
scripts/check_records_dns2.py
Normal file
21
scripts/check_records_dns2.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import ssl
|
||||||
|
import json
|
||||||
|
import http.client
|
||||||
|
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
def do_req(ip, host_header, path):
|
||||||
|
conn = http.client.HTTPSConnection(ip, 443, context=ctx, timeout=8)
|
||||||
|
conn.request("GET", path, headers={"Host": host_header})
|
||||||
|
resp = conn.getresponse()
|
||||||
|
data = resp.read().decode('utf-8')
|
||||||
|
conn.close()
|
||||||
|
return json.loads(data)
|
||||||
|
|
||||||
|
token = do_req("140.44.4.62", "dns2.snarfnet.net", "/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true")['token']
|
||||||
|
zone_data = do_req("140.44.4.62", "dns2.snarfnet.net", f"/api/zones/records/get?token={token}&zone=snarfnet.net&domain=pihole1.snarfnet.net")
|
||||||
|
print("pihole1 records on dns2:\n", zone_data)
|
||||||
@@ -2,13 +2,13 @@ import paramiko
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
key_path = r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet"
|
ssh = paramiko.SSHClient()
|
||||||
client = paramiko.SSHClient()
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10)
|
||||||
|
|
||||||
client.connect("140.44.4.41", username="root", key_filename=key_path, timeout=10)
|
stdin, stdout, stderr = ssh.exec_command("k3s kubectl get nodes -o wide")
|
||||||
stdin, stdout, stderr = client.exec_command("k3s kubectl get nodes -o wide")
|
print("Spaceport nodes:\n", stdout.read().decode())
|
||||||
print("=== SPACEPORT NODES ===")
|
|
||||||
print(stdout.read().decode('utf-8'))
|
ssh.close()
|
||||||
client.close()
|
|
||||||
|
|||||||
73
scripts/fix_technitium_web_ips.py
Normal file
73
scripts/fix_technitium_web_ips.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import ssl
|
||||||
|
import json
|
||||||
|
import http.client
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
def do_req(ip, host_header, path):
|
||||||
|
conn = http.client.HTTPSConnection(ip, 443, context=ctx, timeout=8)
|
||||||
|
conn.request("GET", path, headers={"Host": host_header})
|
||||||
|
resp = conn.getresponse()
|
||||||
|
data = resp.read().decode('utf-8')
|
||||||
|
conn.close()
|
||||||
|
return json.loads(data)
|
||||||
|
|
||||||
|
def get_token(ip, host_header):
|
||||||
|
path = "/api/user/login?user=admin&pass=snarfnetadmin&includeInfo=true"
|
||||||
|
data = do_req(ip, host_header, path)
|
||||||
|
return data['token']
|
||||||
|
|
||||||
|
def update_server(ip, host_header):
|
||||||
|
print(f"\n=======================================================")
|
||||||
|
print(f"Connecting to {host_header} via {ip}...")
|
||||||
|
token = get_token(ip, host_header)
|
||||||
|
print(f"Token acquired for {host_header}")
|
||||||
|
|
||||||
|
# Records to fix
|
||||||
|
records = [
|
||||||
|
("pihole1", "140.44.4.70"),
|
||||||
|
("pihole2", "140.44.4.70"),
|
||||||
|
("dns1", "140.44.4.70"),
|
||||||
|
("dns2", "140.44.4.70"),
|
||||||
|
("ns1", "140.44.4.36"),
|
||||||
|
("ns2", "140.44.4.65"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for dom, target_ip in records:
|
||||||
|
full_dom = f"{dom}.snarfnet.net"
|
||||||
|
# 1. Delete old record
|
||||||
|
del_p = urllib.parse.urlencode({"token": token, "domain": full_dom, "zone": "snarfnet.net", "type": "A"})
|
||||||
|
try:
|
||||||
|
do_req(ip, host_header, f"/api/zones/records/delete?{del_p}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2. Add correct record
|
||||||
|
use_ptr = "true" if dom.startswith("ns") else "false"
|
||||||
|
add_p = urllib.parse.urlencode({
|
||||||
|
"token": token,
|
||||||
|
"domain": full_dom,
|
||||||
|
"zone": "snarfnet.net",
|
||||||
|
"type": "A",
|
||||||
|
"ipAddress": target_ip,
|
||||||
|
"ptr": use_ptr,
|
||||||
|
"overwrite": "true",
|
||||||
|
"ttl": 60
|
||||||
|
})
|
||||||
|
res = do_req(ip, host_header, f"/api/zones/records/add?{add_p}")
|
||||||
|
print(f" ✓ {full_dom} -> {target_ip}: {res.get('status')}")
|
||||||
|
|
||||||
|
# Update Aerodrome Technitium via Traefik 140.44.4.34
|
||||||
|
update_server("140.44.4.34", "dns1.snarfnet.net")
|
||||||
|
|
||||||
|
# Update Spaceport Technitium via Traefik 140.44.4.62
|
||||||
|
update_server("140.44.4.62", "dns2.snarfnet.net")
|
||||||
|
|
||||||
|
print("\nSuccessfully updated DNS records on both Technitium servers!")
|
||||||
19
scripts/flush_pihole_cache.py
Normal file
19
scripts/flush_pihole_cache.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
key_path = r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet'
|
||||||
|
|
||||||
|
def flush_pihole(ip, name):
|
||||||
|
print(f"Flushing Pi-hole DNS cache on {name} ({ip})...")
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect(ip, username="root", key_filename=key_path, timeout=5)
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("k3s kubectl get pods -n dns -l app=pihole -o jsonpath='{.items[0].metadata.name}'")
|
||||||
|
pod = stdout.read().decode().strip()
|
||||||
|
stdin, stdout, stderr = ssh.exec_command(f"k3s kubectl exec -n dns {pod} -- pihole restartdns")
|
||||||
|
print(stdout.read().decode().strip())
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
flush_pihole("140.44.4.11", "Aerodrome")
|
||||||
|
flush_pihole("140.44.4.41", "Spaceport")
|
||||||
148
scripts/populate_technitium_complete.py
Normal file
148
scripts/populate_technitium_complete.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
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
|
||||||
|
|
||||||
|
A_RECORDS = [
|
||||||
|
# Gateways
|
||||||
|
("gateway", "140.44.4.1"),
|
||||||
|
("udm", "140.44.4.1"),
|
||||||
|
("gateway-lan", "14.44.4.1"),
|
||||||
|
("gateway-mgmt", "140.44.44.1"),
|
||||||
|
("gateway-storage", "14.10.10.1"),
|
||||||
|
("gateway-smarthome", "14.4.4.1"),
|
||||||
|
("gateway-media", "192.168.1.1"),
|
||||||
|
|
||||||
|
# VIPs
|
||||||
|
("npm", "140.44.4.70"),
|
||||||
|
("npm-vip", "140.44.4.70"),
|
||||||
|
("*", "140.44.4.70"), # Wildcard for all reverse-proxied services
|
||||||
|
("aero-traefik", "140.44.4.34"),
|
||||||
|
("space-traefik", "140.44.4.62"),
|
||||||
|
("dns1", "140.44.4.36"),
|
||||||
|
("pihole1", "140.44.4.36"),
|
||||||
|
("dns2", "140.44.4.65"),
|
||||||
|
("pihole2", "140.44.4.65"),
|
||||||
|
|
||||||
|
# Storage
|
||||||
|
("sn-nas", "14.10.10.71"),
|
||||||
|
("snarfnas", "14.10.10.72"),
|
||||||
|
|
||||||
|
# Aerodrome Control Plane
|
||||||
|
("sninfr2lnxctl01", "140.44.4.11"),
|
||||||
|
("sninfr2lnxctl02", "140.44.4.12"),
|
||||||
|
("sninfr2lnxctl03", "140.44.4.13"),
|
||||||
|
|
||||||
|
# Aerodrome Workers
|
||||||
|
("sninfr2lnxwrk01", "140.44.4.21"),
|
||||||
|
("sninfr2lnxwrk02", "140.44.4.22"),
|
||||||
|
("sninfr2lnxwrk03", "140.44.4.23"),
|
||||||
|
("sninfr2lnxwrk04", "140.44.4.24"),
|
||||||
|
("sninfr2lnxwrk05", "140.44.4.25"),
|
||||||
|
("sninfr2lnxwrk06", "140.44.4.26"),
|
||||||
|
("sninfr2lnxwrk07", "140.44.4.27"),
|
||||||
|
|
||||||
|
# Spaceport Control Plane
|
||||||
|
("sninfr0lnxctl01", "140.44.4.41"),
|
||||||
|
("sninfr0lnxctl02", "140.44.4.42"),
|
||||||
|
("sninfr0lnxctl03", "140.44.4.43"),
|
||||||
|
("sninfr0lnxctl04", "140.44.4.44"),
|
||||||
|
|
||||||
|
# Spaceport Workers
|
||||||
|
("sninfr0lnxwrk01", "140.44.4.51"),
|
||||||
|
("sninfr0lnxwrk02", "140.44.4.52"),
|
||||||
|
("sninfr0lnxwrk03", "140.44.4.53"),
|
||||||
|
("sninfr0lnxwrk04", "140.44.4.54"),
|
||||||
|
("sninfr0lnxwrk05", "140.44.4.55"),
|
||||||
|
("sninfr0lnxwrk06", "140.44.4.56"),
|
||||||
|
|
||||||
|
# Swarm Node
|
||||||
|
("sninfr1lnxctl01", "140.44.4.71"),
|
||||||
|
|
||||||
|
# Harvester
|
||||||
|
("spacedock", "140.44.4.90"),
|
||||||
|
]
|
||||||
|
|
||||||
|
PTR_ZONES = [
|
||||||
|
"4.44.140.in-addr.arpa",
|
||||||
|
"44.44.140.in-addr.arpa",
|
||||||
|
"10.10.14.in-addr.arpa",
|
||||||
|
"4.44.14.in-addr.arpa",
|
||||||
|
"4.4.14.in-addr.arpa",
|
||||||
|
"1.168.192.in-addr.arpa",
|
||||||
|
]
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
def provision_server(host):
|
||||||
|
print(f"\n=======================================================")
|
||||||
|
print(f"Provisioning Technitium on {host}...")
|
||||||
|
token = get_token(host)
|
||||||
|
print(f"Token acquired for {host}")
|
||||||
|
|
||||||
|
# 1. Create Forward Zone snarfnet.net
|
||||||
|
create_zone_url = f"https://{host}/api/zones/create?token={token}&zone=snarfnet.net&type=Primary"
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(urllib.request.Request(create_zone_url), context=ctx, timeout=8) as resp:
|
||||||
|
print(f" ✓ Created forward zone snarfnet.net on {host}")
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
err = e.read().decode()
|
||||||
|
if "already exists" in err:
|
||||||
|
print(f" - Forward zone snarfnet.net already exists on {host}")
|
||||||
|
else:
|
||||||
|
print(f" ✗ Error creating snarfnet.net: {err}")
|
||||||
|
|
||||||
|
# 2. Create Reverse PTR Zones
|
||||||
|
for z in PTR_ZONES:
|
||||||
|
cz_url = f"https://{host}/api/zones/create?token={token}&zone={z}&type=Primary"
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(urllib.request.Request(cz_url), context=ctx, timeout=8) as resp:
|
||||||
|
print(f" ✓ Created reverse zone {z} on {host}")
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
err = e.read().decode()
|
||||||
|
if "already exists" in err:
|
||||||
|
print(f" - Reverse zone {z} already exists on {host}")
|
||||||
|
else:
|
||||||
|
print(f" ✗ Error creating reverse zone {z}: {err}")
|
||||||
|
|
||||||
|
# 3. Add A records + Auto PTR
|
||||||
|
print(f"\nAdding {len(A_RECORDS)} authoritative records on {host}...")
|
||||||
|
added = 0
|
||||||
|
for name, ip in A_RECORDS:
|
||||||
|
domain = f"{name}.snarfnet.net" if name != "@" else "snarfnet.net"
|
||||||
|
# Don't create PTR for wildcard
|
||||||
|
use_ptr = "false" if name == "*" else "true"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"token": token,
|
||||||
|
"domain": domain,
|
||||||
|
"zone": "snarfnet.net",
|
||||||
|
"type": "A",
|
||||||
|
"ipAddress": ip,
|
||||||
|
"ptr": use_ptr,
|
||||||
|
"overwrite": "true",
|
||||||
|
"ttl": 3600
|
||||||
|
}
|
||||||
|
add_url = f"https://{host}/api/zones/records/add?{urllib.parse.urlencode(params)}"
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(urllib.request.Request(add_url), context=ctx, timeout=8) as resp:
|
||||||
|
added += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ✗ Failed adding {domain} -> {ip}: {e}")
|
||||||
|
|
||||||
|
print(f" ✓ Successfully added/updated {added} A and PTR records on {host}")
|
||||||
|
|
||||||
|
provision_server("dns1.snarfnet.net")
|
||||||
|
provision_server("dns2.snarfnet.net")
|
||||||
|
print("\nDone provisioning both Technitium DNS servers!")
|
||||||
|
|
||||||
30
scripts/test_dns_record_ips.py
Normal file
30
scripts/test_dns_record_ips.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def make_query(name):
|
||||||
|
import random
|
||||||
|
txid = random.randint(1, 65535)
|
||||||
|
header = txid.to_bytes(2, 'big') + (0x0100).to_bytes(2, 'big') + (1).to_bytes(2, 'big') + b'\x00\x00\x00\x00\x00\x00'
|
||||||
|
qname = b''.join([bytes([len(p)]) + p.encode('ascii') for p in name.split('.')]) + b'\x00'
|
||||||
|
return header + qname + (1).to_bytes(2, 'big') + (1).to_bytes(2, 'big')
|
||||||
|
|
||||||
|
def query_ip(server, domain):
|
||||||
|
packet = make_query(domain)
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.settimeout(2.0)
|
||||||
|
try:
|
||||||
|
s.sendto(packet, (server, 53))
|
||||||
|
data, _ = s.recvfrom(1024)
|
||||||
|
# Parse answer IP (last 4 bytes of first A record)
|
||||||
|
if len(data) >= 16:
|
||||||
|
ip = f"{data[-4]}.{data[-3]}.{data[-2]}.{data[-1]}"
|
||||||
|
return ip
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
for server in ["140.44.4.36", "140.44.4.65"]:
|
||||||
|
for dom in ["pihole1.snarfnet.net", "pihole2.snarfnet.net", "dns1.snarfnet.net", "dns2.snarfnet.net", "ns1.snarfnet.net", "ns2.snarfnet.net"]:
|
||||||
|
ip = query_ip(server, dom)
|
||||||
|
print(f"[{server}] {dom:<25} -> {ip}")
|
||||||
62
scripts/test_dns_vips.py
Normal file
62
scripts/test_dns_vips.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
|
# Simple DNS query packet generator
|
||||||
|
def make_query(name, qtype=1): # 1=A, 12=PTR
|
||||||
|
import random
|
||||||
|
txid = random.randint(1, 65535)
|
||||||
|
flags = 0x0100 # standard query, recursion desired
|
||||||
|
qdcount = 1
|
||||||
|
header = txid.to_bytes(2, 'big') + flags.to_bytes(2, 'big') + qdcount.to_bytes(2, 'big') + b'\x00\x00\x00\x00\x00\x00'
|
||||||
|
qname = b''.join([bytes([len(p)]) + p.encode('ascii') for p in name.split('.')]) + b'\x00'
|
||||||
|
question = qname + qtype.to_bytes(2, 'big') + (1).to_bytes(2, 'big') # class IN
|
||||||
|
return header + question, txid
|
||||||
|
|
||||||
|
def query_server(server, name, qtype=1, port=53):
|
||||||
|
packet, txid = make_query(name, qtype)
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.settimeout(3.0)
|
||||||
|
try:
|
||||||
|
s.sendto(packet, (server, port))
|
||||||
|
data, _ = s.recvfrom(1024)
|
||||||
|
# Parse basic response
|
||||||
|
ancount = int.from_bytes(data[6:8], 'big')
|
||||||
|
return True, ancount
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
targets = [
|
||||||
|
("Aerodrome DNS VIP (140.44.4.36)", "140.44.4.36"),
|
||||||
|
("Spaceport DNS VIP (140.44.4.65)", "140.44.4.65"),
|
||||||
|
]
|
||||||
|
|
||||||
|
queries = [
|
||||||
|
("sninfr2lnxctl01.snarfnet.net", 1),
|
||||||
|
("sn-nas.snarfnet.net", 1),
|
||||||
|
("gateway.snarfnet.net", 1),
|
||||||
|
("app-test.snarfnet.net", 1), # Wildcard
|
||||||
|
("11.4.44.140.in-addr.arpa", 12), # Reverse PTR
|
||||||
|
("google.com", 1), # Upstream DoH recursion
|
||||||
|
]
|
||||||
|
|
||||||
|
print("=======================================================")
|
||||||
|
print(" TESTING DNS VIPS PORT 53 RESOLUTION ")
|
||||||
|
print("=======================================================")
|
||||||
|
|
||||||
|
for s_label, server in targets:
|
||||||
|
print(f"\n--- Testing {s_label} ---")
|
||||||
|
for q_name, qtype in queries:
|
||||||
|
ok, res = query_server(server, q_name, qtype)
|
||||||
|
if ok and res > 0:
|
||||||
|
print(f" ✓ {q_name:<30} -> Resolved ({res} answers)")
|
||||||
|
elif ok and res == 0:
|
||||||
|
print(f" ? {q_name:<30} -> 0 answers returned")
|
||||||
|
else:
|
||||||
|
print(f" ✗ {q_name:<30} -> Error: {res}")
|
||||||
|
|
||||||
|
print("\n=======================================================")
|
||||||
|
|
||||||
13
scripts/test_gotify.py
Normal file
13
scripts/test_gotify.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import urllib.request
|
||||||
|
import ssl
|
||||||
|
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen("https://gotify.snarfnet.net/version", context=ctx, timeout=5) as resp:
|
||||||
|
print("Gotify version:", resp.read().decode().strip())
|
||||||
|
except Exception as e:
|
||||||
|
print("Gotify error:", e)
|
||||||
|
|
||||||
@@ -33,3 +33,4 @@ try:
|
|||||||
print("Add record response:", json.loads(resp.read().decode()))
|
print("Add record response:", json.loads(resp.read().decode()))
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
print("HTTPError:", e.code, e.read().decode())
|
print("HTTPError:", e.code, e.read().decode())
|
||||||
|
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ try:
|
|||||||
print("Create zone response:", json.loads(resp.read().decode()))
|
print("Create zone response:", json.loads(resp.read().decode()))
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
print("HTTPError:", e.code, e.read().decode())
|
print("HTTPError:", e.code, e.read().decode())
|
||||||
|
|
||||||
|
|||||||
@@ -24,3 +24,4 @@ params = {
|
|||||||
url = f"https://dns1.snarfnet.net/api/zones/records/delete?{urllib.parse.urlencode(params)}"
|
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:
|
with urllib.request.urlopen(urllib.request.Request(url), context=ctx, timeout=8) as resp:
|
||||||
print("Delete record response:", json.loads(resp.read().decode()))
|
print("Delete record response:", json.loads(resp.read().decode()))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user