mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 02:40:49 +02:00
Add TLS-Anvil RFC compliance GitHub Actions workflow
Runs the TLS-Anvil combinatorial test suite nightly against wolfSSL in all four roles: TLS 1.2/1.3 server and TLS 1.2/1.3 client. Results are summarized in the job summary and uploaded as artifacts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# TLS-Anvil RFC compliance test script for wolfSSL
|
||||
# Usage: ./tls-anvil-test.sh <mode> [extra_configure_flags]
|
||||
# mode: 'server' or 'client'
|
||||
# extra_configure_flags: additional ./configure options (optional)
|
||||
#
|
||||
# This script:
|
||||
# 1. Builds wolfSSL with appropriate TLS options
|
||||
# 2. Runs TLS-Anvil Docker container against wolfSSL
|
||||
# 3. Collects and reports results
|
||||
#
|
||||
# Must be run from the wolfSSL source root directory.
|
||||
|
||||
set -e
|
||||
|
||||
MODE="${1:-server}"
|
||||
EXTRA_FLAGS="${2:-}"
|
||||
|
||||
# Unique name for port/container isolation (set externally or default)
|
||||
TEST_NAME="${TLS_ANVIL_TEST_NAME:-default}"
|
||||
|
||||
RESULTS_DIR="tls-anvil-results"
|
||||
TLS_ANVIL_IMAGE="ghcr.io/tls-attacker/tlsanvil:latest"
|
||||
TIMEOUT_SECONDS=1200
|
||||
STRENGTH="${TLS_ANVIL_STRENGTH:-1}"
|
||||
|
||||
# Derive a unique port from the test name to avoid conflicts on parallel runs.
|
||||
# Produces a port in the range 11111-11999.
|
||||
PORT_HASH=$(echo -n "$TEST_NAME" | cksum | awk '{print $1}')
|
||||
WOLFSSL_PORT=$((11111 + (PORT_HASH % 889)))
|
||||
|
||||
# Unique container name per run
|
||||
CONTAINER_NAME="tls-anvil-${TEST_NAME}-$$"
|
||||
|
||||
log_info() { echo "[INFO] $1"; }
|
||||
log_warn() { echo "[WARN] $1"; }
|
||||
log_error() { echo "[ERROR] $1"; }
|
||||
|
||||
cleanup() {
|
||||
log_info "Cleaning up..."
|
||||
if [[ -f "$RESULTS_DIR/server.pid" ]]; then
|
||||
local pid
|
||||
pid=$(cat "$RESULTS_DIR/server.pid")
|
||||
kill "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
rm -f "$RESULTS_DIR/server.pid"
|
||||
fi
|
||||
if command -v fuser &> /dev/null; then
|
||||
fuser -k "${WOLFSSL_PORT}/tcp" 2>/dev/null || true
|
||||
fi
|
||||
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
|
||||
sleep 1
|
||||
}
|
||||
|
||||
ensure_port_available() {
|
||||
local port=$1
|
||||
local attempt=0
|
||||
|
||||
if command -v fuser &> /dev/null; then
|
||||
fuser -k "${port}/tcp" 2>/dev/null || true
|
||||
elif command -v lsof &> /dev/null; then
|
||||
lsof -ti:"${port}" | xargs kill -9 2>/dev/null || true
|
||||
fi
|
||||
|
||||
while [ $attempt -lt 10 ]; do
|
||||
if ! (ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null) | grep -q ":${port} "; then
|
||||
return 0
|
||||
fi
|
||||
log_warn "Port ${port} still in use, waiting..."
|
||||
sleep 1
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Port ${port} still in use after 10 attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Clear any state from a previous run
|
||||
cleanup
|
||||
|
||||
if [[ "$MODE" != "server" && "$MODE" != "client" ]]; then
|
||||
log_error "Invalid mode: $MODE. Must be 'server' or 'client'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "TLS-Anvil Test - Mode: $MODE, Test: $TEST_NAME (port: $WOLFSSL_PORT)"
|
||||
log_info "Extra configure flags: $EXTRA_FLAGS"
|
||||
|
||||
mkdir -p "$RESULTS_DIR"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build wolfSSL
|
||||
# ---------------------------------------------------------------------------
|
||||
log_info "Building wolfSSL..."
|
||||
./autogen.sh
|
||||
|
||||
CONFIGURE_OPTS="--enable-asn=all"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-ocspstapling"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-tlsx"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-dtls"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-opensslextra"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-opensslall"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-supportedcurves"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-session-ticket"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-sni"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-alpn"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-truncatedhmac"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-extended-master"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-enc-then-mac"
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS C_EXTRA_FLAGS='-DWOLFSSL_EXTRA_ALERTS'"
|
||||
|
||||
if [[ -n "$EXTRA_FLAGS" ]]; then
|
||||
CONFIGURE_OPTS="$CONFIGURE_OPTS $EXTRA_FLAGS"
|
||||
fi
|
||||
|
||||
log_info "Configure options: $CONFIGURE_OPTS"
|
||||
# shellcheck disable=SC2086
|
||||
./configure $CONFIGURE_OPTS
|
||||
|
||||
make clean
|
||||
make -j"$(nproc)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Server mode: wolfSSL listens, TLS-Anvil probes as client
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "$MODE" == "server" ]]; then
|
||||
log_info "Starting wolfSSL server on port $WOLFSSL_PORT..."
|
||||
ensure_port_available "$WOLFSSL_PORT"
|
||||
|
||||
if [[ ! -f "certs/server-cert.pem" ]] || [[ ! -f "certs/server-key.pem" ]]; then
|
||||
log_error "Certificate files not found in certs/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wrapper loop: restarts the server if it exits so TLS-Anvil can reconnect
|
||||
# between test cases without the whole run failing.
|
||||
cat > "$RESULTS_DIR/run-server.sh" << 'SERVERSCRIPT'
|
||||
#!/bin/bash
|
||||
CHILD_PID=
|
||||
cleanup() {
|
||||
[[ -n "$CHILD_PID" ]] && kill "$CHILD_PID" 2>/dev/null; wait "$CHILD_PID" 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
trap cleanup SIGTERM SIGINT
|
||||
while true; do
|
||||
./examples/server/server -p "$1" -C 4 -r -i -d -x \
|
||||
-c certs/server-cert.pem -k certs/server-key.pem -v d 2>&1 &
|
||||
CHILD_PID=$!
|
||||
wait "$CHILD_PID"
|
||||
echo "Server exited, restarting in 1 second..."
|
||||
sleep 1
|
||||
done
|
||||
SERVERSCRIPT
|
||||
chmod +x "$RESULTS_DIR/run-server.sh"
|
||||
|
||||
"$RESULTS_DIR/run-server.sh" "$WOLFSSL_PORT" > "$RESULTS_DIR/server.log" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
echo "$SERVER_PID" > "$RESULTS_DIR/server.pid"
|
||||
sleep 1
|
||||
|
||||
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
log_error "wolfSSL server failed to start"
|
||||
cat "$RESULTS_DIR/server.log" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "wolfSSL server started (PID: $SERVER_PID)"
|
||||
|
||||
if command -v openssl &> /dev/null; then
|
||||
log_info "Quick connectivity check..."
|
||||
echo "Q" | timeout 5 openssl s_client \
|
||||
-connect "127.0.0.1:$WOLFSSL_PORT" -tls1_2 2>&1 | head -5 \
|
||||
|| log_warn "Pre-check had issues (not fatal)"
|
||||
fi
|
||||
|
||||
log_info "Running TLS-Anvil (client mode, timeout: ${TIMEOUT_SECONDS}s, strength: $STRENGTH)..."
|
||||
ANVIL_EXIT_CODE=0
|
||||
timeout "$TIMEOUT_SECONDS" docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--network host \
|
||||
-v "$(pwd)/$RESULTS_DIR:/output" \
|
||||
"$TLS_ANVIL_IMAGE" \
|
||||
-outputFolder /output \
|
||||
-parallelHandshakes 4 \
|
||||
-strength "$STRENGTH" \
|
||||
-connectionTimeout 200 \
|
||||
server \
|
||||
-connect "127.0.0.1:$WOLFSSL_PORT" \
|
||||
|| ANVIL_EXIT_CODE=$?
|
||||
|
||||
log_info "Stopping wolfSSL server..."
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
|
||||
if [[ "$ANVIL_EXIT_CODE" -ne 0 ]]; then
|
||||
log_warn "TLS-Anvil exited $ANVIL_EXIT_CODE - last 50 lines of server log:"
|
||||
tail -50 "$RESULTS_DIR/server.log" || true
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Client mode: TLS-Anvil listens, wolfSSL connects on each test case
|
||||
# ---------------------------------------------------------------------------
|
||||
else
|
||||
log_info "Running TLS-Anvil (server mode, wolfSSL as client, timeout: ${TIMEOUT_SECONDS}s)..."
|
||||
ensure_port_available "$WOLFSSL_PORT"
|
||||
|
||||
WOLFSSL_DIR="$(pwd)"
|
||||
|
||||
# TLS-Anvil calls this script once per test case to trigger a client connection.
|
||||
cat > "$RESULTS_DIR/trigger-client.sh" << EOF
|
||||
#!/bin/bash
|
||||
cd "$WOLFSSL_DIR"
|
||||
exec ./examples/client/client -h "127.0.0.1" -p "$WOLFSSL_PORT" -d -g -v d
|
||||
EOF
|
||||
chmod +x "$RESULTS_DIR/trigger-client.sh"
|
||||
|
||||
ANVIL_EXIT_CODE=0
|
||||
timeout "$TIMEOUT_SECONDS" docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--network host \
|
||||
-v "$(pwd)/$RESULTS_DIR:/output" \
|
||||
-v "$WOLFSSL_DIR:$WOLFSSL_DIR" \
|
||||
"$TLS_ANVIL_IMAGE" \
|
||||
-outputFolder /output \
|
||||
-parallelHandshakes 3 \
|
||||
-parallelTests 3 \
|
||||
-strength "$STRENGTH" \
|
||||
client \
|
||||
-port "$WOLFSSL_PORT" \
|
||||
-triggerScript "$WOLFSSL_DIR/$RESULTS_DIR/trigger-client.sh" \
|
||||
|| ANVIL_EXIT_CODE=$?
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Results
|
||||
# ---------------------------------------------------------------------------
|
||||
log_info "Checking results..."
|
||||
|
||||
if [[ -f "$RESULTS_DIR/report.json" ]]; then
|
||||
log_info "report.json found"
|
||||
if command -v jq &> /dev/null; then
|
||||
TOTAL=$(jq '.Score.Total // "N/A"' "$RESULTS_DIR/report.json" 2>/dev/null || echo "N/A")
|
||||
PASS=$( jq '.Score.Succeeded // "N/A"' "$RESULTS_DIR/report.json" 2>/dev/null || echo "N/A")
|
||||
FAIL=$( jq '.Score.Failed // "N/A"' "$RESULTS_DIR/report.json" 2>/dev/null || echo "N/A")
|
||||
log_info " Total: $TOTAL"
|
||||
log_info " Passed: $PASS"
|
||||
log_info " Failed: $FAIL"
|
||||
|
||||
cat > "$RESULTS_DIR/summary.txt" << EOF
|
||||
TLS-Anvil Test Summary
|
||||
======================
|
||||
Mode: $MODE
|
||||
Date: $(date)
|
||||
Config: $CONFIGURE_OPTS
|
||||
|
||||
Results:
|
||||
Total: $TOTAL
|
||||
Passed: $PASS
|
||||
Failed: $FAIL
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
log_warn "No report.json found"
|
||||
ls -la "$RESULTS_DIR/" || true
|
||||
fi
|
||||
|
||||
if [[ "$ANVIL_EXIT_CODE" -ne 0 ]]; then
|
||||
log_error "TLS-Anvil exited with code $ANVIL_EXIT_CODE"
|
||||
# Exit non-zero so the workflow step is marked failed
|
||||
exit "$ANVIL_EXIT_CODE"
|
||||
fi
|
||||
|
||||
log_info "TLS-Anvil testing complete"
|
||||
Reference in New Issue
Block a user