mirror of
https://github.com/openappsec/attachment.git
synced 2025-08-13 13:25:59 +03:00
parent
6154961b0b
commit
a5db1bbbc6
@ -14,7 +14,7 @@ static HttpHeaderData *
|
|||||||
get_http_header(HttpHeaders *http_headers, const char *header_name) {
|
get_http_header(HttpHeaders *http_headers, const char *header_name) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < http_headers->headers_count; ++i) {
|
for (i = 0; i < http_headers->headers_count; ++i) {
|
||||||
if (strcmp((char*)http_headers->data[i].key.data, header_name) == 0) {
|
if (strcasecmp((char*)http_headers->data[i].key.data, header_name) == 0) {
|
||||||
return &http_headers->data[i];
|
return &http_headers->data[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,11 +42,11 @@ set_response_content_encoding(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp((char*)content_encoding->value.data, "gzip") == 0) {
|
if (strcasecmp((char*)content_encoding->value.data, "gzip") == 0) {
|
||||||
session_data_p->response_data.compression_type = GZIP;
|
session_data_p->response_data.compression_type = GZIP;
|
||||||
} else if (strcmp((char*)content_encoding->value.data, "deflate") == 0) {
|
} else if (strcasecmp((char*)content_encoding->value.data, "deflate") == 0) {
|
||||||
session_data_p->response_data.compression_type = ZLIB;
|
session_data_p->response_data.compression_type = ZLIB;
|
||||||
} else if (strcmp((char*)content_encoding->value.data, "identity") == 0) {
|
} else if (strcasecmp((char*)content_encoding->value.data, "identity") == 0) {
|
||||||
session_data_p->response_data.compression_type = NO_COMPRESSION;
|
session_data_p->response_data.compression_type = NO_COMPRESSION;
|
||||||
} else {
|
} else {
|
||||||
write_dbg(
|
write_dbg(
|
||||||
|
@ -385,7 +385,7 @@ set_docker_id(NanoAttachment *attachment)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!uid_read) {
|
if (!uid_read) {
|
||||||
const char *env_var_name = "OPENAPPSEC_UID"; // Replace with your environment variable name
|
const char *env_var_name = "CLOUDGUARD_UID"; // Replace with your environment variable name
|
||||||
const char *env_value = getenv(env_var_name);
|
const char *env_value = getenv(env_var_name);
|
||||||
|
|
||||||
if (env_value) {
|
if (env_value) {
|
||||||
|
@ -153,6 +153,47 @@ set_buffer_data(ngx_buf_t *buffer, const ngx_str_t *data)
|
|||||||
buffer->end = buffer->last;
|
buffer->end = buffer->last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Removes empty chunks from the specified NGINX chain.
|
||||||
|
/// @param[in,out] chain Pointer to the start of the chain to modify.
|
||||||
|
/// @param[in] pool NGINX pool used for allocating and freeing chain links.
|
||||||
|
/// @returns ngx_int_t
|
||||||
|
/// - #NGX_OK
|
||||||
|
/// - #NGX_ERROR
|
||||||
|
///
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_chain_remove_empty_chunks(ngx_chain_t **chain, ngx_pool_t *pool)
|
||||||
|
{
|
||||||
|
ngx_chain_t *prev = NULL;
|
||||||
|
ngx_chain_t *curr = *chain;
|
||||||
|
size_t chunk_num = 0;
|
||||||
|
|
||||||
|
while (curr != NULL) {
|
||||||
|
size_t size = curr->buf->last - curr->buf->pos;
|
||||||
|
if (size == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Removing empty chunk from the chain, chunk number: %d", chunk_num);
|
||||||
|
if (prev == NULL) {
|
||||||
|
*chain = curr->next;
|
||||||
|
} else {
|
||||||
|
prev->next = curr->next;
|
||||||
|
}
|
||||||
|
ngx_chain_t *tmp = curr;
|
||||||
|
curr = curr->next;
|
||||||
|
ngx_free_chain(pool, tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prev = curr;
|
||||||
|
curr = curr->next;
|
||||||
|
chunk_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk_num == 0) {
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Empty chain after removing empty chunks");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Decompresses or compresses the provided data.
|
/// @brief Decompresses or compresses the provided data.
|
||||||
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||||
@ -432,11 +473,17 @@ compress_chain(
|
|||||||
ngx_pool_t *pool
|
ngx_pool_t *pool
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
ngx_int_t compression_result;
|
||||||
ngx_cp_http_compression_params params;
|
ngx_cp_http_compression_params params;
|
||||||
params.compression_type = compression_type;
|
params.compression_type = compression_type;
|
||||||
params.is_last_part = is_last_part;
|
params.is_last_part = is_last_part;
|
||||||
|
|
||||||
return compression_chain_filter(1, compression_stream, NULL, body, original_body_contents, pool, ¶ms);
|
compression_result = compression_chain_filter(1, compression_stream, NULL, body, original_body_contents, pool, ¶ms);
|
||||||
|
// remove empty chunks from the chain to prevent getting nginx alert: "zero size buf in writer" down the line
|
||||||
|
if (compression_result == NGX_OK) {
|
||||||
|
compression_result = ngx_chain_remove_empty_chunks(body, pool);
|
||||||
|
}
|
||||||
|
return compression_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -534,6 +581,14 @@ compress_body(
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compression_type == BROTLI) {
|
||||||
|
// Brotli compression is not supported.
|
||||||
|
// This if statement serves a case that the compression type is set to BROTLI
|
||||||
|
// For now, we should not reach inside this function with a compression type of BROTLI.
|
||||||
|
write_dbg(DBG_LEVEL_WARNING, "Brotli compression is not supported");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_TRACE,
|
DBG_LEVEL_TRACE,
|
||||||
|
@ -73,6 +73,7 @@ decompress_body(
|
|||||||
/// @param[in] compression_type Compression type.
|
/// @param[in] compression_type Compression type.
|
||||||
/// - #GZIP
|
/// - #GZIP
|
||||||
/// - #ZLIB
|
/// - #ZLIB
|
||||||
|
/// - #BROTLI
|
||||||
/// - #NO_COMPRESSION - Serves as a sanity check in case this function is called
|
/// - #NO_COMPRESSION - Serves as a sanity check in case this function is called
|
||||||
/// on a compression type of data that isn't defined and will return NGX_ERROR.
|
/// on a compression type of data that isn't defined and will return NGX_ERROR.
|
||||||
/// @param[in] chunk_type Body chunk type:
|
/// @param[in] chunk_type Body chunk type:
|
||||||
|
@ -205,11 +205,11 @@ ngx_add_event_id_to_header(ngx_http_request_t *request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request)
|
ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request, int is_response_phase)
|
||||||
{
|
{
|
||||||
static u_char text_html[] = {'t', 'e', 'x', 't', '/', 'h', 't', 'm', 'l'};
|
static u_char text_html[] = {'t', 'e', 'x', 't', '/', 'h', 't', 'm', 'l'};
|
||||||
static size_t size_of_text_html = sizeof(text_html);
|
static size_t size_of_text_html = sizeof(text_html);
|
||||||
ngx_int_t http_res_code, rc;
|
ngx_int_t http_res_code, rc = NGX_OK;
|
||||||
ngx_table_elt_t *location_header;
|
ngx_table_elt_t *location_header;
|
||||||
ngx_chain_t out_chain[7]; // http://lxr.nginx.org/source/src/http/ngx_http_special_response.c#0772
|
ngx_chain_t out_chain[7]; // http://lxr.nginx.org/source/src/http/ngx_http_special_response.c#0772
|
||||||
int send_response_custom_body = 1;
|
int send_response_custom_body = 1;
|
||||||
@ -235,6 +235,12 @@ ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request)
|
|||||||
goto CUSTOM_RES_OUT;
|
goto CUSTOM_RES_OUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_response_phase) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Closing connection in response phase");
|
||||||
|
rc = NGX_HTTP_CLOSE;
|
||||||
|
goto CUSTOM_RES_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
if (get_response_code() == NGX_HTTP_TEMPORARY_REDIRECT) {
|
if (get_response_code() == NGX_HTTP_TEMPORARY_REDIRECT) {
|
||||||
// Handling redirect web response.
|
// Handling redirect web response.
|
||||||
write_dbg(
|
write_dbg(
|
||||||
@ -791,7 +797,7 @@ ngx_http_cp_body_modifier(
|
|||||||
if (curr_modification == NULL) return NGX_OK;
|
if (curr_modification == NULL) return NGX_OK;
|
||||||
if (curr_modification->modification.orig_buff_index != cur_body_chunk) continue;
|
if (curr_modification->modification.orig_buff_index != cur_body_chunk) continue;
|
||||||
|
|
||||||
cur_chunk_size = body_chain->buf->last - body_chain->buf->pos;
|
cur_chunk_size = chain_iter->buf->last - chain_iter->buf->pos;
|
||||||
if (cur_chunk_size == 0) {
|
if (cur_chunk_size == 0) {
|
||||||
write_dbg(DBG_LEVEL_TRACE, "No need to modify body chunk of size 0. Chunk index: %d", cur_body_chunk);
|
write_dbg(DBG_LEVEL_TRACE, "No need to modify body chunk of size 0. Chunk index: %d", cur_body_chunk);
|
||||||
continue;
|
continue;
|
||||||
|
@ -81,7 +81,7 @@ ngx_http_cp_file_response_sender(
|
|||||||
/// - #NGX_OK
|
/// - #NGX_OK
|
||||||
/// - #NGX_ERROR
|
/// - #NGX_ERROR
|
||||||
///
|
///
|
||||||
ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request);
|
ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request, int is_response_phase);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Modifies headers with the provided modifiers.
|
/// @brief Modifies headers with the provided modifiers.
|
||||||
|
@ -165,6 +165,7 @@ end_req_header_handler(
|
|||||||
return ngx_http_cp_reply_receiver(
|
return ngx_http_cp_reply_receiver(
|
||||||
&session_data_p->remaining_messages_to_reply,
|
&session_data_p->remaining_messages_to_reply,
|
||||||
&session_data_p->verdict,
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
request,
|
request,
|
||||||
modifications,
|
modifications,
|
||||||
@ -189,7 +190,7 @@ ngx_http_cp_req_header_handler_thread(void *_ctx)
|
|||||||
ngx_uint_t num_messages_sent = 0;
|
ngx_uint_t num_messages_sent = 0;
|
||||||
ngx_int_t send_header_result;
|
ngx_int_t send_header_result;
|
||||||
|
|
||||||
send_meta_data_result = ngx_http_cp_meta_data_sender(request, session_data_p->session_id, &num_messages_sent);
|
send_meta_data_result = ngx_http_cp_meta_data_sender(request, session_data_p->session_id, &num_messages_sent, &ctx->waf_tag);
|
||||||
if (send_meta_data_result == inspection_irrelevant) {
|
if (send_meta_data_result == inspection_irrelevant) {
|
||||||
// Ignoring irrelevant requests.
|
// Ignoring irrelevant requests.
|
||||||
session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
||||||
@ -223,8 +224,7 @@ ngx_http_cp_req_header_handler_thread(void *_ctx)
|
|||||||
&(request->headers_in.headers.part),
|
&(request->headers_in.headers.part),
|
||||||
REQUEST_HEADER,
|
REQUEST_HEADER,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
&num_messages_sent,
|
&num_messages_sent
|
||||||
&ctx->waf_tag
|
|
||||||
);
|
);
|
||||||
if (send_header_result != NGX_OK) {
|
if (send_header_result != NGX_OK) {
|
||||||
write_dbg(
|
write_dbg(
|
||||||
@ -281,6 +281,7 @@ ngx_http_cp_req_body_filter_thread(void *_ctx)
|
|||||||
ctx->res = ngx_http_cp_reply_receiver(
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
&session_data_p->remaining_messages_to_reply,
|
&session_data_p->remaining_messages_to_reply,
|
||||||
&session_data_p->verdict,
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
request,
|
request,
|
||||||
&ctx->modifications,
|
&ctx->modifications,
|
||||||
@ -322,6 +323,7 @@ ngx_http_cp_req_end_transaction_thread(void *_ctx)
|
|||||||
ctx->res = ngx_http_cp_reply_receiver(
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
&session_data_p->remaining_messages_to_reply,
|
&session_data_p->remaining_messages_to_reply,
|
||||||
&session_data_p->verdict,
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
request,
|
request,
|
||||||
&ctx->modifications,
|
&ctx->modifications,
|
||||||
@ -407,34 +409,36 @@ ngx_http_cp_res_header_filter_thread(void *_ctx)
|
|||||||
}
|
}
|
||||||
session_data_p->response_data.new_compression_type = session_data_p->response_data.original_compression_type;
|
session_data_p->response_data.new_compression_type = session_data_p->response_data.original_compression_type;
|
||||||
|
|
||||||
// Sends response headers to the nano service.
|
if (session_data_p->response_data.inspect_all_response_headers) {
|
||||||
num_messages_sent = 0;
|
// Sends response headers to the nano service.
|
||||||
send_header_result = ngx_http_cp_header_sender(
|
num_messages_sent = 0;
|
||||||
&request->headers_out.headers.part,
|
send_header_result = ngx_http_cp_header_sender(
|
||||||
RESPONSE_HEADER,
|
&request->headers_out.headers.part,
|
||||||
session_data_p->session_id,
|
RESPONSE_HEADER,
|
||||||
&num_messages_sent,
|
session_data_p->session_id,
|
||||||
&ctx->waf_tag
|
&num_messages_sent
|
||||||
);
|
|
||||||
if (send_header_result != NGX_OK) {
|
|
||||||
write_dbg(
|
|
||||||
DBG_LEVEL_WARNING,
|
|
||||||
"Failed to send response headers to the nano service. Session ID: %d",
|
|
||||||
session_data_p->session_id
|
|
||||||
);
|
);
|
||||||
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
if (send_header_result != NGX_OK) {
|
||||||
if (fail_mode_verdict == NGX_OK) {
|
write_dbg(
|
||||||
THREAD_CTX_RETURN_NEXT_FILTER();
|
DBG_LEVEL_WARNING,
|
||||||
|
"Failed to send response headers to the nano service. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
||||||
}
|
}
|
||||||
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch nano services' results.
|
// Fetch nano services' results.
|
||||||
ctx->res = ngx_http_cp_reply_receiver(
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
&session_data_p->remaining_messages_to_reply,
|
&session_data_p->remaining_messages_to_reply,
|
||||||
&session_data_p->verdict,
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
request,
|
request,
|
||||||
&ctx->modifications,
|
&ctx->modifications,
|
||||||
@ -455,7 +459,13 @@ ngx_http_cp_res_body_filter_thread(void *_ctx)
|
|||||||
ngx_uint_t num_messages_sent = 0;
|
ngx_uint_t num_messages_sent = 0;
|
||||||
ngx_int_t is_last_response_part = 0;
|
ngx_int_t is_last_response_part = 0;
|
||||||
|
|
||||||
// Send response body data to the nano service.
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Starting response body filter thread for session ID: %d, current verdict: %d",
|
||||||
|
session_data_p->session_id,
|
||||||
|
session_data_p->verdict
|
||||||
|
);
|
||||||
|
|
||||||
send_body_result = ngx_http_cp_body_sender(
|
send_body_result = ngx_http_cp_body_sender(
|
||||||
ctx->chain,
|
ctx->chain,
|
||||||
RESPONSE_BODY,
|
RESPONSE_BODY,
|
||||||
@ -464,6 +474,16 @@ ngx_http_cp_res_body_filter_thread(void *_ctx)
|
|||||||
&num_messages_sent,
|
&num_messages_sent,
|
||||||
&ctx->chain
|
&ctx->chain
|
||||||
);
|
);
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Body sender result: %d, num_messages_sent: %d, is_last_response_part: %d for session ID: %d",
|
||||||
|
send_body_result,
|
||||||
|
num_messages_sent,
|
||||||
|
is_last_response_part,
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
|
||||||
if (send_body_result != NGX_OK) {
|
if (send_body_result != NGX_OK) {
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_WARNING,
|
DBG_LEVEL_WARNING,
|
||||||
@ -478,9 +498,48 @@ ngx_http_cp_res_body_filter_thread(void *_ctx)
|
|||||||
}
|
}
|
||||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
|
||||||
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
&ctx->modifications,
|
||||||
|
RESPONSE_BODY,
|
||||||
|
session_data_p->processed_res_body_size
|
||||||
|
);
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Reply receiver returned: %d, got verdict: %d, remaining messages: %d for session ID: %d",
|
||||||
|
ctx->res,
|
||||||
|
session_data_p->verdict,
|
||||||
|
session_data_p->remaining_messages_to_reply,
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) {
|
||||||
|
if (!ngx_http_cp_hold_verdict(ctx)) {
|
||||||
|
session_data_p->verdict = fail_mode_hold_verdict == NGX_OK ? TRAFFIC_VERDICT_ACCEPT : TRAFFIC_VERDICT_DROP;
|
||||||
|
updateMetricField(HOLD_THREAD_TIMEOUT, 1);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
num_messages_sent = 0;
|
num_messages_sent = 0;
|
||||||
if (is_last_response_part) {
|
if (is_last_response_part) {
|
||||||
// Signals the nano service that the transaction reached the end.
|
// Signals the nano service that the transaction reached the end.
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"This is the last response part, sending RESPONSE_END signal for session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
|
||||||
if (ngx_http_cp_end_transaction_sender(RESPONSE_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
if (ngx_http_cp_end_transaction_sender(RESPONSE_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_WARNING,
|
DBG_LEVEL_WARNING,
|
||||||
@ -493,19 +552,51 @@ ngx_http_cp_res_body_filter_thread(void *_ctx)
|
|||||||
}
|
}
|
||||||
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
||||||
}
|
}
|
||||||
session_data_p->remaining_messages_to_reply++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch nano services' results.
|
if (
|
||||||
ctx->res = ngx_http_cp_reply_receiver(
|
session_data_p->verdict == TRAFFIC_VERDICT_ACCEPT ||
|
||||||
&session_data_p->remaining_messages_to_reply,
|
session_data_p->verdict == TRAFFIC_VERDICT_DROP
|
||||||
&session_data_p->verdict,
|
) {
|
||||||
session_data_p->session_id,
|
write_dbg(
|
||||||
request,
|
DBG_LEVEL_DEBUG,
|
||||||
&ctx->modifications,
|
"Will not wait for verdict after RESPONSE_END as we already have verdict %d for session ID %d",
|
||||||
RESPONSE_BODY,
|
session_data_p->session_id,
|
||||||
session_data_p->processed_res_body_size
|
session_data_p->verdict
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||||
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
|
&session_data_p->remaining_messages_to_reply,
|
||||||
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
|
session_data_p->session_id,
|
||||||
|
request,
|
||||||
|
&ctx->modifications,
|
||||||
|
RESPONSE_END,
|
||||||
|
session_data_p->processed_res_body_size
|
||||||
|
);
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Received verdict %d after RESPONSE_END for session ID %d, will proceed normally",
|
||||||
|
session_data_p->verdict,
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) {
|
||||||
|
if (!ngx_http_cp_hold_verdict(ctx)) {
|
||||||
|
session_data_p->verdict = fail_mode_hold_verdict == NGX_OK ? TRAFFIC_VERDICT_ACCEPT : TRAFFIC_VERDICT_DROP;
|
||||||
|
updateMetricField(HOLD_THREAD_TIMEOUT, 1);
|
||||||
|
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||||
|
if (fail_mode_verdict == NGX_OK) {
|
||||||
|
THREAD_CTX_RETURN_NEXT_FILTER();
|
||||||
|
}
|
||||||
|
THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -537,6 +628,7 @@ ngx_http_cp_hold_verdict_thread(void *_ctx)
|
|||||||
ctx->res = ngx_http_cp_reply_receiver(
|
ctx->res = ngx_http_cp_reply_receiver(
|
||||||
&session_data_p->remaining_messages_to_reply,
|
&session_data_p->remaining_messages_to_reply,
|
||||||
&session_data_p->verdict,
|
&session_data_p->verdict,
|
||||||
|
&session_data_p->response_data.inspect_all_response_headers,
|
||||||
session_data_p->session_id,
|
session_data_p->session_id,
|
||||||
request,
|
request,
|
||||||
&ctx->modifications,
|
&ctx->modifications,
|
||||||
|
@ -78,6 +78,7 @@ init_cp_session_data(ngx_http_request_t *request)
|
|||||||
session_data->response_data.response_data_status = NGX_OK;
|
session_data->response_data.response_data_status = NGX_OK;
|
||||||
session_data->response_data.original_compressed_body = NULL;
|
session_data->response_data.original_compressed_body = NULL;
|
||||||
session_data->response_data.request_pool = NULL;
|
session_data->response_data.request_pool = NULL;
|
||||||
|
session_data->response_data.inspect_all_response_headers = 1;
|
||||||
if (!metric_timeout.tv_sec) {
|
if (!metric_timeout.tv_sec) {
|
||||||
metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL);
|
metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL);
|
||||||
}
|
}
|
||||||
@ -86,6 +87,7 @@ init_cp_session_data(ngx_http_request_t *request)
|
|||||||
session_data->res_proccesing_time = 0;
|
session_data->res_proccesing_time = 0;
|
||||||
session_data->processed_req_body_size = 0;
|
session_data->processed_req_body_size = 0;
|
||||||
session_data->processed_res_body_size = 0;
|
session_data->processed_res_body_size = 0;
|
||||||
|
session_data->is_res_body_inspected = 0;
|
||||||
|
|
||||||
ngx_http_set_ctx(request, session_data, ngx_http_cp_attachment_module);
|
ngx_http_set_ctx(request, session_data, ngx_http_cp_attachment_module);
|
||||||
|
|
||||||
@ -334,7 +336,7 @@ ngx_http_cp_finalize_request_headers_hook(
|
|||||||
|
|
||||||
if (final_res == NGX_HTTP_FORBIDDEN) {
|
if (final_res == NGX_HTTP_FORBIDDEN) {
|
||||||
handle_inspection_success(session_data_p);
|
handle_inspection_success(session_data_p);
|
||||||
return ngx_http_cp_finalize_rejected_request(request);
|
return ngx_http_cp_finalize_rejected_request(request, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final_res != NGX_OK) {
|
if (final_res != NGX_OK) {
|
||||||
@ -715,6 +717,15 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo
|
|||||||
return fail_mode_verdict == NGX_OK ? ngx_http_next_request_body_filter(request, request_body_chain) : NGX_HTTP_FORBIDDEN;
|
return fail_mode_verdict == NGX_OK ? ngx_http_next_request_body_filter(request, request_body_chain) : NGX_HTTP_FORBIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "spawn ngx_http_cp_hold_verdict");
|
||||||
|
res = ngx_http_cp_hold_verdict(&ctx);
|
||||||
|
if (!res) {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "ngx_http_cp_hold_verdict failed");
|
||||||
|
updateMetricField(HOLD_THREAD_TIMEOUT, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_DEBUG,
|
DBG_LEVEL_DEBUG,
|
||||||
"finished ngx_http_cp_req_end_transaction_thread successfully. return=%d next_filter=%d res=%d",
|
"finished ngx_http_cp_req_end_transaction_thread successfully. return=%d next_filter=%d res=%d",
|
||||||
@ -754,7 +765,7 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo
|
|||||||
|
|
||||||
if (final_res == NGX_HTTP_FORBIDDEN) {
|
if (final_res == NGX_HTTP_FORBIDDEN) {
|
||||||
handle_inspection_success(session_data_p);
|
handle_inspection_success(session_data_p);
|
||||||
return ngx_http_cp_finalize_rejected_request(request);
|
return ngx_http_cp_finalize_rejected_request(request, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final_res != NGX_OK) {
|
if (final_res != NGX_OK) {
|
||||||
@ -934,7 +945,7 @@ ngx_http_cp_res_header_filter(ngx_http_request_t *request)
|
|||||||
|
|
||||||
if (final_res == NGX_HTTP_FORBIDDEN) {
|
if (final_res == NGX_HTTP_FORBIDDEN) {
|
||||||
handle_inspection_success(session_data_p);
|
handle_inspection_success(session_data_p);
|
||||||
return ngx_http_cp_finalize_rejected_request(request);
|
return ngx_http_cp_finalize_rejected_request(request, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final_res != NGX_OK) {
|
if (final_res != NGX_OK) {
|
||||||
@ -1005,23 +1016,48 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
if (session_data_p->response_data.response_data_status != NGX_OK) {
|
if (session_data_p->response_data.response_data_status != NGX_OK) {
|
||||||
write_dbg(DBG_LEVEL_WARNING, "skipping session with corrupted compression");
|
write_dbg(DBG_LEVEL_WARNING, "skipping session with corrupted compression");
|
||||||
updateMetricField(CORRUPTED_ZIP_SKIPPED_SESSION_COUNT, 1);
|
updateMetricField(CORRUPTED_ZIP_SKIPPED_SESSION_COUNT, 1);
|
||||||
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) request->keepalive = 0;
|
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) {
|
||||||
|
request->keepalive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP && session_data_p->is_res_body_inspected) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Session with corrupted compression has DROP verdict, returning HTTP_FORBIDDEN. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
return NGX_HTTP_FORBIDDEN;
|
||||||
|
}
|
||||||
return ngx_http_next_response_body_filter(request, body_chain);
|
return ngx_http_next_response_body_filter(request, body_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
session_data_p->verdict != TRAFFIC_VERDICT_INSPECT &&
|
session_data_p->verdict != TRAFFIC_VERDICT_INSPECT &&
|
||||||
|
session_data_p->verdict != TRAFFIC_VERDICT_WAIT &&
|
||||||
(
|
(
|
||||||
session_data_p->verdict != TRAFFIC_VERDICT_ACCEPT ||
|
session_data_p->verdict != TRAFFIC_VERDICT_ACCEPT ||
|
||||||
session_data_p->response_data.new_compression_type == NO_COMPRESSION ||
|
session_data_p->response_data.new_compression_type == NO_COMPRESSION ||
|
||||||
|
session_data_p->response_data.new_compression_type == BROTLI ||
|
||||||
session_data_p->response_data.num_body_chunk == 0
|
session_data_p->response_data.num_body_chunk == 0
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
write_dbg(DBG_LEVEL_TRACE, "skipping already inspected session");
|
write_dbg(DBG_LEVEL_TRACE, "skipping already inspected session");
|
||||||
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) request->keepalive = 0;
|
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) {
|
||||||
|
request->keepalive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP && session_data_p->is_res_body_inspected) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Session has DROP verdict, returning HTTP_FORBIDDEN instead of streaming. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
return NGX_HTTP_FORBIDDEN;
|
||||||
|
}
|
||||||
return ngx_http_next_response_body_filter(request, body_chain);
|
return ngx_http_next_response_body_filter(request, body_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session_data_p->is_res_body_inspected = 1;
|
||||||
session_data_p->response_data.num_body_chunk++;
|
session_data_p->response_data.num_body_chunk++;
|
||||||
|
|
||||||
if (body_chain == NULL) {
|
if (body_chain == NULL) {
|
||||||
@ -1033,7 +1069,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
return ngx_http_next_response_body_filter(request, body_chain);
|
return ngx_http_next_response_body_filter(request, body_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body_chain->buf->pos != NULL && session_data_p->response_data.new_compression_type != NO_COMPRESSION) {
|
if (body_chain->buf->pos != NULL && session_data_p->response_data.new_compression_type != NO_COMPRESSION && session_data_p->response_data.new_compression_type != BROTLI) {
|
||||||
write_dbg(DBG_LEVEL_TRACE, "Decompressing response body");
|
write_dbg(DBG_LEVEL_TRACE, "Decompressing response body");
|
||||||
if (init_cp_session_original_body(session_data_p, request->pool) == NGX_OK) {
|
if (init_cp_session_original_body(session_data_p, request->pool) == NGX_OK) {
|
||||||
if (session_data_p->response_data.decompression_stream == NULL) {
|
if (session_data_p->response_data.decompression_stream == NULL) {
|
||||||
@ -1208,7 +1244,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
|
|
||||||
if (final_res == NGX_HTTP_FORBIDDEN) {
|
if (final_res == NGX_HTTP_FORBIDDEN) {
|
||||||
handle_inspection_success(session_data_p);
|
handle_inspection_success(session_data_p);
|
||||||
return ngx_http_cp_finalize_rejected_request(request);
|
return ngx_http_cp_finalize_rejected_request(request, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final_res != NGX_OK) {
|
if (final_res != NGX_OK) {
|
||||||
@ -1224,7 +1260,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
return NGX_HTTP_FORBIDDEN;
|
return NGX_HTTP_FORBIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.modifications) {
|
if (ctx.modifications && session_data_p->response_data.new_compression_type != BROTLI) {
|
||||||
write_dbg(DBG_LEVEL_TRACE, "Handling response body modification");
|
write_dbg(DBG_LEVEL_TRACE, "Handling response body modification");
|
||||||
if (ngx_http_cp_body_modifier(body_chain, ctx.modifications, request->pool) != NGX_OK) {
|
if (ngx_http_cp_body_modifier(body_chain, ctx.modifications, request->pool) != NGX_OK) {
|
||||||
write_dbg(DBG_LEVEL_WARNING, "Failed to modify response body");
|
write_dbg(DBG_LEVEL_WARNING, "Failed to modify response body");
|
||||||
@ -1237,6 +1273,16 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.modifications && session_data_p->response_data.new_compression_type == BROTLI) {
|
||||||
|
ngx_http_cp_modification_list *mod = ctx.modifications;
|
||||||
|
while (mod != NULL) {
|
||||||
|
ngx_http_cp_modification_list *next_mod = mod->next;
|
||||||
|
ngx_pfree(request->pool, mod);
|
||||||
|
mod = next_mod;
|
||||||
|
}
|
||||||
|
ctx.modifications = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
session_data_p->verdict == TRAFFIC_VERDICT_ACCEPT &&
|
session_data_p->verdict == TRAFFIC_VERDICT_ACCEPT &&
|
||||||
session_data_p->response_data.num_body_chunk == 1 &&
|
session_data_p->response_data.num_body_chunk == 1 &&
|
||||||
@ -1249,7 +1295,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
return ngx_http_next_response_body_filter(request, body_chain);
|
return ngx_http_next_response_body_filter(request, body_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_data_p->response_data.new_compression_type != NO_COMPRESSION) {
|
if (session_data_p->response_data.new_compression_type != NO_COMPRESSION && session_data_p->response_data.new_compression_type != BROTLI) {
|
||||||
if (session_data_p->response_data.compression_stream == NULL) {
|
if (session_data_p->response_data.compression_stream == NULL) {
|
||||||
session_data_p->response_data.compression_stream = initCompressionStream();
|
session_data_p->response_data.compression_stream = initCompressionStream();
|
||||||
}
|
}
|
||||||
@ -1275,5 +1321,23 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain
|
|||||||
|
|
||||||
print_buffer_chain(body_chain, "outgoing", 32, DBG_LEVEL_TRACE);
|
print_buffer_chain(body_chain, "outgoing", 32, DBG_LEVEL_TRACE);
|
||||||
|
|
||||||
|
// Check final verdict before streaming data to client
|
||||||
|
// This prevents malicious data from reaching client when verdict is DROP
|
||||||
|
if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) {
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Final verdict is DROP, blocking stream to client. Session ID: %d",
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
return NGX_HTTP_FORBIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Final verdict is %d, streaming to client. Session ID: %d",
|
||||||
|
session_data_p->verdict,
|
||||||
|
session_data_p->session_id
|
||||||
|
);
|
||||||
|
|
||||||
return ngx_http_next_response_body_filter(request, body_chain);
|
return ngx_http_next_response_body_filter(request, body_chain);
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ typedef struct ngx_http_cp_session_data {
|
|||||||
double res_proccesing_time; ///< Holds session's response processing time.
|
double res_proccesing_time; ///< Holds session's response processing time.
|
||||||
uint64_t processed_req_body_size; ///< Holds session's request body's size.
|
uint64_t processed_req_body_size; ///< Holds session's request body's size.
|
||||||
uint64_t processed_res_body_size; ///< Holds session's response body's size'.
|
uint64_t processed_res_body_size; ///< Holds session's response body's size'.
|
||||||
|
ngx_int_t is_res_body_inspected; ///< Holds if the response body was inspected
|
||||||
} ngx_http_cp_session_data;
|
} ngx_http_cp_session_data;
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -19,24 +19,31 @@
|
|||||||
|
|
||||||
static const char *gzip_encoding_string = "gzip";
|
static const char *gzip_encoding_string = "gzip";
|
||||||
static const char *zlib_encoding_string = "deflate";
|
static const char *zlib_encoding_string = "deflate";
|
||||||
|
static const char *brotli_encoding_string = "br";
|
||||||
static const char *identity_encoding_string = "identity";
|
static const char *identity_encoding_string = "identity";
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
parse_content_encoding(CompressionType *response_encoding, const ngx_str_t *content_encoding_header_value)
|
parse_content_encoding(CompressionType *response_encoding, const ngx_str_t *content_encoding_header_value)
|
||||||
{
|
{
|
||||||
if (ngx_strcmp(content_encoding_header_value->data, gzip_encoding_string) == 0) {
|
if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)gzip_encoding_string) == 0) {
|
||||||
/// Sets GZIP encoding.
|
/// Sets GZIP encoding.
|
||||||
*response_encoding = GZIP;
|
*response_encoding = GZIP;
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_strcmp(content_encoding_header_value->data, zlib_encoding_string) == 0) {
|
if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)zlib_encoding_string) == 0) {
|
||||||
/// Sets GZIP encoding.
|
/// Sets GZIP encoding.
|
||||||
*response_encoding = ZLIB;
|
*response_encoding = ZLIB;
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_strcmp(content_encoding_header_value->data, identity_encoding_string) == 0) {
|
if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)brotli_encoding_string) == 0) {
|
||||||
|
/// Sets Brotli encoding.
|
||||||
|
*response_encoding = BROTLI;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)identity_encoding_string) == 0) {
|
||||||
/// Sets NO_COMPRESSION encoding.
|
/// Sets NO_COMPRESSION encoding.
|
||||||
*response_encoding = NO_COMPRESSION;
|
*response_encoding = NO_COMPRESSION;
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
@ -30,6 +30,11 @@ typedef struct {
|
|||||||
/// - #NGX_ERROR
|
/// - #NGX_ERROR
|
||||||
ngx_int_t response_data_status;
|
ngx_int_t response_data_status;
|
||||||
|
|
||||||
|
/// This flag determines whether all response headers should be sent to a service for inspection.
|
||||||
|
/// - If set to 1, all response headers will be sent.
|
||||||
|
/// - If set to 0, only the Content-Length, response code, and encoding checks for the response body will be sent.
|
||||||
|
ngx_int_t inspect_all_response_headers;
|
||||||
|
|
||||||
/// Original compression type, can hold the following values:
|
/// Original compression type, can hold the following values:
|
||||||
/// - #GZIP
|
/// - #GZIP
|
||||||
/// - #ZLIB
|
/// - #ZLIB
|
||||||
@ -58,6 +63,7 @@ typedef struct {
|
|||||||
/// @param[in, out] response_encoding Returns value of one of the supported encoding:
|
/// @param[in, out] response_encoding Returns value of one of the supported encoding:
|
||||||
/// - #GZIP
|
/// - #GZIP
|
||||||
/// - #ZLIB
|
/// - #ZLIB
|
||||||
|
/// - #BROTLI
|
||||||
/// - #NO_COMPRESSION
|
/// - #NO_COMPRESSION
|
||||||
/// @param[in, out] content_encoding_header_value Encoded value.
|
/// @param[in, out] content_encoding_header_value Encoded value.
|
||||||
/// @return ngx_int_t
|
/// @return ngx_int_t
|
||||||
@ -75,6 +81,7 @@ parse_content_encoding(
|
|||||||
/// @param[in, out] content_encoding Returns variable of one of the supported encoding:
|
/// @param[in, out] content_encoding Returns variable of one of the supported encoding:
|
||||||
/// - #GZIP
|
/// - #GZIP
|
||||||
/// - #ZLIB
|
/// - #ZLIB
|
||||||
|
/// - #BROTLI
|
||||||
/// - #NO_COMPRESSION
|
/// - #NO_COMPRESSION
|
||||||
/// @param[in, out] content_encoding_header NGINX table Encoding header.
|
/// @param[in, out] content_encoding_header NGINX table Encoding header.
|
||||||
/// @return ngx_int_t
|
/// @return ngx_int_t
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "ngx_cp_utils.h"
|
#include "ngx_cp_utils.h"
|
||||||
#include "ngx_cp_initializer.h"
|
#include "ngx_cp_initializer.h"
|
||||||
@ -452,6 +453,7 @@ ngx_int_t
|
|||||||
ngx_http_cp_reply_receiver(
|
ngx_http_cp_reply_receiver(
|
||||||
ngx_int_t *expected_replies,
|
ngx_int_t *expected_replies,
|
||||||
ngx_http_cp_verdict_e *verdict,
|
ngx_http_cp_verdict_e *verdict,
|
||||||
|
ngx_int_t *inspect_all_response_headers,
|
||||||
uint32_t cur_session_id,
|
uint32_t cur_session_id,
|
||||||
ngx_http_request_t *request,
|
ngx_http_request_t *request,
|
||||||
ngx_http_cp_modification_list **modification_list,
|
ngx_http_cp_modification_list **modification_list,
|
||||||
@ -492,7 +494,7 @@ ngx_http_cp_reply_receiver(
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply_p->verdict != TRAFFIC_VERDICT_RECONF) {
|
if (reply_p->verdict != TRAFFIC_VERDICT_RECONF && reply_p->verdict != LIMIT_RESPONSE_HEADERS) {
|
||||||
// Handling reconfiguration verdict.
|
// Handling reconfiguration verdict.
|
||||||
if (reply_p->session_id != cur_session_id) {
|
if (reply_p->session_id != cur_session_id) {
|
||||||
write_dbg(DBG_LEVEL_DEBUG, "Ignoring verdict to an already handled request %d", reply_p->session_id);
|
write_dbg(DBG_LEVEL_DEBUG, "Ignoring verdict to an already handled request %d", reply_p->session_id);
|
||||||
@ -600,6 +602,12 @@ ngx_http_cp_reply_receiver(
|
|||||||
updateMetricField(HOLD_VERDICTS_COUNT, 1);
|
updateMetricField(HOLD_VERDICTS_COUNT, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case LIMIT_RESPONSE_HEADERS: {
|
||||||
|
write_dbg(DBG_LEVEL_DEBUG, "Verdict ignore response headers received from the nano service");
|
||||||
|
*inspect_all_response_headers = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free_data_from_service();
|
free_data_from_service();
|
||||||
@ -662,7 +670,7 @@ convert_sock_addr_to_string(const struct sockaddr *sa, char *ip_addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_id, ngx_uint_t *num_messages_sent)
|
ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_id, ngx_uint_t *num_messages_sent, ngx_str_t *waf_tag)
|
||||||
{
|
{
|
||||||
char client_ip[INET6_ADDRSTRLEN];
|
char client_ip[INET6_ADDRSTRLEN];
|
||||||
char listening_ip[INET6_ADDRSTRLEN];
|
char listening_ip[INET6_ADDRSTRLEN];
|
||||||
@ -788,6 +796,16 @@ ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_i
|
|||||||
set_fragment_elem(fragments, fragments_sizes, &parsed_uri.len, sizeof(uint16_t), PARSED_URI_SIZE + 2);
|
set_fragment_elem(fragments, fragments_sizes, &parsed_uri.len, sizeof(uint16_t), PARSED_URI_SIZE + 2);
|
||||||
set_fragment_elem(fragments, fragments_sizes, parsed_uri.data, parsed_uri.len, PARSED_URI_DATA + 2);
|
set_fragment_elem(fragments, fragments_sizes, parsed_uri.data, parsed_uri.len, PARSED_URI_DATA + 2);
|
||||||
|
|
||||||
|
// Add WAF tag data if provided
|
||||||
|
if (waf_tag != NULL && waf_tag->len > 0) {
|
||||||
|
set_fragment_elem(fragments, fragments_sizes, &waf_tag->len, sizeof(uint16_t), WAF_TAG_SIZE + 2);
|
||||||
|
set_fragment_elem(fragments, fragments_sizes, waf_tag->data, waf_tag->len, WAF_TAG_DATA + 2);
|
||||||
|
} else {
|
||||||
|
uint16_t zero = 0;
|
||||||
|
set_fragment_elem(fragments, fragments_sizes, &zero, sizeof(uint16_t), WAF_TAG_SIZE + 2);
|
||||||
|
set_fragment_elem(fragments, fragments_sizes, "", 0, WAF_TAG_DATA + 2);
|
||||||
|
}
|
||||||
|
|
||||||
// Sends all the data to the nano service.
|
// Sends all the data to the nano service.
|
||||||
res = ngx_http_cp_send_data_to_service(fragments, fragments_sizes, META_DATA_COUNT + 2, cur_request_id, NULL, fail_open_timeout, min_retries_for_verdict);
|
res = ngx_http_cp_send_data_to_service(fragments, fragments_sizes, META_DATA_COUNT + 2, cur_request_id, NULL, fail_open_timeout, min_retries_for_verdict);
|
||||||
if (res != NGX_OK) {
|
if (res != NGX_OK) {
|
||||||
@ -1008,8 +1026,7 @@ ngx_http_cp_header_sender(
|
|||||||
ngx_list_part_t *headers_list,
|
ngx_list_part_t *headers_list,
|
||||||
ngx_http_chunk_type_e header_type,
|
ngx_http_chunk_type_e header_type,
|
||||||
uint32_t cur_request_id,
|
uint32_t cur_request_id,
|
||||||
ngx_uint_t *num_messages_sent,
|
ngx_uint_t *num_messages_sent
|
||||||
ngx_str_t *waf_tag
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ngx_uint_t header_idx = 0;
|
ngx_uint_t header_idx = 0;
|
||||||
@ -1025,7 +1042,6 @@ ngx_http_cp_header_sender(
|
|||||||
const ngx_uint_t max_bulk_size = 10;
|
const ngx_uint_t max_bulk_size = 10;
|
||||||
char *fragments[HEADER_DATA_COUNT * max_bulk_size + 4];
|
char *fragments[HEADER_DATA_COUNT * max_bulk_size + 4];
|
||||||
uint16_t fragments_sizes[HEADER_DATA_COUNT * max_bulk_size + 4];
|
uint16_t fragments_sizes[HEADER_DATA_COUNT * max_bulk_size + 4];
|
||||||
ngx_flag_t waf_tag_found = 0;
|
|
||||||
|
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_TRACE,
|
DBG_LEVEL_TRACE,
|
||||||
@ -1036,38 +1052,6 @@ ngx_http_cp_header_sender(
|
|||||||
// Sets fragments identifier to the provided body type.
|
// Sets fragments identifier to the provided body type.
|
||||||
set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&header_type, &cur_request_id);
|
set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&header_type, &cur_request_id);
|
||||||
|
|
||||||
// If waf_tag is provided and valid, check for existing x-waf-tag headers
|
|
||||||
if (waf_tag != NULL && waf_tag->len > 0) {
|
|
||||||
for (headers_iter = headers_list; headers_iter; headers_iter = headers_iter->next) {
|
|
||||||
headers_to_inspect = headers_iter->elts;
|
|
||||||
for (header_idx = 0; header_idx < headers_iter->nelts; ++header_idx) {
|
|
||||||
header = headers_to_inspect + header_idx;
|
|
||||||
if (header->key.len == 9 && ngx_strncasecmp(header->key.data, (u_char *)"x-waf-tag", 9) == 0) {
|
|
||||||
// Found existing x-waf-tag header, override its value
|
|
||||||
// header->value = *waf_tag;
|
|
||||||
waf_tag_found = 1;
|
|
||||||
write_dbg(DBG_LEVEL_DEBUG, "Overriding existing x-waf-tag header with value: %.*s", waf_tag->len, waf_tag->data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (waf_tag_found) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no existing x-waf-tag header found, add a new one
|
|
||||||
if (!waf_tag_found) {
|
|
||||||
ngx_table_elt_t waf_header;
|
|
||||||
waf_header.hash = 1;
|
|
||||||
ngx_str_set(&waf_header.key, "x-waf-tag");
|
|
||||||
waf_header.value = *waf_tag;
|
|
||||||
waf_header.lowcase_key = NULL; // Not needed for sending to agent
|
|
||||||
|
|
||||||
add_header_to_bulk(fragments, fragments_sizes, &waf_header, idx_in_bulk);
|
|
||||||
idx_in_bulk++;
|
|
||||||
part_count++;
|
|
||||||
write_dbg(DBG_LEVEL_DEBUG, "Adding new x-waf-tag header with value: %.*s", waf_tag->len, waf_tag->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (headers_iter = headers_list; headers_iter ; headers_iter = headers_iter->next) {
|
for (headers_iter = headers_list; headers_iter ; headers_iter = headers_iter->next) {
|
||||||
// Going over the header list.
|
// Going over the header list.
|
||||||
for (header_idx = 0 ; header_idx < headers_iter->nelts ; ++header_idx) {
|
for (header_idx = 0 ; header_idx < headers_iter->nelts ; ++header_idx) {
|
||||||
@ -1085,16 +1069,7 @@ ngx_http_cp_header_sender(
|
|||||||
|
|
||||||
is_last_part = (headers_iter->next == NULL && header_idx + 1 == headers_iter->nelts) ? 1 : 0;
|
is_last_part = (headers_iter->next == NULL && header_idx + 1 == headers_iter->nelts) ? 1 : 0;
|
||||||
// Create a header bulk to send.
|
// Create a header bulk to send.
|
||||||
if (waf_tag_found && header->key.len == 9 && ngx_strncasecmp(header->key.data, (u_char *)"x-waf-tag", 9) == 0) {
|
add_header_to_bulk(fragments, fragments_sizes, header, idx_in_bulk);
|
||||||
ngx_table_elt_t waf_header;
|
|
||||||
waf_header.hash = 1;
|
|
||||||
ngx_str_set(&waf_header.key, "x-waf-tag");
|
|
||||||
waf_header.value = *waf_tag;
|
|
||||||
waf_header.lowcase_key = NULL;
|
|
||||||
add_header_to_bulk(fragments, fragments_sizes, &waf_header, idx_in_bulk);
|
|
||||||
} else {
|
|
||||||
add_header_to_bulk(fragments, fragments_sizes, header, idx_in_bulk);
|
|
||||||
}
|
|
||||||
|
|
||||||
idx_in_bulk++;
|
idx_in_bulk++;
|
||||||
part_count++;
|
part_count++;
|
||||||
@ -1152,16 +1127,20 @@ ngx_http_cp_body_sender(
|
|||||||
ngx_int_t res = NGX_ERROR;
|
ngx_int_t res = NGX_ERROR;
|
||||||
uint8_t is_last_chunk;
|
uint8_t is_last_chunk;
|
||||||
uint8_t part_count;
|
uint8_t part_count;
|
||||||
|
size_t buf_size;
|
||||||
ngx_int_t tout_retries = min_retries_for_verdict;
|
ngx_int_t tout_retries = min_retries_for_verdict;
|
||||||
char *fragments[num_body_chunk_fragments];
|
char *fragments[num_body_chunk_fragments];
|
||||||
uint16_t fragments_sizes[num_body_chunk_fragments];
|
uint16_t fragments_sizes[num_body_chunk_fragments];
|
||||||
int was_waiting = 0;
|
int was_waiting = 0;
|
||||||
|
int max_chunks_to_process = (body_type == RESPONSE_BODY) ? 1 : INT_MAX;
|
||||||
|
int chunks_processed = 0;
|
||||||
|
|
||||||
write_dbg(
|
write_dbg(
|
||||||
DBG_LEVEL_TRACE,
|
DBG_LEVEL_DEBUG,
|
||||||
"Sending %s body chunk from session id %d for inspection",
|
"Started %s body sender for session ID %d, max_chunks_to_process: %d",
|
||||||
body_type == REQUEST_BODY ? "request" : "response",
|
body_type == REQUEST_BODY ? "request" : "response",
|
||||||
session_data->session_id
|
session_data->session_id,
|
||||||
|
max_chunks_to_process
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sets fragments identifier to the provided body type.
|
// Sets fragments identifier to the provided body type.
|
||||||
@ -1169,13 +1148,24 @@ ngx_http_cp_body_sender(
|
|||||||
|
|
||||||
num_parts_sent = 0;
|
num_parts_sent = 0;
|
||||||
part_count = 0;
|
part_count = 0;
|
||||||
for (chain_iter = input; chain_iter; chain_iter = chain_iter->next) {
|
|
||||||
|
for (chain_iter = input; chain_iter && chunks_processed < max_chunks_to_process; chain_iter = chain_iter->next) {
|
||||||
// For each NGINX buffer, fragment the buffer and then send the fragments to the nano service.
|
// For each NGINX buffer, fragment the buffer and then send the fragments to the nano service.
|
||||||
buf = chain_iter->buf;
|
buf = chain_iter->buf;
|
||||||
is_last_chunk = buf->last_buf ? 1 : 0;
|
is_last_chunk = buf->last_buf ? 1 : 0;
|
||||||
write_dbg(DBG_LEVEL_TRACE, "Sending last_buf: %d, part_count: %d", buf->last_buf ? 1: 0, part_count);
|
buf_size = buf->last - buf->pos;
|
||||||
|
|
||||||
if (buf->last - buf->pos > 0 || is_last_chunk) {
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Processing %s body chunk %d of size: %d, last_chunk: %d for session ID: %d",
|
||||||
|
body_type == REQUEST_BODY ? "request" : "response",
|
||||||
|
part_count,
|
||||||
|
buf_size,
|
||||||
|
is_last_chunk,
|
||||||
|
session_data->session_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (buf_size > 0 || is_last_chunk) {
|
||||||
// Setting the fragments, including in the case of the last chunk.
|
// Setting the fragments, including in the case of the last chunk.
|
||||||
set_fragment_elem(fragments, fragments_sizes, &is_last_chunk, sizeof(is_last_chunk), 2);
|
set_fragment_elem(fragments, fragments_sizes, &is_last_chunk, sizeof(is_last_chunk), 2);
|
||||||
set_fragment_elem(fragments, fragments_sizes, &part_count, sizeof(part_count), 3);
|
set_fragment_elem(fragments, fragments_sizes, &part_count, sizeof(part_count), 3);
|
||||||
@ -1191,16 +1181,32 @@ ngx_http_cp_body_sender(
|
|||||||
tout_retries = max_retries_for_verdict;
|
tout_retries = max_retries_for_verdict;
|
||||||
}
|
}
|
||||||
// Sending the data to the nano service.
|
// Sending the data to the nano service.
|
||||||
res = ngx_http_cp_send_data_to_service(fragments, fragments_sizes, num_body_chunk_fragments, session_data->session_id,
|
res = ngx_http_cp_send_data_to_service(
|
||||||
&was_waiting, fail_open_timeout, tout_retries);
|
fragments,
|
||||||
|
fragments_sizes,
|
||||||
|
num_body_chunk_fragments,
|
||||||
|
session_data->session_id,
|
||||||
|
&was_waiting,
|
||||||
|
fail_open_timeout,
|
||||||
|
tout_retries
|
||||||
|
);
|
||||||
|
|
||||||
if (res != NGX_OK) {
|
if (res != NGX_OK) {
|
||||||
// Failed to send the fragments to the nano service.
|
// Failed to send the fragments to the nano service.
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_dbg(
|
||||||
|
DBG_LEVEL_DEBUG,
|
||||||
|
"Successfully sent %s body chunk %d to service for session ID: %d",
|
||||||
|
body_type == REQUEST_BODY ? "request" : "response",
|
||||||
|
part_count,
|
||||||
|
session_data->session_id
|
||||||
|
);
|
||||||
|
|
||||||
num_parts_sent++;
|
num_parts_sent++;
|
||||||
is_empty_chain = 0;
|
is_empty_chain = 0;
|
||||||
|
chunks_processed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
part_count++;
|
part_count++;
|
||||||
@ -1213,7 +1219,7 @@ ngx_http_cp_body_sender(
|
|||||||
*is_last_part = is_last_chunk;
|
*is_last_part = is_last_chunk;
|
||||||
*num_messages_sent = num_parts_sent;
|
*num_messages_sent = num_parts_sent;
|
||||||
|
|
||||||
*next_elem_to_inspect = chain_iter;
|
*next_elem_to_inspect = chain_iter;
|
||||||
|
|
||||||
return (!is_empty_chain && num_parts_sent == 0) ? NGX_ERROR : NGX_OK;
|
return (!is_empty_chain && num_parts_sent == 0) ? NGX_ERROR : NGX_OK;
|
||||||
}
|
}
|
||||||
@ -1239,3 +1245,4 @@ ngx_http_cp_metric_data_sender()
|
|||||||
reset_metric_data();
|
reset_metric_data();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ ngx_int_t
|
|||||||
ngx_http_cp_reply_receiver(
|
ngx_http_cp_reply_receiver(
|
||||||
ngx_int_t *expected_replies,
|
ngx_int_t *expected_replies,
|
||||||
ngx_http_cp_verdict_e *verdict,
|
ngx_http_cp_verdict_e *verdict,
|
||||||
|
ngx_int_t *inspect_all_response_headers,
|
||||||
uint32_t cur_session_id,
|
uint32_t cur_session_id,
|
||||||
ngx_http_request_t *request,
|
ngx_http_request_t *request,
|
||||||
ngx_http_cp_modification_list **modification_list,
|
ngx_http_cp_modification_list **modification_list,
|
||||||
@ -70,6 +71,7 @@ ngx_http_cp_reply_receiver(
|
|||||||
/// @param[in, out] request NGINX request.
|
/// @param[in, out] request NGINX request.
|
||||||
/// @param[in] cur_request_id Request session's Id.
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
|
/// @param[in] waf_tag WAF tag to be sent.
|
||||||
/// @returns ngx_int_t
|
/// @returns ngx_int_t
|
||||||
/// - #NGX_OK
|
/// - #NGX_OK
|
||||||
/// - #NGX_ERROR
|
/// - #NGX_ERROR
|
||||||
@ -78,7 +80,8 @@ ngx_int_t
|
|||||||
ngx_http_cp_meta_data_sender(
|
ngx_http_cp_meta_data_sender(
|
||||||
ngx_http_request_t *request,
|
ngx_http_request_t *request,
|
||||||
uint32_t cur_request_id,
|
uint32_t cur_request_id,
|
||||||
ngx_uint_t *num_messages_sent
|
ngx_uint_t *num_messages_sent,
|
||||||
|
ngx_str_t *waf_tag
|
||||||
);
|
);
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -138,7 +141,6 @@ ngx_http_cp_content_length_sender(
|
|||||||
/// - #RESPONSE_HEADER
|
/// - #RESPONSE_HEADER
|
||||||
/// @param[in] cur_request_id Request session's Id.
|
/// @param[in] cur_request_id Request session's Id.
|
||||||
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||||
/// @param[in, out] waf_tag WAF tag to be sent.
|
|
||||||
/// @returns ngx_int_t
|
/// @returns ngx_int_t
|
||||||
/// - #NGX_OK
|
/// - #NGX_OK
|
||||||
/// - #NGX_ERROR
|
/// - #NGX_ERROR
|
||||||
@ -148,8 +150,7 @@ ngx_http_cp_header_sender(
|
|||||||
ngx_list_part_t *headers,
|
ngx_list_part_t *headers,
|
||||||
ngx_http_chunk_type_e header_type,
|
ngx_http_chunk_type_e header_type,
|
||||||
uint32_t cur_request_id,
|
uint32_t cur_request_id,
|
||||||
ngx_uint_t *num_messages_sent,
|
ngx_uint_t *num_messages_sent
|
||||||
ngx_str_t *waf_tag
|
|
||||||
);
|
);
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -45,7 +45,8 @@ typedef enum CompressionType
|
|||||||
{
|
{
|
||||||
NO_COMPRESSION,
|
NO_COMPRESSION,
|
||||||
GZIP,
|
GZIP,
|
||||||
ZLIB
|
ZLIB,
|
||||||
|
BROTLI
|
||||||
} CompressionType;
|
} CompressionType;
|
||||||
|
|
||||||
typedef struct CompressionResult
|
typedef struct CompressionResult
|
||||||
|
@ -146,7 +146,8 @@ typedef enum ngx_http_cp_verdict
|
|||||||
TRAFFIC_VERDICT_INJECT,
|
TRAFFIC_VERDICT_INJECT,
|
||||||
TRAFFIC_VERDICT_IRRELEVANT,
|
TRAFFIC_VERDICT_IRRELEVANT,
|
||||||
TRAFFIC_VERDICT_RECONF,
|
TRAFFIC_VERDICT_RECONF,
|
||||||
TRAFFIC_VERDICT_WAIT
|
TRAFFIC_VERDICT_WAIT,
|
||||||
|
LIMIT_RESPONSE_HEADERS
|
||||||
} ngx_http_cp_verdict_e;
|
} ngx_http_cp_verdict_e;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -190,6 +191,8 @@ typedef enum ngx_http_meta_data
|
|||||||
PARSED_HOST_DATA,
|
PARSED_HOST_DATA,
|
||||||
PARSED_URI_SIZE,
|
PARSED_URI_SIZE,
|
||||||
PARSED_URI_DATA,
|
PARSED_URI_DATA,
|
||||||
|
WAF_TAG_SIZE,
|
||||||
|
WAF_TAG_DATA,
|
||||||
|
|
||||||
META_DATA_COUNT
|
META_DATA_COUNT
|
||||||
} ngx_http_meta_data_e;
|
} ngx_http_meta_data_e;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user