Files
Checkmate/scripts/test-custom-ca.sh

455 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
# Dev/Test only: Not required in production
# This script tests the custom CA trust functionality in development environment
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DEV_DIR="${ROOT_DIR}/docker/dev"
CERT_DIR="${DEV_DIR}/certs"
NGINX_DIR="${DEV_DIR}/nginx-test"
COMPOSE_BASELINE="${DEV_DIR}/docker-compose.yaml"
COMPOSE_CUSTOM_CA="${DEV_DIR}/docker-compose.custom-ca-example.yaml"
COMPOSE_NGINX_TEST="${NGINX_DIR}/docker-compose.nginx-test.yaml"
# Test configuration
NGINX_PORT=8443
CHECKMATE_PORT=52345
HEALTH_ENDPOINT="http://localhost:$CHECKMATE_PORT/api/v1/health"
TEST_URL="https://host.docker.internal:$NGINX_PORT"
MAX_WAIT=60
WAIT_INTERVAL=2
# Global flag for cleanup behavior
CLEAN_CERTS=false
# Function to print colored output
print_status() {
local status=$1
local message=$2
case $status in
"PASS")
echo -e "${GREEN}[PASS]${NC} $message"
;;
"FAIL")
echo -e "${RED}[FAIL]${NC} $message"
;;
"INFO")
echo -e "${BLUE}[INFO]${NC} $message"
;;
"WARN")
echo -e "${YELLOW}[WARN]${NC} $message"
;;
esac
}
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to check if certificates exist and are valid
check_certificates() {
local ca_file="${CERT_DIR}/custom-ca.pem"
local cert_file="${CERT_DIR}/host-int-cert.pem"
local key_file="${CERT_DIR}/host-int-key.pem"
if [ ! -f "$ca_file" ] || [ ! -f "$cert_file" ] || [ ! -f "$key_file" ]; then
return 1
fi
# Check if files are not empty
if [ ! -s "$ca_file" ] || [ ! -s "$cert_file" ] || [ ! -s "$key_file" ]; then
return 1
fi
# Check if CA file starts with BEGIN CERTIFICATE
if ! head -1 "$ca_file" | grep -q "BEGIN CERTIFICATE"; then
return 1
fi
return 0
}
# Function to wait for service to be ready
wait_for_service() {
local url=$1
local service_name=$2
local elapsed=0
print_status "INFO" "Waiting for $service_name to be ready..."
while [ $elapsed -lt $MAX_WAIT ]; do
if curl -s -f "$url" >/dev/null 2>&1; then
print_status "PASS" "$service_name is ready after ${elapsed}s"
return 0
fi
sleep $WAIT_INTERVAL
elapsed=$((elapsed + WAIT_INTERVAL))
echo -n "."
done
print_status "FAIL" "$service_name failed to start within ${MAX_WAIT}s"
return 1
}
# Function to get container name by image
get_container_name() {
local image_name=$1
docker ps --filter "ancestor=$image_name" --format "{{.Names}}" | head -n1
}
# Function to run Node.js probe test
run_probe_test() {
local container_name=$1
local test_name=$2
local expected_exit_code=$3
print_status "INFO" "Running $test_name probe test..."
local probe_script="
const https = require('https');
https.get('https://host.docker.internal:8443', res => {
console.log('STATUS', res.statusCode);
process.exit(res.statusCode===200?0:1);
}).on('error', e => {
console.error('ERR', e.code||e.message);
process.exit(1);
});
"
local exit_code
if docker exec -i "$container_name" node -e "$probe_script" 2>/dev/null; then
exit_code=$?
else
exit_code=$?
fi
if [ $exit_code -eq $expected_exit_code ]; then
print_status "PASS" "$test_name probe test completed with expected exit code $expected_exit_code"
return 0
else
print_status "FAIL" "$test_name probe test failed with exit code $exit_code (expected $expected_exit_code)"
return 1
fi
}
# Function to setup certificates if needed
setup_certificates_if_needed() {
if check_certificates; then
print_status "INFO" "Certificates already exist and are valid, skipping generation"
return 0
fi
print_status "INFO" "Certificates missing or invalid, generating new ones..."
if [ -x "${ROOT_DIR}/scripts/dev/setup-custom-ca.sh" ]; then
"${ROOT_DIR}/scripts/dev/setup-custom-ca.sh"
else
print_status "FAIL" "Certificate setup script not found at scripts/dev/setup-custom-ca.sh"
return 1
fi
}
# Function to create nginx test configuration
create_nginx_test_config() {
print_status "INFO" "Setting up nginx test configuration..."
mkdir -p "$NGINX_DIR"
# Create nginx.conf if it doesn't exist
if [ ! -f "${NGINX_DIR}/nginx.conf" ]; then
cat > "${NGINX_DIR}/nginx.conf" << 'EOF'
events {}
http {
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
location / {
return 200 "hello from tls\n";
}
}
}
EOF
fi
# Create docker-compose.nginx-test.yaml if it doesn't exist
if [ ! -f "$COMPOSE_NGINX_TEST" ]; then
cat > "$COMPOSE_NGINX_TEST" << EOF
services:
nginx-test:
image: nginx:alpine
ports:
- "$NGINX_PORT:443"
volumes:
- ../certs/host-int-cert.pem:/etc/nginx/certs/server.crt:ro
- ../certs/host-int-key.pem:/etc/nginx/certs/server.key:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
restart: unless-stopped
EOF
fi
print_status "PASS" "Nginx test configuration created"
}
# Function to start nginx test service
start_nginx_test() {
print_status "INFO" "Starting nginx test service..."
cd "$NGINX_DIR"
docker-compose -f docker-compose.nginx-test.yaml up -d
# Wait for nginx to be ready
local elapsed=0
while [ $elapsed -lt $MAX_WAIT ]; do
if curl -s -f -k "https://localhost:$NGINX_PORT" >/dev/null 2>&1; then
print_status "PASS" "Nginx test service is ready"
cd "$ROOT_DIR"
return 0
fi
sleep $WAIT_INTERVAL
elapsed=$((elapsed + WAIT_INTERVAL))
echo -n "."
done
print_status "FAIL" "Nginx test service failed to start"
cd "$ROOT_DIR"
return 1
}
# Function to stop nginx test service
stop_nginx_test() {
print_status "INFO" "Stopping nginx test service..."
cd "$NGINX_DIR"
docker-compose -f docker-compose.nginx-test.yaml down 2>/dev/null || true
cd "$ROOT_DIR"
}
# Function to run baseline test
run_baseline_test() {
print_status "INFO" "Running baseline test (should fail due to unknown CA)..."
# Start baseline Checkmate
cd "$DEV_DIR"
docker-compose -f docker-compose.yaml up -d --build
# Wait for Checkmate to be ready
if ! wait_for_service "$HEALTH_ENDPOINT" "Checkmate baseline"; then
cd "$ROOT_DIR"
return 1
fi
# Get container name
local container_name
container_name=$(get_container_name "uptime_server")
if [ -z "$container_name" ]; then
print_status "FAIL" "Could not find Checkmate server container"
cd "$ROOT_DIR"
return 1
fi
# Run probe test (should fail)
if run_probe_test "$container_name" "baseline" 1; then
print_status "PASS" "Baseline test completed - TLS failure as expected"
cd "$ROOT_DIR"
return 0
else
print_status "FAIL" "Baseline test failed - unexpected behavior"
cd "$ROOT_DIR"
return 1
fi
}
# Function to run custom CA test
run_custom_ca_test() {
print_status "INFO" "Running custom CA test (should succeed)..."
# Stop baseline Checkmate
cd "$DEV_DIR"
docker-compose -f docker-compose.yaml down
# Start Checkmate with custom CA
docker-compose -f docker-compose.yaml -f docker-compose.custom-ca-example.yaml up -d --build
# Wait for Checkmate to be ready
if ! wait_for_service "$HEALTH_ENDPOINT" "Checkmate custom CA"; then
cd "$ROOT_DIR"
return 1
fi
# Get container name
local container_name
container_name=$(get_container_name "uptime_server")
if [ -z "$container_name" ]; then
print_status "FAIL" "Could not find Checkmate server container"
cd "$ROOT_DIR"
return 1
fi
# Run probe test (should succeed)
if run_probe_test "$container_name" "custom CA" 0; then
print_status "PASS" "Custom CA test completed - TLS success as expected"
cd "$ROOT_DIR"
return 0
else
print_status "FAIL" "Custom CA test failed - unexpected behavior"
cd "$ROOT_DIR"
return 1
fi
}
# Function to cleanup everything
cleanup() {
print_status "INFO" "Cleaning up test environment..."
# Stop all services
cd "$DEV_DIR"
docker-compose -f docker-compose.yaml down 2>/dev/null || true
docker-compose -f docker-compose.yaml -f docker-compose.custom-ca-example.yaml down 2>/dev/null || true
stop_nginx_test
# Only remove certificates if --clean was specified
if [ "$CLEAN_CERTS" = true ]; then
print_status "INFO" "Removing certificates as requested with --clean"
rm -rf "${CERT_DIR:?}"/*
else
print_status "INFO" "Preserving certificates for future test runs"
fi
cd "$ROOT_DIR"
print_status "PASS" "Cleanup completed"
}
# Function to show usage
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --clean Clean up all test containers and certificates"
echo " --help Show this help message"
echo ""
echo "This script tests the custom CA trust functionality in Checkmate."
}
# Main function
main() {
local clean_only=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--clean)
clean_only=true
CLEAN_CERTS=true
shift
;;
--help)
show_usage
exit 0
;;
*)
echo "Unknown option: $1"
show_usage
exit 1
;;
esac
done
print_status "INFO" "Starting Checkmate Custom CA Trust Test"
echo "=================================================="
if [ "$clean_only" = true ]; then
cleanup
exit 0
fi
# Check prerequisites
if ! command_exists docker; then
print_status "FAIL" "Docker is not installed or not in PATH"
exit 1
fi
if ! command_exists docker-compose; then
print_status "FAIL" "docker-compose is not installed or not in PATH"
exit 1
fi
# Check if required files exist
if [ ! -f "$COMPOSE_BASELINE" ]; then
print_status "FAIL" "Baseline docker-compose.yaml not found at $COMPOSE_BASELINE"
exit 1
fi
if [ ! -f "$COMPOSE_CUSTOM_CA" ]; then
print_status "FAIL" "Custom CA docker-compose override not found at $COMPOSE_CUSTOM_CA"
exit 1
fi
# Setup test environment
setup_certificates_if_needed
create_nginx_test_config
start_nginx_test
# Run tests
local baseline_result=false
local custom_ca_result=false
if run_baseline_test; then
baseline_result=true
fi
if run_custom_ca_test; then
custom_ca_result=true
fi
# Print summary
echo ""
echo "=================================================="
print_status "INFO" "Test Summary"
echo "=================================================="
if [ "$baseline_result" = true ]; then
print_status "PASS" "Baseline: TLS failure as expected"
else
print_status "FAIL" "Baseline: Unexpected behavior"
fi
if [ "$custom_ca_result" = true ]; then
print_status "PASS" "Custom CA: TLS success (STATUS 200)"
else
print_status "FAIL" "Custom CA: Unexpected behavior"
fi
if [ "$baseline_result" = true ] && [ "$custom_ca_result" = true ]; then
echo ""
print_status "PASS" "All tests passed! Custom CA trust is working correctly."
echo ""
print_status "INFO" "To clean up, run: $0 --clean"
exit 0
else
echo ""
print_status "FAIL" "Some tests failed. Custom CA trust may not be working correctly."
echo ""
print_status "INFO" "To clean up, run: $0 --clean"
exit 1
fi
}
# Trap cleanup on script exit
trap cleanup EXIT
# Run main function
main "$@"