Skip to content

Commit 724e856

Browse files
authored
Merge pull request #170 from microsoft/kabazaz/waf_latency_metrics
Calculating waf latency metric for modsecurity
2 parents f23dde9 + 54fd889 commit 724e856

File tree

1 file changed

+200
-21
lines changed

1 file changed

+200
-21
lines changed

nginx/modsecurity/ngx_http_modsecurity.c

Lines changed: 200 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#endif
3131

3232
#define NOTE_NGINX_REQUEST_CTX "nginx-ctx"
33+
#define WAF_DETECTION_MODE 0
34+
#define WAF_PREVENTION_MODE 1
3335

3436
typedef struct {
3537
ngx_flag_t enable;
@@ -52,6 +54,8 @@ typedef struct {
5254

5355
int thread_running;
5456
int status_code;
57+
ngx_time_t *start_time;
58+
ngx_msec_int_t azwaf_latency;
5559
} ngx_http_modsecurity_ctx_t;
5660

5761
#define STATUS_CODE_NOT_SET -1000
@@ -63,7 +67,9 @@ typedef struct {
6367
/*
6468
** Module's registred function/handlers.
6569
*/
66-
static ngx_int_t ngx_http_modsecurity_handler(ngx_http_request_t *r);
70+
static ngx_int_t ngx_http_modsecurity_handler(ngx_http_request_t *r, ngx_http_modsecurity_loc_conf_t *cf,
71+
ngx_http_modsecurity_ctx_t *ctx);
72+
static ngx_int_t ngx_http_modsecurity_handler_with_timer(ngx_http_request_t *r);
6773
static ngx_int_t ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf);
6874
static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf);
6975
static ngx_int_t ngx_http_modsecurity_init_process(ngx_cycle_t *cycle);
@@ -72,10 +78,28 @@ static char *ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, v
7278
static char *ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
7379
static char *ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
7480

75-
static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r);
81+
static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r, ngx_time_t *start_time);
7682
static int ngx_http_modsecurity_drop_action(request_rec *r);
7783
static void ngx_http_modsecurity_terminate(ngx_cycle_t *cycle);
7884
static void ngx_http_modsecurity_cleanup(void *data);
85+
static ngx_int_t ngx_http_calculate_modsec_latency(ngx_http_request_t *r, ngx_http_modsecurity_ctx_t *ctx);
86+
static void store_azwaf_latency(ngx_http_modsecurity_ctx_t *ctx, ngx_http_variable_value_t *waf_latency_var);
87+
88+
static ngx_int_t ngx_http_modsecurity_set_modsec_latency(ngx_http_request_t* r,
89+
ngx_http_variable_value_t* v, ngx_msec_int_t modsec_latency);
90+
static void ngx_http_modsecurity_set_modsec_mode(ngx_http_request_t* r,
91+
ngx_http_variable_value_t* v, ngx_uint_t modsec_mode);
92+
static ngx_int_t ngx_http_variable_get_modsec_latency(ngx_http_request_t* r,
93+
ngx_http_variable_value_t* v, uintptr_t data);
94+
static ngx_int_t ngx_http_variable_get_modsec_mode(ngx_http_request_t* r,
95+
ngx_http_variable_value_t* v, uintptr_t data);
96+
97+
static ngx_str_t waf_latency_varname = ngx_string("waf_latency");
98+
static ngx_uint_t waf_latency_index;
99+
static ngx_str_t waf_mode_varname = ngx_string("waf_mode");
100+
static ngx_uint_t waf_mode_index;
101+
static ngx_str_t modsec_mode_prev = ngx_string("Prevention");
102+
static ngx_str_t modsec_mode_detect = ngx_string("Detection");
79103

80104
static ngx_str_t thread_pool_name = ngx_string("default");
81105

@@ -160,6 +184,16 @@ ngx_module_t ngx_http_modsecurity = {
160184
};
161185

162186

