From 7f52bd8a324c689dbb8328229516aec4bc9d5e60 Mon Sep 17 00:00:00 2001 From: Razvan Dimescu Date: Wed, 8 Apr 2026 01:12:16 +0300 Subject: [PATCH] =?UTF-8?q?test:=20Suite=206=20=E2=80=94=20proxy=20+=20DoT?= =?UTF-8?q?=20coexistence,=20NUMA=5FDATA=5FDIR=20override?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds integration test coverage for the realistic production shape where both the HTTPS proxy and DoT are enabled simultaneously. This was previously untested — every existing suite had either one or the other, so the interaction path was implicit. What Suite 6 verifies: - Both listeners bind without panic - DoT still resolves queries with the proxy enabled - Proxy HTTPS handshake still works with DoT enabled - Both certs validate against the same shared CA To run non-root, adds a NUMA_DATA_DIR env var override to data_dir() that lets callers point the CA/cert storage at any writable path. Useful beyond tests: containerized deployments, CI runners, dev testing without sudo. The fallback is the existing platform-specific path (unix: /usr/local/var/numa, windows: %PROGRAMDATA%\numa). Suite 6 sets NUMA_DATA_DIR=/tmp/numa-integration-data before starting numa, then trusts the generated CA at $NUMA_DATA_DIR/ca.pem for both kdig (DoT query) and openssl s_client (HTTPS proxy handshake) verification. All 6 suites, 32 checks, run non-root and pass locally. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib.rs | 5 ++ tests/integration.sh | 111 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 36017fe..05d18a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,9 +67,14 @@ fn config_dir_unix() -> std::path::PathBuf { } /// System-wide data directory for TLS certs. +/// Override with `NUMA_DATA_DIR` env var (useful for containerized +/// deployments and integration tests that can't write to the default path). /// Unix: /usr/local/var/numa /// Windows: %PROGRAMDATA%\numa pub fn data_dir() -> std::path::PathBuf { + if let Ok(dir) = std::env::var("NUMA_DATA_DIR") { + return std::path::PathBuf::from(dir); + } #[cfg(windows)] { std::path::PathBuf::from( diff --git a/tests/integration.sh b/tests/integration.sh index a19d3bc..f1c5205 100755 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -526,6 +526,117 @@ CONF wait "$NUMA_PID" 2>/dev/null || true rm -f "$DOT_CERT" "$DOT_KEY" fi +sleep 1 + +# ---- Suite 6: Proxy + DoT coexistence ---- +echo "" +echo "╔══════════════════════════════════════════╗" +echo "║ Suite 6: Proxy + DoT Coexistence ║" +echo "╚══════════════════════════════════════════╝" + +if ! command -v kdig >/dev/null 2>&1 || ! command -v openssl >/dev/null 2>&1; then + printf " ${DIM}skipped — needs kdig + openssl${RESET}\n" +else + DOT_PORT=8853 + PROXY_HTTP_PORT=8080 + PROXY_HTTPS_PORT=8443 + NUMA_DATA=/tmp/numa-integration-data + + # Fresh data dir so we generate a fresh CA for this suite — NUMA_DATA_DIR + # env var lets numa write under $TMPDIR instead of /usr/local/var/numa. + rm -rf "$NUMA_DATA" + mkdir -p "$NUMA_DATA" + + cat > "$CONFIG" << CONF +[server] +bind_addr = "127.0.0.1:$PORT" +api_port = $API_PORT + +[upstream] +mode = "forward" +address = "127.0.0.1" +port = 65535 + +[cache] +max_entries = 10000 + +[blocking] +enabled = false + +[proxy] +enabled = true +port = $PROXY_HTTP_PORT +tls_port = $PROXY_HTTPS_PORT +tld = "numa" +bind_addr = "127.0.0.1" + +[dot] +enabled = true +port = $DOT_PORT +bind_addr = "127.0.0.1" + +[[zones]] +domain = "dot-test.example" +record_type = "A" +value = "10.0.0.1" +ttl = 60 +CONF + + NUMA_DATA_DIR="$NUMA_DATA" RUST_LOG=info "$BINARY" "$CONFIG" > "$LOG" 2>&1 & + NUMA_PID=$! + sleep 4 + + if ! kill -0 "$NUMA_PID" 2>/dev/null; then + FAILED=$((FAILED + 1)) + printf " ${RED}✗${RESET} Startup with proxy + DoT\n" + printf " ${DIM}%s${RESET}\n" "$(tail -5 "$LOG")" + else + echo "" + echo "=== Both listeners ===" + + check "DoT listener bound" \ + "DoT listening on 127.0.0.1:$DOT_PORT" \ + "$(grep 'DoT listening' "$LOG")" + + check "HTTPS proxy listener bound" \ + "HTTPS proxy listening on 127.0.0.1:$PROXY_HTTPS_PORT" \ + "$(grep 'HTTPS proxy listening' "$LOG")" + + PANIC_COUNT=$(grep -c 'panicked' "$LOG" 2>/dev/null || echo 0) + check "No startup panics in log" \ + "^0$" \ + "$PANIC_COUNT" + + echo "" + echo "=== DoT works with proxy enabled ===" + + # Proxy's build_tls_config runs first and creates the CA in + # $NUMA_DATA_DIR. DoT self_signed_tls then loads the same CA and + # issues its own leaf cert. One CA trusts both listeners. + CA="$NUMA_DATA/ca.pem" + KDIG="kdig @127.0.0.1 -p $DOT_PORT +tls +tls-ca=$CA +tls-hostname=numa.numa +time=5 +retry=0" + + check "DoT local zone A (with proxy on)" \ + "10.0.0.1" \ + "$($KDIG +short dot-test.example A 2>/dev/null)" + + echo "" + echo "=== Proxy TLS works with DoT enabled ===" + + # Proxy cert has SAN numa.numa (auto-added "numa" service). A + # successful handshake validates that the proxy's separate + # ServerConfig wasn't disturbed by DoT's own cert generation. + PROXY_TLS=$(echo "" | openssl s_client -connect "127.0.0.1:$PROXY_HTTPS_PORT" \ + -servername numa.numa -CAfile "$CA" 2>&1 /dev/null || true + wait "$NUMA_PID" 2>/dev/null || true + rm -rf "$NUMA_DATA" +fi # Summary echo ""