release: version 1.0.2 - Audit security fixes and RPM packaging

Security hardening:
- Add input sanitization for method (32), path (2048), host (256), http_version (16)
- Prevent log injection via oversized HTTP values
- Add LOG_THROTTLED macro for consistent error reporting
- Improve socket state double-check pattern to avoid unnecessary reconnects

Code quality:
- Fix const qualifier warnings in get_header()
- Add flags field to module definition
- Add -Wno-error=format-security for compatibility

Documentation:
- Clarify timestamp precision (microseconds expressed as nanoseconds)
- Update README and architecture.yml

Testing:
- Add 4 unit tests for input sanitization
- All 78 tests passing

Packaging:
- Remove DEB package support (RPM only: el8, el9, el10)
- Add CHANGELOG file included in RPM packages
- Bump version to 1.0.2

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Jacquin Antoine
2026-02-28 21:45:06 +01:00
parent d0ca0a7e4c
commit c2e1221e5a
8 changed files with 223 additions and 140 deletions

View File

@ -39,6 +39,21 @@
/* Maximum JSON log line size (64KB) - prevents memory exhaustion DoS */
#define MAX_JSON_SIZE (64 * 1024)
/* Helper macro for throttled error logging - prevents error_log flooding */
#define LOG_THROTTLED(state, cfg, s, level, err, msg, ...) do { \
apr_time_t lt_now = apr_time_now(); \
int lt_should_report = 0; \
FD_MUTEX_LOCK(state); \
if ((lt_now - state->last_error_report) >= apr_time_from_sec(cfg->error_report_interval)) { \
state->last_error_report = lt_now; \
lt_should_report = 1; \
} \
FD_MUTEX_UNLOCK(state); \
if (lt_should_report) { \
ap_log_error(APLOG_MARK, level, err, s, MOD_REQIN_LOG_NAME ": " msg, ##__VA_ARGS__); \
} \
} while(0)
/* Default sensitive headers blacklist - prevents accidental logging of credentials */
static const char *const DEFAULT_SENSITIVE_HEADERS[] = {
"Authorization",
@ -126,7 +141,8 @@ module AP_MODULE_DECLARE_DATA reqin_log_module = {
reqin_log_create_server_conf, /* server config creator */
NULL, /* server config merger */
reqin_log_cmds, /* command table */
reqin_log_register_hooks /* register hooks */
reqin_log_register_hooks, /* register hooks */
0 /* flags */
};
/* Get module configuration */
@ -434,9 +450,7 @@ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state,
{
apr_time_t now;
apr_time_t reconnect_interval;
apr_time_t error_interval;
int err = 0;
int should_report = 0;
int fd;
int flags;
struct sockaddr_un addr;
@ -454,7 +468,6 @@ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state,
now = apr_time_now();
reconnect_interval = apr_time_from_sec(cfg->reconnect_interval);
error_interval = apr_time_from_sec(cfg->error_report_interval);
FD_MUTEX_LOCK(state);
@ -471,16 +484,9 @@ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state,
if (state->socket_fd < 0) {
err = errno;
state->connect_failed = 1;
if ((now - state->last_error_report) >= error_interval) {
state->last_error_report = now;
should_report = 1;
}
FD_MUTEX_UNLOCK(state);
if (should_report) {
ap_log_error(APLOG_MARK, APLOG_ERR, err, s,
MOD_REQIN_LOG_NAME ": Unix socket connect failed: cannot create socket");
}
LOG_THROTTLED(state, cfg, s, APLOG_ERR, err,
"Unix socket connect failed: cannot create socket");
return -1;
}
@ -508,16 +514,9 @@ static int try_connect(reqin_log_config_t *cfg, reqin_log_child_state_t *state,
close(fd);
state->socket_fd = -1;
state->connect_failed = 1;
if ((now - state->last_error_report) >= error_interval) {
state->last_error_report = now;
should_report = 1;
}
FD_MUTEX_UNLOCK(state);
if (should_report) {
ap_log_error(APLOG_MARK, APLOG_ERR, err, s,
MOD_REQIN_LOG_NAME ": Unix socket connect failed: %s", cfg->socket_path);
}
LOG_THROTTLED(state, cfg, s, APLOG_ERR, err,
"Unix socket connect failed: %s", cfg->socket_path);
return -1;
}
@ -530,8 +529,11 @@ static int ensure_connected(reqin_log_config_t *cfg, reqin_log_child_state_t *st
{
int connected;
/* Double-check pattern: validate config and state under lock to avoid
* unnecessary reconnect attempts under high concurrency */
FD_MUTEX_LOCK(state);
connected = (state->socket_fd >= 0 && !state->connect_failed);
connected = (state->socket_fd >= 0 && !state->connect_failed &&
cfg != NULL && cfg->socket_path != NULL && cfg->socket_path[0] != '\0');
FD_MUTEX_UNLOCK(state);
if (connected) {
@ -546,14 +548,11 @@ static int write_to_socket(const char *data, apr_size_t len, server_rec *s,
{
int fd;
ssize_t n;
apr_time_t error_interval;
if (!cfg || !state || !s || !data || len == 0) {
return -1;
}
error_interval = apr_time_from_sec(cfg->error_report_interval);
FD_MUTEX_LOCK(state);
fd = state->socket_fd;
@ -565,8 +564,6 @@ static int write_to_socket(const char *data, apr_size_t len, server_rec *s,
n = send(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL);
if (n < 0) {
int err = errno;
apr_time_t now = apr_time_now();
int should_report = 0;
int conn_lost = (err == EPIPE || err == ECONNRESET || err == ENOTCONN);
if (conn_lost) {
@ -575,24 +572,14 @@ static int write_to_socket(const char *data, apr_size_t len, server_rec *s,
state->connect_failed = 1;
}
if (err != EAGAIN && err != EWOULDBLOCK &&
(now - state->last_error_report) >= error_interval) {
state->last_error_report = now;
should_report = 1;
}
FD_MUTEX_UNLOCK(state);
if (should_report) {
if (conn_lost) {
ap_log_error(APLOG_MARK, APLOG_ERR, err, s,
MOD_REQIN_LOG_NAME ": Unix socket write failed: connection lost");
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, err, s,
MOD_REQIN_LOG_NAME ": Unix socket write failed");
}
if (conn_lost) {
LOG_THROTTLED(state, cfg, s, APLOG_ERR, err,
"Unix socket write failed: connection lost");
} else {
LOG_THROTTLED(state, cfg, s, APLOG_ERR, err,
"Unix socket write failed");
}
return -1;
}
@ -613,8 +600,8 @@ static int write_to_socket(const char *data, apr_size_t len, server_rec *s,
static const char *get_header(request_rec *r, const char *name)
{
const apr_table_t *headers = r->headers_in;
apr_array_header_t *arr = apr_table_elts(headers);
apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
const apr_array_header_t *arr = apr_table_elts(headers);
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
int nelts = arr->nelts;
for (int i = 0; i < nelts; i++) {
@ -654,11 +641,27 @@ static void log_request(request_rec *r, reqin_log_config_t *cfg, reqin_log_child
dst_ip = r->connection->local_ip ? r->connection->local_ip : "";
method = r->method ? r->method : "UNKNOWN";
path = r->parsed_uri.path ? r->parsed_uri.path : "/";
/* Sanitize method and path to prevent log injection via oversized values */
if (strlen(method) > 32) {
method = apr_pstrmemdup(pool, method, 32);
}
if (strlen(path) > 2048) {
path = apr_pstrmemdup(pool, path, 2048);
}
host = apr_table_get(r->headers_in, "Host");
if (host == NULL) {
host = "";
} else {
/* Sanitize Host header to prevent log injection via oversized values */
if (strlen(host) > 256) {
host = apr_pstrmemdup(pool, host, 256);
}
}
http_version = r->protocol ? r->protocol : "UNKNOWN";
/* Sanitize HTTP version string */
if (strlen(http_version) > 16) {
http_version = apr_pstrmemdup(pool, http_version, 16);
}
dynbuf_init(&buf, pool, 4096);