Files
mod_reqin_log/scripts/test_unix_socket.sh
Jacquin Antoine e44059865b Security: fix critical vulnerabilities and harden module
Security fixes:
#1 Buffer overflow: Validate socket path length against sun_path limit
    - Add MAX_SOCKET_PATH_LEN constant
    - Reject paths >= 108 bytes before snprintf

#2,#3 NULL pointer dereference: Add NULL checks
    - r->connection->local_ip: use conditional append
    - r->protocol: fallback to "UNKNOWN" if NULL

#4 Sensitive headers blacklist: Prevent credential leakage
    - Add DEFAULT_SENSITIVE_HEADERS[] blacklist
    - Block: Authorization, Cookie, Set-Cookie, X-Api-Key, etc.
    - Log skipped headers at DEBUG level only

#5 Memory exhaustion DoS: Add MAX_JSON_SIZE limit (64KB)
    - Check buffer size before adding headers
    - Truncate header list if limit reached

#6 Socket permissions: Change 0o666 → 0o660
    - Owner and group only (not world-writable)
    - Apache user must be in socket's group

#7 Race condition: Add mutex for FD access in worker/event MPMs
    - apr_thread_mutex_t protects socket_fd
    - FD_MUTEX_LOCK/UNLOCK macros
    - Created in reqin_log_create_server_conf()

#8 Timestamp overflow: Document 2262 limitation
    - Add comment explaining apr_time_t limits
    - Safe until ~2262 (uint64 nanoseconds)

#9 Error logging verbosity: Reduce information disclosure
    - APLOG_ERR: Generic messages only
    - APLOG_DEBUG: Detailed error information

#10 Socket path security: Move from /tmp to /var/run
    - Update socket_consumer.py, test scripts
    - Use environment variable MOD_REQIN_LOG_SOCKET
    - More secure default location

Files modified:
- src/mod_reqin_log.c: All security fixes
- scripts/socket_consumer.py: Permissions, path
- scripts/run_integration_tests.sh: Path security
- scripts/test_unix_socket.sh: Path security
- tests/integration/test_integration.py: Path security

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-26 23:37:30 +01:00

319 lines
8.1 KiB
Bash
Executable File

