Jacquin Antoine 7cfd14fb65 Fix: add missing child_exit hook per architecture.yml
- Add reqin_log_child_exit() to close Unix socket on child exit
- Register hook with ap_hook_child_exit()
- Ensures clean socket cleanup as specified in architecture.yml

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-26 14:04:47 +01:00

mod_reqin_log

Apache HTTPD 2.4 module for logging all incoming HTTP requests as JSON lines to a Unix domain socket.

Features

  • Non-blocking I/O: Logging never blocks worker processes
  • Request-time logging: Logs at post_read_request phase, capturing request data before application processing
  • Configurable headers: Select which HTTP headers to include in logs
  • Header truncation: Limit header value length to protect against oversized logs
  • Automatic reconnection: Reconnects to Unix socket on failure with configurable backoff
  • Throttled error reporting: Prevents error_log flooding on persistent failures
  • MPM compatible: Works with prefork, worker, and event MPMs

Requirements

  • Apache HTTPD 2.4+
  • GCC compiler
  • APR development libraries
  • Apache development headers (httpd-devel or apache2-dev)

Installation

Build from Source

# Clone or extract the source
cd mod_reqin_log

# Build the module
make

# Install (requires root privileges)
sudo make install

Using Package Manager

RPM (Rocky Linux 8+)

# Build RPM package
rpmbuild -ba packaging/rpm/mod_reqin_log.spec

# Install the package
sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/mod_reqin_log-1.0.0-1.el8.x86_64.rpm

DEB (Debian/Ubuntu)

# Build DEB package
cd packaging/deb
debuild -us -uc

# Install the package
sudo dpkg -i ../libapache2-mod-reqin-log_1.0.0_amd64.deb

Configuration

Load the module and configure in your Apache configuration:

# Load the module
LoadModule reqin_log_module modules/mod_reqin_log.so

# Enable logging
JsonSockLogEnabled On

# Unix socket path
JsonSockLogSocket "/var/run/mod_reqin_log.sock"

# Headers to log (be careful not to log sensitive data)
JsonSockLogHeaders X-Request-Id X-Trace-Id User-Agent Referer

# Maximum headers to log
JsonSockLogMaxHeaders 10

# Maximum header value length
JsonSockLogMaxHeaderValueLen 256

# Reconnect interval (seconds)
JsonSockLogReconnectInterval 10

# Error report interval (seconds)
JsonSockLogErrorReportInterval 10

Configuration Directives

Directive Type Default Description
JsonSockLogEnabled On/Off Off Enable or disable logging
JsonSockLogSocket String - Unix domain socket path
JsonSockLogHeaders List - HTTP headers to include
JsonSockLogMaxHeaders Integer 10 Max headers to log
JsonSockLogMaxHeaderValueLen Integer 256 Max header value length
JsonSockLogReconnectInterval Integer 10 Reconnect delay (seconds)
JsonSockLogErrorReportInterval Integer 10 Error log throttle (seconds)

JSON Log Format

Each log entry is a single-line JSON object with a flat structure:

{
  "time": "2026-02-26T11:59:30Z",
  "timestamp": 1708948770000000000,
  "src_ip": "192.0.2.10",
  "src_port": 45678,
  "dst_ip": "198.51.100.5",
  "dst_port": 443,
  "method": "GET",
  "path": "/api/users",
  "host": "example.com",
  "http_version": "HTTP/1.1",
  "header_X-Request-Id": "abcd-1234",
  "header_User-Agent": "curl/7.70.0"
  }
}

Fields

Field Type Description
time String ISO8601 timestamp with timezone
timestamp Integer Nanoseconds since epoch
src_ip String Client IP address
src_port Integer Client port
dst_ip String Server IP address
dst_port Integer Server port
method String HTTP method
path String Request path
host String Host header value
http_version String HTTP protocol version
headers Object Configured HTTP headers

Unix Socket Consumer

Create a Unix socket listener to receive log entries:

#!/usr/bin/env python3
import socket
import os
import json

SOCKET_PATH = "/var/run/mod_reqin_log.sock"

# Remove existing socket file
if os.path.exists(SOCKET_PATH):
    os.remove(SOCKET_PATH)

# Create Unix socket server
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(SOCKET_PATH)
server.listen(5)
os.chmod(SOCKET_PATH, 0o666)

print(f"Listening on {SOCKET_PATH}")

while True:
    conn, addr = server.accept()
    data = b""
    while True:
        chunk = conn.recv(4096)
        if not chunk:
            break
        data += chunk
        if b"\n" in data:
            for line in data.decode().strip().split("\n"):
                if line:
                    log_entry = json.loads(line)
                    print(json.dumps(log_entry, indent=2))
            data = b""
    conn.close()

Security Considerations

⚠️ Important: This module logs all HTTP requests transparently.

  • Do not log sensitive headers: Avoid including Authorization, Cookie, X-Api-Key, or other sensitive headers in JsonSockLogHeaders
  • Socket permissions: Ensure the Unix socket has appropriate file permissions
  • Log consumer security: Protect the socket consumer from unauthorized access

Troubleshooting

Module not loading

AH00534: mod_reqin_log: Unable to load module

Ensure the module path is correct and the file exists:

ls -la /usr/lib/apache2/modules/mod_reqin_log.so

Socket connection failures

[mod_reqin_log] Unix socket connect failed: /var/run/mod_reqin_log.sock
  • Ensure the socket consumer is running
  • Check socket file permissions
  • Verify SELinux/AppArmor policies if applicable

No logs appearing

  1. Verify JsonSockLogEnabled On is set
  2. Verify JsonSockLogSocket path is configured
  3. Check Apache error log for module errors
  4. Ensure socket consumer is listening

Testing

Run Unit Tests

# Install test dependencies
sudo dnf install cmocka-devel  # Rocky Linux
sudo apt install libcmocka-dev # Debian/Ubuntu

# Build and run tests
mkdir build && cd build
cmake ..
make test

Integration Testing

# Start socket consumer
python3 scripts/socket_consumer.py &

# Start Apache with module enabled
sudo systemctl start httpd

# Send test requests
curl -H "X-Request-Id: test-123" http://localhost/

# Check consumer output

License

Apache License 2.0

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests
  5. Submit a pull request
Description
No description provided
Readme 533 KiB
Languages
C 93%
Makefile 5.4%
CMake 1.6%