Jan 06 2026 dev (#387)

* sync code

* update code to support brotli

* update code to support brotli

* update code to support brotli

* sync code

* fix findBrotli

* sync code

* sync code

* sync code

* sync code

---------

Co-authored-by: Ned Wright <nedwright@proton.me>
Co-authored-by: Daniel Eisenberg <danielei@checkpoint.com>
This commit is contained in:
Daniel-Eisenberg
2026-01-13 17:17:52 +02:00
committed by GitHub
parent c1058db57d
commit e7b6e51b31
216 changed files with 12601 additions and 2825 deletions

View File

@@ -51,12 +51,56 @@ static const boost::regex error_log_regex(
", (host: \".+?\")$"
);
static const boost::regex incident_log_regex(
"("
+ syslog_regex_string + ") "
+ "incidentLog\\s+"
"host=([^\\s]+)\\s+"
"host_port=(\\d+)\\s+"
"client_addr=([\\d\\.]+)\\s+"
"client_port=(\\d+)\\s+"
"time_local=\\[([^\\]]+)\\]\\s+"
"request=\"([^\"]+)\"\\s+"
"status=(\\d{3})\\s+"
"uri=\"([^\"]+)\"\\s+"
"request_id=([^\\s]+)\\s+"
"upstream_status=([^,\\s]+)(?:,\\s*[^\\s]+)?\\s+"
"upstream_response_time=([^,\\s]+)(?:,\\s*[^\\s]+)?\\s+"
"body_bytes_sent=(\\d+)\\s+"
"referer=\"([^\"]*)\"\\s+"
"user_agent=\"([^\"]*)\"\\s+"
"pf=([^\\s]*)\\s+"
"x_event_id=([^\\s]*)"
);
static const boost::regex generic_crit_log_regex(
"("
+ syslog_regex_string + ") "
+ "(?:\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} )?" // Optional nginx timestamp
+ "\\[crit\\] (.+)$"
);
static const boost::regex generic_emerg_log_regex(
"("
+ syslog_regex_string + ") "
+ "(?:\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} )?" // Optional nginx timestamp
+ "\\[emerg\\] (.+)$"
);
static const boost::regex generic_fallback_log_regex(
"("
+ syslog_regex_string + ") "
+ "(?:\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} )?" // Optional nginx timestamp
+ "\\[(\\w+)\\] (.+)$"
);
static const boost::regex server_regex("(\\d+\\.\\d+\\.\\d+\\.\\d+)|(\\w+\\.\\w+)");
static const boost::regex uri_regex("^/");
static const boost::regex port_regex("\\d+");
static const boost::regex response_code_regex("[0-9]{3}");
static const boost::regex http_method_regex("[A-Za-z]+");
static const string central_nginx_manager = "Central NGINX Manager";
class NginxMessageReader::Impl
{
public:
@@ -64,6 +108,15 @@ public:
init()
{
dbgFlow(D_NGINX_MESSAGE_READER);
if (Singleton::exists<I_Environment>()) {
auto name = Singleton::Consume<I_Environment>::by<Report>()->get<string>("Service Name");
if (name.ok()) {
dbgInfo(D_NGINX_MESSAGE_READER) << "Service name: " << *name;
service_name = *name;
}
}
I_MainLoop *mainloop = Singleton::Consume<I_MainLoop>::by<NginxMessageReader>();
mainloop->addOneTimeRoutine(
I_MainLoop::RoutineType::System,
@@ -97,6 +150,7 @@ public:
"429",
"accessControl.rateLimit.returnCode"
);
dbgTrace(D_NGINX_MESSAGE_READER) << "Selected rate-limit status code: " << rate_limit_status_code;
}
@@ -107,6 +161,7 @@ private:
RESPONSE_CODE,
HOST,
SOURCE,
SOURCE_PORT,
DESTINATION_IP,
DESTINATION_PORT,
EVENT_MESSAGE,
@@ -114,9 +169,21 @@ private:
ASSET_NAME,
RULE_NAME,
RULE_ID,
REFERENCE_ID,
LOG_TYPE,
PROXY_FAULT,
X_EVENT_ID,
MATCHED_REASON,
REMEDIATION,
COUNT
};
struct GenericLogInfo {
string timestamp;
string severity;
string message;
};
void
initSyslogServerSocket()
{
@@ -175,10 +242,12 @@ private:
bool log_sent;
if (isAccessLog(log)) {
log_sent = sendAccessLog(log);
} else if (isAlertErrorLog(log) || isErrorLog(log)) {
} else if (isAlertErrorLog(log) || isErrorLog(log) || isCritErrorLog(log) || isEmergErrorLog(log)) {
log_sent = sendErrorLog(log);
} else if (isIncidentLog(log)) {
log_sent = sendIncidentLog(log);
} else {
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx log format";
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx log format for message: "<< log;
continue;
}
if (!log_sent) {
@@ -222,13 +291,74 @@ private:
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Error log" << log;
Maybe<EnumArray<LogInfo, string>> log_info = parseErrorLog(log);
if (!log_info.ok()) {
dbgWarning(D_NGINX_MESSAGE_READER)
<< "Failed parsing the NGINX logs. Error: "
<< log_info.getErr();
if (log_info.ok()) {
return sendLog(log_info.unpack());
}
if (service_name == central_nginx_manager) {
dbgDebug(D_NGINX_MESSAGE_READER) << "Detailed parsing failed, trying generic parsing";
Maybe<GenericLogInfo> generic_log = parseGenericErrorLog(log);
if (generic_log.ok()) {
return sendGenericLog(generic_log.unpack());
}
}
dbgWarning(D_NGINX_MESSAGE_READER)
<< "Failed parsing the NGINX logs. Error: "
<< log_info.getErr()
<< service_name;
return false;
}
static bool
isValidUuid(const string &uuid_str)
{
if (uuid_str.empty() || uuid_str == "-") {
return false;
}
return sendLog(log_info.unpack());
if (uuid_str.length() != 36) {
return false;
}
if (uuid_str[8] != '-' || uuid_str[13] != '-' || uuid_str[18] != '-' || uuid_str[23] != '-') {
return false;
}
for (size_t i = 0; i < uuid_str.length(); ++i) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
continue;
}
char c = uuid_str[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
return false;
}
}
return true;
}
bool
sendIncidentLog(const string &log)
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Incident log" << log;
Maybe<EnumArray<LogInfo, string>> log_info = parseIncidentLog(log);
if (!log_info.ok()) {
dbgTrace(D_NGINX_MESSAGE_READER) << log_info.getErr();
return false;
}
auto unpacked_log_info = log_info.unpack();
if (isValidUuid(unpacked_log_info[LogInfo::X_EVENT_ID])) {
dbgTrace(D_NGINX_MESSAGE_READER)
<< "Skipping incident log - valid x_event_id present: "
<< unpacked_log_info[LogInfo::X_EVENT_ID];
return true;
}
unpacked_log_info[LogInfo::LOG_TYPE] = "incidentLog";
return sendNotification(unpacked_log_info);
}
bool
@@ -253,7 +383,111 @@ private:
}
bool
sendLog(const EnumArray<LogInfo, string> &log_info)
isIncidentLog(const string &log) const
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Check if log is of type 'incident log'. Log: " << log;
return log.find("incidentLog") != string::npos;
}
bool
sendNotification(const EnumArray<LogInfo, string> &log_info)
{
dbgFlow(D_NGINX_MESSAGE_READER);
string event_name;
if (
log_info[LogInfo::LOG_TYPE] == "incidentLog"
&& (log_info[LogInfo::RESPONSE_CODE][0] == '4' || log_info[LogInfo::RESPONSE_CODE][0] == '5')
) {
bool is_proxy_fault = (log_info[LogInfo::PROXY_FAULT] == "yes");
event_name = getEventName(is_proxy_fault);
} else {
return false;
}
LogGen log(
event_name,
ReportIS::Level::LOG,
ReportIS::Audience::SECURITY,
ReportIS::Severity::HIGH,
ReportIS::Priority::HIGH,
ReportIS::Tags::REVERSE_PROXY
);
log << LogField("eventConfidence", "High");
for (LogInfo field : makeRange<LogInfo>()) {
Maybe<string> string_field = convertLogFieldToString(field);
if (!string_field.ok()) {
dbgDebug(D_NGINX_MESSAGE_READER) << "Enum field was not converted: " << string_field.getErr();
return false;
}
if (string_field.unpack().empty() || log_info[field].empty()) {
continue;
}
if (field == LogInfo::ASSET_ID || field == LogInfo::ASSET_NAME) {
log.addToOrigin(LogField(string_field.unpack(), log_info[field]));
continue;
}
if (field != LogInfo::DESTINATION_PORT && field != LogInfo::SOURCE_PORT) {
log << LogField(string_field.unpack(), log_info[field]);
continue;
}
try {
log << LogField(string_field.unpack(), stoi(log_info[field]));
} catch (const exception &e) {
dbgError(D_NGINX_MESSAGE_READER)
<< "Unable to convert port to numeric value: "
<< e.what();
log << LogField(string_field.unpack(), 0);
}
}
return true;
}
bool
isCritErrorLog(const string &log) const
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Check if log is of type 'crit log'. Log: " << log;
return log.find("[crit]") != string::npos;
}
bool
isEmergErrorLog(const string &log) const
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Check if log is of type 'emerg log'. Log: " << log;
return log.find("[emerg]") != string::npos;
}
Maybe<string>
getCNMEventName(const EnumArray<LogInfo, string> &log_info) const
{
dbgFlow(D_NGINX_MESSAGE_READER);
string event_name;
switch (log_info[LogInfo::RESPONSE_CODE][0]) {
case '4': {
event_name = "NGINX Proxy Error: Invalid request or incorrect NGINX configuration - Request dropped."
" Please check the reverse proxy configuration of your relevant assets";
break;
}
case '5': {
event_name = "NGINX Proxy Error: Request failed! Please verify your proxy configuration."
"If the issue persists please contact open-appsec support";
break;
}
default: {
dbgError(D_NGINX_MESSAGE_READER) << "Irrelevant status code";
return genError("Irrelevant status code");
}
}
return event_name;
}
Maybe<string>
getRPMEventName(const EnumArray<LogInfo, string> &log_info) const
{
dbgFlow(D_NGINX_MESSAGE_READER);
string event_name;
@@ -265,27 +499,55 @@ private:
}
case '5': {
event_name = "AppSec Gateway reverse proxy error - Request dropped. "
"Please verify the reverse proxy configuration of your relevant assets. "
"If the issue persists please contact Check Point Support";
"Please verify the reverse proxy configuration of your relevant assets. "
"If the issue persists please contact Check Point Support";
break;
}
default: {
dbgError(D_NGINX_MESSAGE_READER) << "Irrelevant status code";
return false;
return genError("Irrelevant status code");
}
}
return event_name;
}
Maybe<string> getEventName(const EnumArray<LogInfo, string> &log_info)
{
if (service_name != central_nginx_manager) {
dbgWarning(D_NGINX_MESSAGE_READER)
<< "Unknown service name: "
<< service_name
<< " Response will be sent as CNM";
}
return getCNMEventName(log_info);
}
bool
sendLog(const EnumArray<LogInfo, string> &log_info)
{
dbgFlow(D_NGINX_MESSAGE_READER);
Maybe<string> event_name = getEventName(log_info);
if (!event_name.ok()) {
dbgError(D_NGINX_MESSAGE_READER) << event_name.getErr();
return false;
}
dbgTrace(D_NGINX_MESSAGE_READER)
<< "Nginx log's event name and response code: "
<< event_name
<< event_name.unpack()
<< ", "
<< log_info[LogInfo::RESPONSE_CODE];
LogGen log(
event_name,
event_name.unpack(),
ReportIS::Audience::SECURITY,
ReportIS::Severity::INFO,
ReportIS::Severity::HIGH,
ReportIS::Priority::LOW,
ReportIS::Tags::REVERSE_PROXY
service_name == central_nginx_manager ?
ReportIS::Tags::CENTRAL_NGINX_MANAGER :
ReportIS::Tags::REVERSE_PROXY
);
log << LogField("eventConfidence", "High");
@@ -296,6 +558,10 @@ private:
return false;
}
if (string_field.unpack().empty() || log_info[field].empty()) {
continue;
}
if (field != LogInfo::DESTINATION_PORT) {
log << LogField(string_field.unpack(), log_info[field]);
continue;
@@ -313,6 +579,45 @@ private:
return true;
}
bool
sendGenericLog(const GenericLogInfo &log_info)
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Sending generic log";
string event_name = "NGINX Proxy Error: Request failed! Please verify your proxy configuration."
"If the issue persists please contact open-appsec support";
ReportIS::Severity severity = ReportIS::Severity::MEDIUM;
ReportIS::Priority priority = ReportIS::Priority::MEDIUM;
if (log_info.severity == "emerg" || log_info.severity == "crit") {
severity = ReportIS::Severity::CRITICAL;
priority = ReportIS::Priority::URGENT;
} else if (log_info.severity == "error" || log_info.severity == "alert") {
severity = ReportIS::Severity::HIGH;
priority = ReportIS::Priority::HIGH;
}
LogGen log(
event_name,
ReportIS::Audience::SECURITY,
severity,
priority,
ReportIS::Tags::CENTRAL_NGINX_MANAGER
);
log << LogField("eventConfidence", "High");
log << LogField("timestamp", log_info.timestamp);
log << LogField("httpResponseBody", formatGenericLogMessage(log_info));
return true;
}
string
formatGenericLogMessage(const GenericLogInfo &log_info)
{
return "[" + log_info.severity + "] " + log_info.message;
}
bool
sendRateLimitLog(const EnumArray<LogInfo, string> &log_info)
{
@@ -338,7 +643,6 @@ private:
string security_action = "Drop";
bool is_log_required = false;
// Prevent events checkbox (in triggers)
if (rate_limit_trigger.isPreventLogActive(LogTriggerConf::SecurityType::AccessControl)) {
is_log_required = true;
}
@@ -372,7 +676,11 @@ private:
dbgDebug(D_NGINX_MESSAGE_READER) << "Enum field was not converted: " << string_field.getErr();
return false;
}
if (string_field.unpack().empty() || log_info[field].empty()) {
continue;
}
if (
field == LogInfo::HOST ||
field == LogInfo::URI ||
@@ -406,6 +714,55 @@ private:
return true;
}
static string
getStatusCodeMessage(const string &status_code)
{
static map<string, string> status_messages = {
// 4xx Client Error codes
{"400", "Bad Request - malformed syntax"},
{"401", "Unauthorized - authentication required"},
{"403", "Forbidden - access denied"},
{"404", "Not Found - resource does not exist"},
{"405", "Method Not Allowed - HTTP verb not permitted"},
{"408", "Request Timeout - client too slow"},
{"411", "Length Required - missing Content-Length"},
{"413", "Payload Too Large - body exceeds limit"},
{"414", "URI Too Long - request target exceeds limit"},
{"416", "Range Not Satisfiable - invalid byte range"},
{"429", "Too Many Requests - rate limit exceeded"},
{"431", "Header Fields Too Large - headers exceed limit"},
{"451", "Unavailable For Legal Reasons"},
// NGINX specific 4xx codes
{"494", "Request Header Too Large - NGINX internal"},
{"495", "SSL Certificate Error - invalid client cert"},
{"496", "SSL Certificate Required - none provided"},
{"497", "Plain HTTP sent to HTTPS port"},
// 5xx Server Error codes
{"500", "Internal Server Error"},
{"501", "Not Implemented - feature unsupported"},
{"502", "Bad Gateway - upstream connection failed"},
{"503", "Service Unavailable - server overloaded"},
{"504", "Gateway Timeout - upstream too slow"},
{"505", "HTTP Version Not Supported"},
{"507", "Insufficient Storage - WebDAV quota exceeded"}
};
auto it = status_messages.find(status_code);
return (it != status_messages.end()) ? it->second : "HTTP Error " + status_code;
}
static string
getEventName(bool is_proxy_fault)
{
if (!is_proxy_fault) {
return "Upstream Application Error";
}
return "Reverse Proxy Error";
}
Maybe<string>
convertLogFieldToString(LogInfo field)
{
@@ -420,7 +777,9 @@ private:
case LogInfo::HOST:
return string("httpHostName");
case LogInfo::SOURCE:
return string("httpSourceId");
return string("sourceip");
case LogInfo::SOURCE_PORT:
return string("sourcePort");
case LogInfo::DESTINATION_IP:
return string("destinationIp");
case LogInfo::DESTINATION_PORT:
@@ -435,6 +794,18 @@ private:
return string("ruleId");
case LogInfo::RULE_NAME:
return string("ruleName");
case LogInfo::REFERENCE_ID:
return string("eventReferenceId");
case LogInfo::MATCHED_REASON:
return string("matchreason");
case LogInfo::REMEDIATION:
return string("eventRemediation");
case LogInfo::LOG_TYPE:
return string("");
case LogInfo::PROXY_FAULT:
return string("");
case LogInfo::X_EVENT_ID:
return string("");
case LogInfo::COUNT:
dbgError(D_NGINX_MESSAGE_READER) << "LogInfo::COUNT is not allowed";
return genError("LogInfo::COUNT is not allowed");
@@ -512,7 +883,7 @@ private:
static_cast<uint16_t>(stoi(log_info[LogInfo::DESTINATION_PORT]))
);
} catch (const exception &e) {
dbgError(D_NGINX_MESSAGE_READER) << "Failed register values for context " << e.what();
dbgWarning(D_NGINX_MESSAGE_READER) << "Failed register values for context " << e.what();
}
ctx.registerValue<string>(HttpTransactionData::host_name_ctx, log_info[LogInfo::HOST]);
ctx.registerValue<string>(HttpTransactionData::uri_ctx, log_info[LogInfo::URI]);
@@ -525,12 +896,65 @@ private:
}
BasicRuleConfig context = rule_by_ctx.unpack();
dbgTrace(D_NGINX_MESSAGE_READER)
<< "Adding context fields to log info. Asset ID: "
<< context.getAssetId()
<< ", Asset Name: "
<< context.getAssetName()
<< ", Rule ID: "
<< context.getRuleId()
<< ", Rule Name: "
<< context.getRuleName();
log_info[LogInfo::ASSET_ID] = context.getAssetId();
log_info[LogInfo::ASSET_NAME] = context.getAssetName();
log_info[LogInfo::RULE_ID] = context.getRuleId();
log_info[LogInfo::RULE_NAME] = context.getRuleName();
}
Maybe<GenericLogInfo>
parseGenericErrorLog(const string &log_line)
{
dbgFlow(D_NGINX_MESSAGE_READER) << "Parsing generic error log: " << log_line;
boost::smatch matcher;
GenericLogInfo generic_log;
if (isCritErrorLog(log_line)) {
if (NGEN::Regex::regexSearch(__FILE__, __LINE__, log_line, matcher, generic_crit_log_regex)) {
const int timestamp_index = 2;
const int message_index = 5;
generic_log.timestamp = string(matcher[timestamp_index].first, matcher[timestamp_index].second);
generic_log.severity = "crit";
generic_log.message = string(matcher[message_index].first, matcher[message_index].second);
return generic_log;
}
} else if (isEmergErrorLog(log_line)) {
if (NGEN::Regex::regexSearch(__FILE__, __LINE__, log_line, matcher, generic_emerg_log_regex)) {
const int timestamp_index = 2;
const int message_index = 5;
generic_log.timestamp = string(matcher[timestamp_index].first, matcher[timestamp_index].second);
generic_log.severity = "emerg";
generic_log.message = string(matcher[message_index].first, matcher[message_index].second);
return generic_log;
}
}
if (NGEN::Regex::regexSearch(__FILE__, __LINE__, log_line, matcher, generic_fallback_log_regex)) {
const int timestamp_index = 2;
const int severity_index = 5;
const int message_index = 6;
generic_log.timestamp = string(matcher[timestamp_index].first, matcher[timestamp_index].second);
generic_log.severity = string(matcher[severity_index].first, matcher[severity_index].second);
generic_log.message = string(matcher[message_index].first, matcher[message_index].second);
return generic_log;
}
dbgWarning(D_NGINX_MESSAGE_READER) << "Could not parse log with generic method: " << log_line;
return genError("Could not parse log with generic method");
}
Maybe<EnumArray<LogInfo, string>>
parseErrorLog(const string &log_line)
{
@@ -540,17 +964,28 @@ private:
boost::smatch matcher;
vector<string> result;
boost::regex selected_regex;
if (isAlertErrorLog(log_line)) {
selected_regex = alert_log_regex;
} else if (isErrorLog(log_line)) {
selected_regex = error_log_regex;
} else {
dbgWarning(D_NGINX_MESSAGE_READER) << "No matching log type found for log: " << log_line;
return genError("No matching log type found");
}
if (
!NGEN::Regex::regexSearch(
__FILE__,
__LINE__,
log_line,
matcher,
isAlertErrorLog(log_line) ? alert_log_regex : error_log_regex
selected_regex
)
) {
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx log format";
return genError("Unexpected nginx log format");
dbgWarning(D_NGINX_MESSAGE_READER) << "Detailed regex parsing failed for log: " << log_line;
return genError("Detailed regex parsing failed");
}
const int event_message_index = 6;
@@ -591,8 +1026,8 @@ private:
addContextFieldsToLogInfo(log_info);
if (!validateLog(log_info)) {
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx log format";
return genError("Unexpected nginx log format");
dbgWarning(D_NGINX_MESSAGE_READER) << "Log validation failed for detailed parsing";
return genError("Log validation failed for detailed parsing");
}
return log_info;
@@ -642,6 +1077,104 @@ private:
return log_info;
}
Maybe<EnumArray<LogInfo, string>>
parseIncidentLog(const string &log_line)
{
dbgTrace(D_NGINX_MESSAGE_READER) << "Parsing incident log line: " << log_line;
EnumArray<LogInfo, string> log_info(EnumArray<LogInfo, string>::Fill(), string(""));
boost::smatch matcher;
if (!NGEN::Regex::regexSearch(__FILE__, __LINE__, log_line, matcher, incident_log_regex)) {
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx incident log format";
return genError("Unexpected nginx incident log format");
}
const int host_index = 5; // host=server_name
const int host_port_index = 6; // host_port=port
const int client_addr_index = 7; // client_addr=IP
const int client_port_index = 8; // client_port=port
const int request_index = 10; // request="METHOD URI HTTP/1.1"
const int status_index = 11; // status=CODE
const int uri_index = 12; // uri="PATH"
const int request_id_index = 13; // request_id=ID
const int proxy_fault_index = 19; // pf=yes or empty
const int x_event_id_index = 20; // x_event_id=value or empty
string host = string(matcher[host_index].first, matcher[host_index].second);
string host_port = string(matcher[host_port_index].first, matcher[host_port_index].second);
string uri = string(matcher[uri_index].first, matcher[uri_index].second);
string proxy_fault = string(matcher[proxy_fault_index].first, matcher[proxy_fault_index].second);
log_info[LogInfo::HOST] = host;
log_info[LogInfo::URI] = uri;
log_info[LogInfo::DESTINATION_PORT] = host_port;
log_info[LogInfo::PROXY_FAULT] = proxy_fault;
dbgTrace(D_NGINX_MESSAGE_READER)
<< "Parsed host: "
<< host
<< ", host_port: "
<< host_port
<< ", uri: "
<< uri
<< ", proxy_fault: "
<< proxy_fault;
addContextFieldsToLogInfo(log_info);
if (log_info[LogInfo::ASSET_ID].empty() || log_info[LogInfo::ASSET_NAME].empty()) {
return genError("Asset ID or Asset Name is empty");
}
string client_addr = string(matcher[client_addr_index].first, matcher[client_addr_index].second);
string client_port = string(matcher[client_port_index].first, matcher[client_port_index].second);
string request = string(matcher[request_index].first, matcher[request_index].second);
string status = string(matcher[status_index].first, matcher[status_index].second);
string request_id = string(matcher[request_id_index].first, matcher[request_id_index].second);
string x_event_id = string(matcher[x_event_id_index].first, matcher[x_event_id_index].second);
dbgTrace(D_NGINX_MESSAGE_READER)
<< "Parsed client_addr: "
<< client_addr
<< ", client_port: "
<< client_port
<< ", request: "
<< request
<< ", status: "
<< status
<< ", request_id: "
<< request_id
<< ", proxy_fault: "
<< proxy_fault
<< ", x_event_id: "
<< x_event_id;
vector<string> request_parts;
boost::split(request_parts, request, boost::is_any_of(" "), boost::token_compress_on);
string http_method = request_parts.size() > 0 ? request_parts[0] : "";
log_info[LogInfo::REFERENCE_ID] = request_id;
log_info[LogInfo::RESPONSE_CODE] = status;
log_info[LogInfo::HTTP_METHOD] = http_method;
log_info[LogInfo::SOURCE] = client_addr;
log_info[LogInfo::SOURCE_PORT] = client_port;
log_info[LogInfo::DESTINATION_IP] = host;
log_info[LogInfo::X_EVENT_ID] = x_event_id;
if (!validateLog(log_info)) {
dbgWarning(D_NGINX_MESSAGE_READER) << "Unexpected nginx incident log format after validation";
return genError("Unexpected nginx incident log format after validation");
}
if (proxy_fault == "yes") {
log_info[LogInfo::MATCHED_REASON] = getStatusCodeMessage(status);
log_info[LogInfo::REMEDIATION] = getIncidentLogRemediation(status);
}
return log_info;
}
static bool
validateLog(const EnumArray<LogInfo, string> &log_info)
{
@@ -708,8 +1241,54 @@ private:
return move(raw_log);
}
static string
getIncidentLogRemediation(const string &status_code)
{
static map<string, string> status_remediations = {
{"400", "Check request syntax and format; validate client input and HTTP headers"},
{"401", "Verify authentication credentials and configuration"},
{"403", "If this is a valid WAF block, no action is required; otherwise,"
" contact Check Point Support"},
{"404", "Validate the requested asset URI"},
{"405", "Ensure allowed HTTP methods are configured correctly"},
{"408", "Adjust `client_header_timeout` and `client_body_timeout`"
" in the assets advanced nginx server block"},
{"411", "Require a Content-Length header on requests with a body"},
{"413", "Increase `client_max_body_size` in the asset advanced nginx server block"},
{"414", "Increase `large_client_header_buffers` (e.g. `large_client_header_buffers 4 32k;`)"
" in the asset advanced nginx server block"},
{"416", "Validate Range request headers against actual file sizes"},
{"429", "Review rate limiting settings and implement proper backoff strategies"},
{"431", "Increase `large_client_header_buffers` (e.g. `large_client_header_buffers 4 32k;`)"
" in the asset advanced nginx server block"},
{"451", "Review content filtering policies and legal compliance requirements"},
{"494", "Increase `large_client_header_buffers` (e.g. `large_client_header_buffers 4 32k;`)"
" in the asset advanced nginx server block"},
{"495", "Confirm client SSL certificates are valid and properly configured"},
{"496", "Configure SSL client certificate requirements correctly"},
{"497", "Enforce HTTPS for secure endpoints"},
{"500", "Contact Check Point Support"},
{"501", "Ensure requested features are supported and configured correctly"},
{"502", "Verify upstream server connectivity and health; check proxy configuration"},
{"503", "Assess server capacity and load balancing; verify upstream availability"},
{"504", "Increase `proxy_connect_timeout` and `proxy_read_timeout`"
" in the asset advanced nginx configuration"},
{"505", "Ensure HTTP version compatibility between client and server"},
{"507", "Contact Check Point Support"}
};
auto it = status_remediations.find(status_code);
if (it != status_remediations.end()) {
return it->second + ". If this suggestion does not resolve the issue, please contact support.";
}
return "Please verify the nginx configuration of your relevant assets. "
"Please contact support if the issue persists.";
}
I_Socket::socketFd syslog_server_socket = -1;
string rate_limit_status_code = "429";
string service_name = "Unnamed Nano Service";
};
NginxMessageReader::NginxMessageReader() : Component("NginxMessageReader"), pimpl(make_unique<Impl>()) {}