187+
static ngx_http_variable_t ngx_http_modsecurity_vars[] = {
188+
{ ngx_string("waf_latency"), NULL,
189+
ngx_http_variable_get_modsec_latency,
190+
0, NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_CHANGEABLE, 0 },
191+
{ ngx_string("waf_mode"), NULL,
192+
ngx_http_variable_get_modsec_mode,
193+
0, NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_CHANGEABLE, 0 },
194+
ngx_http_null_variable
195+
};
196+
163197
static ngx_http_upstream_t ngx_http_modsecurity_upstream;
164198

165199
static inline char *
@@ -539,9 +573,29 @@ modsec_pcre_free(void *ptr)
539573
static server_rec *modsec_server = NULL;
540574
static ngx_http_modsecurity_config_cache_t *config_cache = NULL;
541575

576+
static ngx_int_t
577+
ngx_http_modsecurity_add_variables(ngx_conf_t* cf)
578+
{
579+
ngx_http_variable_t* var, * v;
580+
581+
for (v = ngx_http_modsecurity_vars; v->name.len; v++) {
582+
var = ngx_http_add_variable(cf, &v->name, v->flags);
583+
if (var == NULL) {
584+
return NGX_ERROR;
585+
}
586+
587+
var->get_handler = v->get_handler;
588+
var->data = v->data;
589+
}
590+
591+
return NGX_OK;
592+
}
593+
542594
static ngx_int_t
543595
ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf)
544596
{
597+
ngx_http_modsecurity_add_variables(cf);
598+
545599
/* XXX: temporary hack, nginx uses pcre as well and hijacks these two */
546600
pcre_malloc = modsec_pcre_malloc;
547601
pcre_free = modsec_pcre_free;
@@ -601,7 +655,7 @@ ngx_http_modsecurity_init(ngx_conf_t *cf)
601655
if (h == NULL) {
602656
return NGX_ERROR;
603657
}
604-
*h = ngx_http_modsecurity_handler;
658+
*h = ngx_http_modsecurity_handler_with_timer;
605659

606660
ngx_memzero(&ngx_http_modsecurity_upstream, sizeof(ngx_http_upstream_t));
607661
ngx_http_modsecurity_upstream.cacheable = 1;
@@ -618,6 +672,9 @@ ngx_http_modsecurity_init(ngx_conf_t *cf)
618672
extern pthread_mutex_t msc_pregcomp_ex_mtx;
619673
pthread_mutex_init(&msc_pregcomp_ex_mtx, NULL);
620674

675+
waf_latency_index = (ngx_uint_t)ngx_http_get_variable_index(cf, &waf_latency_varname);
676+
waf_mode_index = (ngx_uint_t)ngx_http_get_variable_index(cf, &waf_mode_varname);
677+
621678
#ifdef WAF_JSON_LOGGING_ENABLE
622679
int result = init_appgw_rules_id_hash();
623680
if (result) {
@@ -848,19 +905,49 @@ ngx_http_modsecurity_body_handler(ngx_http_request_t *r)
848905
ngx_http_core_run_phases(r);
849906
}
850907

908+
static void
909+
store_azwaf_latency(ngx_http_modsecurity_ctx_t* ctx, ngx_http_variable_value_t* waf_latency_var)
910+
{
911+
int sec, ms;
912+
char* token = strtok((char*)waf_latency_var->data, ".");
913+
sec = atoi(token);
914+
token = strtok(NULL, " ");
915+
ms = atoi(token);
916+
ctx->azwaf_latency = sec * 1000 + ms;
917+
}
918+
919+
static ngx_int_t
920+
ngx_http_calculate_modsec_latency(ngx_http_request_t *r, ngx_http_modsecurity_ctx_t* ctx)
921+
{
922+
ngx_time_t *end_time;
923+
ngx_msec_int_t ms;
924+
ngx_time_update();
925+
end_time = ngx_timeofday();
926+
927+
ms = (ngx_msec_int_t)
928+
((end_time->sec - ctx->start_time->sec) * 1000 + (end_time->msec - ctx->start_time->msec));
929+
ms = ngx_max(ms, 0);
930+
931+
// Add azwaf latency if we are in hybrid mode
932+
if (ctx->azwaf_latency != 0) {
933+
ms += ctx->azwaf_latency;
934+
}
935+
ngx_http_variable_value_t* modsec_latency_var = ngx_http_get_indexed_variable(r, waf_latency_index);
936+
return ngx_http_modsecurity_set_modsec_latency(r, modsec_latency_var, ms);
937+
}
851938

852939
/*
853940
** [ENTRY POINT] does : this function called by nginx from the request handler
941+
* Calculate the modsec latency in this function and invoke the main handler.
854942
*/
855943
static ngx_int_t
856-
ngx_http_modsecurity_handler(ngx_http_request_t *r)
944+
ngx_http_modsecurity_handler_with_timer(ngx_http_request_t *r)
857945
{
858946
ngx_http_modsecurity_loc_conf_t *cf;
859-
ngx_http_modsecurity_ctx_t *ctx;
860-
ngx_int_t rc;
947+
ngx_http_modsecurity_ctx_t *ctx;
948+
ngx_int_t ret, modsec_latency_ret;
861949

862950
cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity);
863-
864951
/* Process only main request */
865952
if (r != r->main || !cf->enable) {
866953
return NGX_DECLINED;
@@ -871,15 +958,61 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
871958
ngx_http_get_variable(r, &azwaf_processing_result_var, azwaf_processing_result_key);
872959
if (azwaf_processing_result != NULL && !azwaf_processing_result->not_found) {
873960
if (ngx_strncasecmp(
874-
azwaf_processing_result->data,
875-
azwaf_action_allow.data,
876-
MIN(azwaf_processing_result->len ,azwaf_action_allow.len)) == 0) {
961+
azwaf_processing_result->data,
962+
azwaf_action_allow.data,
963+
MIN(azwaf_processing_result->len, azwaf_action_allow.len)) == 0) {
877964

878965
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "The request is allowed by AzWAF, skip ModSecurity processing");
879966
return NGX_DECLINED;
880967
}
881968
}
882969

