architecture.yml: - Update header_handling: document built-in sensitive headers blacklist - Expand security section with hardening measures - Add socket permissions, path recommendations, environment variable README.md: - Add new 'Built-in Sensitive Headers Blacklist' section - Document all blocked headers (Authorization, Cookie, X-Api-Key, etc.) - Update socket security: permissions 0o660, /var/run path, group membership - Add hardening features: path validation, JSON size limit, NULL checks, mutex - Fix JSON example (remove extra closing brace) - Update socket consumer example with secure permissions (0o660) - Add MOD_REQIN_LOG_SOCKET environment variable to example - Update Fields table: header_<Name> flat structure description - Add note about automatic sensitive header exclusion Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
286 lines
7.2 KiB
Markdown
286 lines
7.2 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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+)
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```apache
|
|
# 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:
|
|
|
|
```json
|
|
{
|
|
"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 |
|
|
| `header_<Name>` | String | Flattened HTTP headers (e.g., `header_X-Request-Id`) |
|
|
|
|
**Note:** Headers are logged as flat fields at the root level (not nested). Sensitive headers are automatically excluded.
|
|
|
|
## Unix Socket Consumer
|
|
|
|
Create a Unix socket listener to receive log entries:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
import socket
|
|
import os
|
|
import json
|
|
|
|
SOCKET_PATH = os.environ.get("MOD_REQIN_LOG_SOCKET", "/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)
|
|
# Set secure permissions: owner and group only (not world-writable)
|
|
os.chmod(SOCKET_PATH, 0o660)
|
|
|
|
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()
|
|
```
|
|
|
|
**Note:** Ensure the Apache user is in the socket file's group to allow connections.
|
|
|
|
## Security Considerations
|
|
|
|
### Built-in Sensitive Headers Blacklist
|
|
|
|
⚠️ **The module automatically blocks logging of sensitive headers:**
|
|
|
|
The following headers are **always excluded** from logs to prevent credential leakage:
|
|
- `Authorization`
|
|
- `Cookie`, `Set-Cookie`
|
|
- `X-Api-Key`, `X-Auth-Token`
|
|
- `Proxy-Authorization`
|
|
- `WWW-Authenticate`
|
|
|
|
These headers are silently skipped (logged at DEBUG level only).
|
|
|
|
### Socket Security
|
|
|
|
- **Socket permissions**: Default to `0o660` (owner and group only)
|
|
- **Recommended path**: `/var/run/mod_reqin_log.sock` (not `/tmp`)
|
|
- **Environment variable**: Use `MOD_REQIN_LOG_SOCKET` to configure path
|
|
- **Group membership**: Ensure Apache user is in the socket's group
|
|
|
|
### Additional Hardening
|
|
|
|
- **Socket path length**: Validated against system limit (108 bytes max)
|
|
- **JSON size limit**: 64KB max per log line (prevents memory DoS)
|
|
- **NULL pointer checks**: All connection/request fields validated
|
|
- **Thread safety**: Mutex protects socket FD in worker/event MPMs
|
|
- **Error logging**: Generic messages in error_log, details at DEBUG level
|
|
|
|
## Troubleshooting
|
|
|
|
### Module not loading
|
|
|
|
```
|
|
AH00534: mod_reqin_log: Unable to load module
|
|
```
|
|
|
|
Ensure the module path is correct and the file exists:
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|