#!/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 # # Exit: 0 when all checks pass, 1 on any failure. # --------------------------------------------------------------- set -uo pipefail CONFIG_DIR="${1:?Usage: $0 }" 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