Add self signed cert support to the test dashboard.

This commit is contained in:
Kalzu Rekku committed 2026-10-27 12:32:20 +02:00
1 parent ff16548585
commit be278b3c7c
3 files changed
+156 -9

No files matched your search

+123 -3
View File
@@ -1,18 +1,26 @@
#!/usr/bin/env python3
"""
Lightweight test server and transparent proxy for the Records API.
Serves index.html on http://127.0.0.1:8080 and adds CORS headers to Axum requests.
Serves index.html on http://127.0.0.1:8080 and transparently proxies
requests to cluster instances, bypassing self-signed TLS certificate checks.
"""
from http.server import SimpleHTTPRequestHandler, HTTPServer
import urllib.request
import urllib.error
import urllib.parse
import ssl
import json
import os
PORT = 8080
PORT = int(os.environ.get("DASHBOARD_PORT", 8080))
# Create an SSL context that ignores self-signed certificate errors
SSL_CTX = ssl._create_unverified_context()
class ProxyHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, HEAD')
self.send_header('Access-Control-Allow-Headers', '*')
super().end_headers()
@@ -20,8 +28,120 @@ class ProxyHandler(SimpleHTTPRequestHandler):
self.send_response(204)
self.end_headers()
def do_GET(self):
if self.path.startswith('/proxy'):
self.handle_proxy('GET')
else:
super().do_GET()
def do_HEAD(self):
if self.path.startswith('/proxy'):
self.handle_proxy('HEAD')
else:
super().do_HEAD()
def do_POST(self):
if self.path.startswith('/proxy'):
self.handle_proxy('POST')
else:
self.send_error(405, "Method Not Allowed")
def do_PUT(self):
if self.path.startswith('/proxy'):
self.handle_proxy('PUT')
else:
self.send_error(405, "Method Not Allowed")
def do_DELETE(self):
if self.path.startswith('/proxy'):
self.handle_proxy('DELETE')
else:
self.send_error(405, "Method Not Allowed")
def extract_target_url(self):
parsed = urllib.parse.urlparse(self.path)
if parsed.path.rstrip('/') == '/proxy':
if '?url=' in self.path:
return urllib.parse.unquote(self.path.split('?url=', 1)[1])
qs = urllib.parse.parse_qs(parsed.query)
return qs.get('url', [None])[0]
elif parsed.path.startswith('/proxy/'):
rest = self.path[len('/proxy/'):]
if rest.startswith('http://') or rest.startswith('https://'):
return rest
parts = rest.split('/', 1)
host_or_port = parts[0]
subpath = '/' + parts[1] if len(parts) > 1 else ''
if parsed.query:
subpath += '?' + parsed.query
if host_or_port.isdigit():
return f"https://127.0.0.1:{host_or_port}{subpath}"
return f"https://{host_or_port}{subpath}"
return None
def handle_proxy(self, method):
target_url = self.extract_target_url()
if not target_url:
self.send_response(400)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'error': 'Missing or invalid target URL'}).encode())
return
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length) if content_length > 0 else None
# Filter hop-by-hop headers and compression to ensure clean raw data transfer
req_headers = {}
for k, v in self.headers.items():
if k.lower() in ('host', 'content-length', 'connection', 'transfer-encoding', 'accept-encoding'):
continue
req_headers[k] = v
req = urllib.request.Request(target_url, data=body, headers=req_headers, method=method)
try:
with urllib.request.urlopen(req, context=SSL_CTX, timeout=15) as resp:
status_code = resp.status
resp_headers = resp.getheaders()
resp_body = resp.read() if method != 'HEAD' else b''
except urllib.error.HTTPError as e:
status_code = e.code
resp_headers = e.headers.items()
resp_body = e.read() if method != 'HEAD' else b''
except urllib.error.URLError as e:
self.send_response(502)
self.send_header('Content-Type', 'application/json')
self.end_headers()
err_msg = json.dumps({'error': f'Failed to connect to backend: {e.reason}'}).encode()
self.wfile.write(err_msg)
return
except Exception as e:
self.send_response(500)
self.send_header('Content-Type', 'application/json')
self.end_headers()
err_msg = json.dumps({'error': f'Proxy exception: {str(e)}'}).encode()
self.wfile.write(err_msg)
return
self.send_response(status_code)
for k, v in resp_headers:
if k.lower() in ('transfer-encoding', 'connection', 'content-length',
'access-control-allow-origin', 'access-control-allow-methods',
'access-control-allow-headers'):
continue
self.send_header(k, v)
if method != 'HEAD':
self.send_header('Content-Length', str(len(resp_body)))
self.end_headers()
if method != 'HEAD' and resp_body:
self.wfile.write(resp_body)
if __name__ == '__main__':
print(f"🚀 Dashboard running at http://127.0.0.1:{PORT}")
print(f"🔒 TLS unverified proxy active at /proxy?url=<target>")
print("Press Ctrl+C to stop.")
server = HTTPServer(('127.0.0.1', PORT), ProxyHandler)
try:
+31 -2
View File
@@ -176,16 +176,21 @@
<div style="display:flex; gap: 20px; align-items:center; flex-wrap:wrap; font-size:0.85rem;">
<div style="flex:1; min-width:240px;">
<label>Primary Base URL:</label>
<input type="text" id="primaryUrl" value="http://127.0.0.1:3000" style="margin-bottom:0;" />
<input type="text" id="primaryUrl" value="https://127.0.0.1:3000" style="margin-bottom:0;" />
</div>
<div style="flex:1; min-width:240px;">
<label>Replica Base URL:</label>
<input type="text" id="replicaUrl" value="http://127.0.0.1:3001" style="margin-bottom:0;" />
<input type="text" id="replicaUrl" value="https://127.0.0.1:3001" style="margin-bottom:0;" />
</div>
<div style="flex:1; min-width:180px;">
<label>Replication Bearer Token:</label>
<input type="password" id="replToken" value="change-me" style="margin-bottom:0;" />
</div>
<div style="min-width:220px; display:flex; align-items:flex-end;">
<label style="font-size:0.8rem; display:flex; align-items:center; gap:6px; cursor:pointer; padding-bottom:6px; color:var(--success);">
<input type="checkbox" id="trustAllCerts" checked style="width:auto; margin:0;" /> 🛡️ Trust Self-Signed Certs (Proxy)
</label>
</div>
</div>
</div>
@@ -286,6 +291,30 @@
</div>
<script>
// Transparently proxy external/HTTPS instance calls via dashboard proxy when self-signed cert trust is active
const nativeFetch = window.fetch;
window.fetch = function(input, init) {
const trustAll = document.getElementById('trustAllCerts')?.checked ?? true;
if (trustAll) {
const urlStr = typeof input === 'string' ? input : (input && input.url ? input.url : null);
if (urlStr) {
try {
const parsed = new URL(urlStr, window.location.href);
if (parsed.origin !== window.location.origin) {
const proxyBase = (window.location.protocol === 'file:') ? 'http://127.0.0.1:8080' : '';
const proxiedUrl = `${proxyBase}/proxy?url=${encodeURIComponent(urlStr)}`;
if (typeof input === 'string') {
input = proxiedUrl;
} else {
input = new Request(proxiedUrl, input);
}
}
} catch (_) {}
}
}
return nativeFetch(input, init);
};
let primaryHeadSeq = 0;
let replicaHeadSeq = 0;
Regular → Executable
+2 -4
View File
@@ -51,15 +51,13 @@ for port in "$PRIMARY_PORT" "$REPLICA_PORT"; do
fi
done
if [[ ! -x "$BIN" ]]; then
if [[ -f "$ROOT_DIR/Cargo.toml" ]]; then
echo "Binary not found at $BIN. Building debug binary with cargo..."
echo "Ensuring debug binary is up to date with cargo..."
( cd "$ROOT_DIR" && cargo build )
else
elif [[ ! -x "$BIN" ]]; then
echo "Error: Binary not found or not executable: $BIN" >&2
exit 1
fi
fi
# --- Workspace & Schema Setup ---
rm -rf "$WORK"