aboutsummaryrefslogtreecommitdiffstats
path: root/tests/run_cert_bootstrap_test.sh
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-02-18 23:09:43 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-02-18 23:09:43 +0100
commit8bfd0dc6b44438ba6c5d2844ce21fbc2adfe3f1a (patch)
tree8dc81d68d88652f2e4c7643c5cbfd17f24809366 /tests/run_cert_bootstrap_test.sh
parent74f18c6264618187386a5dc8b1152faa8727bf53 (diff)
downloadBobinkCOpcUa-8bfd0dc6b44438ba6c5d2844ce21fbc2adfe3f1a.tar.gz
BobinkCOpcUa-8bfd0dc6b44438ba6c5d2844ce21fbc2adfe3f1a.zip
Add TOFU certificate bootstrap integration test
Make download-cert always use an unsecure client so it can connect to a server's None discovery endpoint without the server certificate in the trust store. Add a cert_bootstrap test that verifies the full Trust On First Use workflow: find-servers succeeds, get-endpoints fails (untrusted cert), download-cert retrieves the certificate via None, then get-endpoints and read-time both succeed.
Diffstat (limited to 'tests/run_cert_bootstrap_test.sh')
-rwxr-xr-xtests/run_cert_bootstrap_test.sh166
1 files changed, 166 insertions, 0 deletions
diff --git a/tests/run_cert_bootstrap_test.sh b/tests/run_cert_bootstrap_test.sh
new file mode 100755
index 0000000..8d31783
--- /dev/null
+++ b/tests/run_cert_bootstrap_test.sh
@@ -0,0 +1,166 @@
+#!/usr/bin/env bash
+# ---------------------------------------------------------------
+# Integration test for certificate bootstrap (TOFU workflow).
+#
+# Demonstrates Trust On First Use:
+# 1. Client finds servers via LDS (trusted) → success
+# 2. Client get-endpoints on ServerRegister → FAILS (untrusted)
+# 3. Client download-cert on ServerRegister via None → success
+# 4. Client get-endpoints on ServerRegister → success (now trusted)
+# 5. Client read-time on ServerRegister → success
+#
+# Usage: tests/run_cert_bootstrap_test.sh <config_dir>
+#
+# Exit: 0 when all checks pass, 1 on any failure.
+# ---------------------------------------------------------------
+set -uo pipefail
+
+CONFIG_DIR="${1:?Usage: $0 <config_dir>}"
+
+LDS_PORT=14840
+SR_PORT=14841
+LDS_PID=""
+SR_PID=""
+TMPFILE=""
+DOWNLOADED_CERT=""
+FAILURES=0
+
+# ── cleanup ────────────────────────────────────────────────────
+cleanup() {
+ [ -n "$LDS_PID" ] && kill "$LDS_PID" 2>/dev/null && wait "$LDS_PID" 2>/dev/null
+ [ -n "$SR_PID" ] && kill "$SR_PID" 2>/dev/null && wait "$SR_PID" 2>/dev/null
+ [ -n "$TMPFILE" ] && rm -f "$TMPFILE"
+ [ -n "$DOWNLOADED_CERT" ] && rm -f "$DOWNLOADED_CERT"
+ rm -f "$CONFIG_DIR/certs/trust_client/ServerRegister_cert.der"
+}
+trap cleanup EXIT
+
+# ── helpers ────────────────────────────────────────────────────
+wait_for_port() {
+ local port="$1" pid="$2" label="$3" i=0
+ while [ $i -lt 50 ]; do
+ if ! kill -0 "$pid" 2>/dev/null; then
+ echo "FAIL: $label exited prematurely"
+ exit 1
+ fi
+ if ss -tlnp 2>/dev/null | grep -q ":${port} "; then
+ return 0
+ fi
+ sleep 0.1
+ i=$((i + 1))
+ done
+ echo "FAIL: $label did not listen on port $port within 5 s"
+ exit 1
+}
+
+check() {
+ local label="$1" result="$2"
+ if [ "$result" -eq 0 ]; then
+ echo "PASS: $label"
+ else
+ echo "FAIL: $label"
+ FAILURES=$((FAILURES + 1))
+ fi
+}
+
+# ── idempotency guard ─────────────────────────────────────────
+rm -f "$CONFIG_DIR/certs/trust_client/ServerRegister_cert.der"
+
+# ── port check ─────────────────────────────────────────────────
+for port in $LDS_PORT $SR_PORT; do
+ if ss -tlnp 2>/dev/null | grep -q ":${port} "; then
+ echo "FAIL: port $port is already in use"
+ exit 1
+ fi
+done
+
+# ── start LDS ──────────────────────────────────────────────────
+build/ServerLDS "$CONFIG_DIR/server_lds.conf" >/dev/null 2>&1 &
+LDS_PID=$!
+wait_for_port "$LDS_PORT" "$LDS_PID" "ServerLDS"
+
+# ── start ServerRegister ───────────────────────────────────────
+build/ServerRegister "$CONFIG_DIR/server_register.conf" "$CONFIG_DIR/server_register_client.conf" "opc.tcp://localhost:$LDS_PORT" >/dev/null 2>&1 &
+SR_PID=$!
+wait_for_port "$SR_PORT" "$SR_PID" "ServerRegister"
+
+TMPFILE=$(mktemp)
+
+# ── Step 1: FindServers on LDS (client trusts LDS) ────────────
+build/Client "$CONFIG_DIR/client.conf" find-servers "opc.tcp://localhost:$LDS_PORT" >"$TMPFILE" 2>&1
+FS_RC=$?
+FS_OUTPUT=$(<"$TMPFILE")
+
+[ "$FS_RC" -eq 0 ]
+check "find-servers exit code is 0 (got $FS_RC)" $?
+
+echo "$FS_OUTPUT" | grep -q "urn:localhost:bobink:ServerRegister"
+check "find-servers contains urn:localhost:bobink:ServerRegister" $?
+
+# ── Step 2: GetEndpoints on ServerRegister (should FAIL) ──────
+build/Client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1
+GE_FAIL_RC=$?
+GE_FAIL_OUTPUT=$(<"$TMPFILE")
+
+[ "$GE_FAIL_RC" -ne 0 ]
+check "get-endpoints FAILS without ServerRegister cert (exit code $GE_FAIL_RC)" $?
+
+# ── Step 3: download-cert from ServerRegister (via None) ──────
+DOWNLOADED_CERT=$(mktemp --suffix=.der)
+
+build/Client "$CONFIG_DIR/client.conf" download-cert "opc.tcp://localhost:$SR_PORT" "$DOWNLOADED_CERT" >"$TMPFILE" 2>&1
+DC_RC=$?
+DC_OUTPUT=$(<"$TMPFILE")
+
+[ "$DC_RC" -eq 0 ]
+check "download-cert exit code is 0 (got $DC_RC)" $?
+
+echo "$DC_OUTPUT" | grep -q "Certificate saved to"
+check "download-cert output contains 'Certificate saved to'" $?
+
+# ── Step 4: Verify downloaded cert matches original ───────────
+cmp -s "$DOWNLOADED_CERT" "$CONFIG_DIR/certs/ServerRegister/cert.der"
+check "downloaded certificate matches ServerRegister cert.der" $?
+
+# ── Step 5: Install cert into trust_client ────────────────────
+cp "$DOWNLOADED_CERT" "$CONFIG_DIR/certs/trust_client/ServerRegister_cert.der"
+
+# ── Step 6: GetEndpoints on ServerRegister (should succeed) ───
+build/Client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1
+GE_RC=$?
+GE_OUTPUT=$(<"$TMPFILE")
+
+[ "$GE_RC" -eq 0 ]
+check "get-endpoints succeeds after cert install (exit code $GE_RC)" $?
+
+echo "$GE_OUTPUT" | grep -q "Aes256_Sha256_RsaPss"
+check "get-endpoints contains Aes256_Sha256_RsaPss" $?
+
+# ── Step 7: ReadTime on ServerRegister (should succeed) ───────
+build/Client "$CONFIG_DIR/client.conf" read-time "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1
+RT_RC=$?
+RT_OUTPUT=$(<"$TMPFILE")
+
+[ "$RT_RC" -eq 0 ]
+check "read-time exit code is 0 (got $RT_RC)" $?
+
+echo "$RT_OUTPUT" | grep -q "date is:"
+check "read-time output contains 'date is:'" $?
+
+# ── result ─────────────────────────────────────────────────────
+if [ "$FAILURES" -ne 0 ]; then
+ echo ""
+ echo "--- find-servers output ---"
+ echo "$FS_OUTPUT"
+ echo "--- get-endpoints (expected fail) output ---"
+ echo "$GE_FAIL_OUTPUT"
+ echo "--- download-cert output ---"
+ echo "$DC_OUTPUT"
+ echo "--- get-endpoints (after install) output ---"
+ echo "$GE_OUTPUT"
+ echo "--- read-time output ---"
+ echo "$RT_OUTPUT"
+ echo "--- end ---"
+ exit 1
+fi
+exit 0