#!/bin/bash
#
# test_unix_socket.sh - Integration test for mod_reqin_log Unix socket logging
#
# This test verifies that:
# 1. The module connects to a Unix socket
# 2. HTTP requests generate JSON log entries
# 3. Log entries are properly formatted and sent to the socket
#
set -e
# Use /var/run for production (more secure than /tmp)
SOCKET_PATH="${SOCKET_PATH:-/var/run/mod_reqin_log_test.sock}"
LOG_OUTPUT="${LOG_OUTPUT:-/var/log/mod_reqin_log_output.jsonl}"
APACHE_PORT="${APACHE_PORT:-8080}"
TIMEOUT=30
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${YELLOW}[INFO]${NC} $1"
}
log_pass() {
echo -e "${GREEN}[PASS]${NC} $1"
}
log_fail() {
echo -e "${RED}[FAIL]${NC} $1"
}
cleanup() {
log_info "Cleaning up..."
rm -f "$SOCKET_PATH" "$LOG_OUTPUT"
pkill -f "socket_listener.py" 2>/dev/null || true
pkill -f "apache.*test" 2>/dev/null || true
}
trap cleanup EXIT
# Check dependencies
check_dependencies() {
log_info "Checking dependencies..."
if ! command -v curl &> /dev/null; then
log_fail "curl is required but not installed"
exit 1
fi
if ! command -v python3 &> /dev/null; then
log_fail "python3 is required but not installed"
exit 1
fi
log_pass "Dependencies OK"
}
# Start Unix socket listener that logs received data
start_socket_listener() {
log_info "Starting Unix socket listener on $SOCKET_PATH..."
rm -f "$SOCKET_PATH"
# Use Python script to listen on Unix socket
python3 /build/scripts/socket_listener.py "$SOCKET_PATH" -o "$LOG_OUTPUT" &
LISTENER_PID=$!
sleep 2
if ! kill -0 $LISTENER_PID 2>/dev/null; then
log_fail "Failed to start socket listener"
exit 1
fi
log_pass "Socket listener started (PID: $LISTENER_PID)"
}
# Create Apache test configuration
create_apache_config() {
log_info "Creating Apache test configuration..."
cat > /tmp/httpd_test.conf << EOF
ServerRoot "/etc/httpd"
Listen $APACHE_PORT
ServerName localhost
LoadModule reqin_log_module /build/.libs/mod_reqin_log.so
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule log_config_module modules/mod_log_config.so
User apache
Group apache
TypesConfig /etc/mime.types
DirectoryIndex index.html
JsonSockLogEnabled On
JsonSockLogSocket "$SOCKET_PATH"
JsonSockLogHeaders X-Request-Id User-Agent X-Test-Header
JsonSockLogMaxHeaders 10
JsonSockLogMaxHeaderValueLen 256
JsonSockLogReconnectInterval 5
JsonSockLogErrorReportInterval 5
<VirtualHost *:$APACHE_PORT>
DocumentRoot /var/www/html
<Directory /var/www/html>
Require all granted
</Directory>
</VirtualHost>
ErrorLog /dev/stderr
LogLevel warn
EOF
log_pass "Apache configuration created"
}
# Start Apache with test configuration
start_apache() {
log_info "Starting Apache with mod_reqin_log..."
# Create document root if needed
mkdir -p /var/www/html
echo "<html><body><h1>Test</h1></body></html>" > /var/www/html/index.html
# Check socket exists
if [ ! -S "$SOCKET_PATH" ]; then
log_fail "Socket file does not exist: $SOCKET_PATH"
ls -la /tmp/*.sock 2>&1 || true
exit 1
fi
log_info "Socket file exists: $SOCKET_PATH"
ls -la "$SOCKET_PATH"
# Start Apache and capture stderr
httpd -f /tmp/httpd_test.conf -DFOREGROUND 2>&1 &
APACHE_PID=$!
# Wait for Apache to start
sleep 2
if ! kill -0 $APACHE_PID 2>/dev/null; then
log_fail "Failed to start Apache"
exit 1
fi
log_pass "Apache started (PID: $APACHE_PID)"
# Wait for Apache workers to initialize and connect to socket
log_info "Waiting for Apache workers to initialize..."
sleep 3
}
# Send test HTTP requests
send_test_requests() {
log_info "Sending test HTTP requests..."
# Health check first
local retries=5
while [ $retries -gt 0 ]; do
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:$APACHE_PORT/" | grep -q "200"; then
break
fi
sleep 1
retries=$((retries - 1))
done
if [ $retries -eq 0 ]; then
log_fail "Apache health check failed"
return 1
fi
log_info "Apache health check passed"
# Request 1: Basic GET
curl -s "http://localhost:$APACHE_PORT/" > /dev/null
sleep 0.5
# Request 2: With custom headers
curl -s -H "X-Request-Id: test-12345" "http://localhost:$APACHE_PORT/" > /dev/null
sleep 0.5
# Request 3: POST request
curl -s -X POST -d "test=data" "http://localhost:$APACHE_PORT/" > /dev/null
sleep 0.5
# Request 4: With User-Agent
curl -s -A "TestAgent/1.0" "http://localhost:$APACHE_PORT/api/test" > /dev/null
sleep 0.5
# Request 5: Multiple headers
curl -s \
-H "X-Request-Id: req-abc" \
-H "X-Test-Header: header-value" \
-H "User-Agent: Mozilla/5.0" \
"http://localhost:$APACHE_PORT/page" > /dev/null
sleep 1
log_pass "Test requests sent"
}
# Verify log output
verify_logs() {
log_info "Verifying log output..."
if [ ! -f "$LOG_OUTPUT" ]; then
log_fail "Log file not created"
return 1
fi
local log_count=$(wc -l < "$LOG_OUTPUT")
if [ "$log_count" -lt 1 ]; then
log_fail "Expected at least 1 log entry, got $log_count"
return 1
fi
log_pass "Received $log_count log entries"
# Show all log entries
log_info "Log file contents:"
cat "$LOG_OUTPUT"
# Verify JSON format
log_info "Validating JSON format..."
local invalid_json=0
while IFS= read -r line; do
if [ -n "$line" ]; then
if ! echo "$line" | python3 -m json.tool > /dev/null 2>&1; then
log_fail "Invalid JSON: $line"
invalid_json=1
fi
fi
done < "$LOG_OUTPUT"
if [ $invalid_json -eq 0 ]; then
log_pass "All JSON entries are valid"
fi
# Verify required fields
log_info "Checking required fields..."
local first_line=$(head -1 "$LOG_OUTPUT")
local required_fields=("time" "timestamp" "src_ip" "dst_ip" "method" "path" "host")
local missing_fields=0
for field in "${required_fields[@]}"; do
if ! echo "$first_line" | grep -q "\"$field\":"; then
log_fail "Missing required field: $field"
missing_fields=1
fi
done
if [ $missing_fields -eq 0 ]; then
log_pass "All required fields present"
fi
# Verify header logging
log_info "Checking header logging..."
if grep -q '"header_X-Request-Id"' "$LOG_OUTPUT"; then
log_pass "Custom headers are logged"
else
log_info "Custom headers test skipped (no X-Request-Id in requests)"
fi
# Verify HTTP methods
log_info "Checking HTTP methods..."
if grep -q '"method":"GET"' "$LOG_OUTPUT"; then
log_pass "GET method logged"
fi
if grep -q '"method":"POST"' "$LOG_OUTPUT"; then
log_pass "POST method logged"
fi
# Show sample log entry
log_info "Sample log entry (formatted):"
head -1 "$LOG_OUTPUT" | python3 -m json.tool 2>/dev/null || head -1 "$LOG_OUTPUT"
return 0
}
# Main test runner
main() {
echo "========================================"
echo "mod_reqin_log Unix Socket Integration Test"
echo "========================================"
echo ""
check_dependencies
start_socket_listener
create_apache_config
start_apache
send_test_requests
log_info "Waiting for logs to be written..."
sleep 2
verify_logs
local result=$?
echo ""
echo "========================================"
if [ $result -eq 0 ]; then
echo -e "${GREEN}All tests passed!${NC}"
else
echo -e "${RED}Some tests failed!${NC}"
fi
echo "========================================"
return $result
}
main "$@"