3030#endif
3131
3232#define NOTE_NGINX_REQUEST_CTX "nginx-ctx"
33+ #define WAF_DETECTION_MODE 0
34+ #define WAF_PREVENTION_MODE 1
3335
3436typedef 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 );
6773static ngx_int_t ngx_http_modsecurity_preconfiguration (ngx_conf_t * cf );
6874static ngx_int_t ngx_http_modsecurity_init (ngx_conf_t * cf );
6975static 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
7278static char * ngx_http_modsecurity_config (ngx_conf_t * cf , ngx_command_t * cmd , void * conf );
7379static 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 );
7682static int ngx_http_modsecurity_drop_action (request_rec * r );
7783static void ngx_http_modsecurity_terminate (ngx_cycle_t * cycle );
7884static 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
80104static 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+
163197static ngx_http_upstream_t ngx_http_modsecurity_upstream ;
164198
165199static inline char *
@@ -539,9 +573,29 @@ modsec_pcre_free(void *ptr)
539573static server_rec * modsec_server = NULL ;
540574static 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+
542594static ngx_int_t
543595ngx_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*/
855943static 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
9341057static 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