970+
// Get module request context or create if not yet created
971+
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
972+
if (ctx == NULL) {
973+
ngx_time_update();
974+
ctx = ngx_http_modsecurity_create_ctx(r, ngx_timeofday());
975+
if (ctx == NULL) {
976+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
977+
}
978+
979+
ngx_http_set_ctx(r, ctx, ngx_http_modsecurity);
980+
981+
ngx_http_variable_value_t* waf_latency_var = ngx_http_get_indexed_variable(r, waf_latency_index);
982+
ctx->azwaf_latency = 0;
983+
984+
// We are in hybrid mode, save the latency from azwaf to add later to the waf_latency.
985+
if (waf_latency_var->data != NULL) {
986+
store_azwaf_latency(ctx, waf_latency_var);
987+
}
988+
989+
ngx_http_variable_value_t* modsec_mode_var = ngx_http_get_indexed_variable(r, waf_mode_index);
990+
if (cf->config->is_enabled == MODSEC_DETECTION_ONLY) {
991+
ngx_http_modsecurity_set_modsec_mode(r, modsec_mode_var, WAF_DETECTION_MODE);
992+
}
993+
else {
994+
ngx_http_modsecurity_set_modsec_mode(r, modsec_mode_var, WAF_PREVENTION_MODE);
995+
}
996+
}
997+
998+
// Main modsec handler
999+
ret = ngx_http_modsecurity_handler(r, cf, ctx);
1000+
1001+
// We return failure only if memory allocation fails for latency variable in calculating metric
1002+
modsec_latency_ret = ngx_http_calculate_modsec_latency(r, ctx);
1003+
if (modsec_latency_ret != NGX_OK) {
1004+
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "modSecurity: latency metric memory allocation failed");
1005+
return modsec_latency_ret;
1006+
}
1007+
return ret;
1008+
}
1009+
1010+
static ngx_int_t
1011+
ngx_http_modsecurity_handler(ngx_http_request_t *r, ngx_http_modsecurity_loc_conf_t *cf,
1012+
ngx_http_modsecurity_ctx_t *ctx)
1013+
{
1014+
ngx_int_t rc;
1015+
8831016
// Read body if not yet read
8841017
if (!r->request_body) {
8851018
rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_body_handler);
@@ -893,16 +1026,6 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
8931026

8941027
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler");
8951028

896-
// Get module request context or create if not yet created
897-
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
898-
if (ctx == NULL) {
899-
ctx = ngx_http_modsecurity_create_ctx(r);
900-
if (ctx == NULL) {
901-
return NGX_HTTP_INTERNAL_SERVER_ERROR;
902-
}
903-
904-
ngx_http_set_ctx(r, ctx, ngx_http_modsecurity);
905-
}
9061029

9071030
#ifdef WAF_JSON_LOGGING_ENABLE
9081031
modsecReopenLogfileIfNeeded(ctx->req);
@@ -932,7 +1055,7 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
9321055
#define TXID_SIZE 25
9331056

9341057
static ngx_http_modsecurity_ctx_t *
935-
ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
1058+
ngx_http_modsecurity_create_ctx(ngx_http_request_t *r, ngx_time_t *start_time)
9361059
{
9371060
ngx_http_modsecurity_loc_conf_t *cf;
9381061
ngx_pool_cleanup_t *cln;
@@ -961,6 +1084,7 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
9611084
cln->data = ctx;
9621085

9631086
ctx->r = r;
1087+
ctx->start_time = start_time;
9641088

9651089
if (r->connection->requests == 0 || ctx->connection == NULL) {
9661090

@@ -1136,3 +1260,58 @@ ngx_http_modsecurity_drop_action(request_rec *r)
11361260
ctx->r->connection->error = 1;
11371261
return 0;
11381262
}
1263+
1264+
static ngx_int_t
1265+
ngx_http_variable_get_modsec_latency(ngx_http_request_t* r,
1266+
ngx_http_variable_value_t* v, uintptr_t data)
1267+
{
1268+
return NGX_OK;
1269+
}
1270+
1271+
static ngx_int_t
1272+
ngx_http_variable_get_modsec_mode(ngx_http_request_t* r,
1273+
ngx_http_variable_value_t* v, uintptr_t data)
1274+
{
1275+
return NGX_OK;
1276+
}
1277+
1278+
static ngx_int_t
1279+
ngx_http_modsecurity_set_modsec_latency(ngx_http_request_t *r,
1280+
ngx_http_variable_value_t *v, ngx_msec_int_t modsec_latency)
1281+
{
1282+
if (v->data == NULL) {
1283+
v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
1284+
if (v->data == NULL) {
1285+
return NGX_ERROR;
1286+
}
1287+
}
1288+
1289+
v->len = ngx_sprintf(v->data, "%T.%03M", (time_t)(modsec_latency) / 1000, modsec_latency % 1000) - v->data;
1290+
v->valid = 1;
1291+
v->no_cacheable = 0;
1292+
v->not_found = 0;
1293+
1294+
return NGX_OK;
1295+
}
1296+
1297+
static void
1298+
ngx_http_modsecurity_set_modsec_mode(ngx_http_request_t* r,
1299+
ngx_http_variable_value_t* v, ngx_uint_t modsec_mode)
1300+
{
1301+
u_char *p;
1302+
size_t len;
1303+
if (modsec_mode == WAF_DETECTION_MODE) {
1304+
len = modsec_mode_detect.len;
1305+
p = modsec_mode_detect.data;
1306+
}
1307+
else {
1308+
len = modsec_mode_prev.len;
1309+
p = modsec_mode_prev.data;
1310+
}
1311+
1312+
v->len = len;
1313+
v->valid = 1;
1314+
v->no_cacheable = 0;
1315+
v->not_found = 0;
1316+
v->data = p;
1317+
}

0 commit comments

Comments
 (0)