280 lines
9.9 KiB
Diff
280 lines
9.9 KiB
Diff
From 96c96ff41ad3d3ead3288ea5ba990d9649f30541 Mon Sep 17 00:00:00 2001
|
|
From: Maxim Dounin <mdounin@mdounin.ru>
|
|
Date: Tue, 10 Oct 2023 15:13:39 +0300
|
|
Subject: [PATCH] HTTP/2: per-iteration stream handling limit.
|
|
|
|
The directive limits the number of request headers accepted from clients.
|
|
While the total amount of headers is believed to be sufficiently limited
|
|
by the existing buffer size limits (client_header_buffer_size and
|
|
large_client_header_buffers), the additional limit on the number of headers
|
|
might be beneficial to better protect backend servers.
|
|
|
|
Requested by Maksim Yevmenkin.
|
|
|
|
The original patch was modified so that the header count and the limit are
|
|
stored in a separate module, ngx_http_header_count_module, instead of the
|
|
core module. This allows to avoid changing the size of existing structures
|
|
and thus avoid potential ABI breakage and keep all the 3rd party modules
|
|
compatible with the new version of nginx without recompilation.
|
|
|
|
Signed-off-by: Elijah Zupancic <e.zupancic@f5.com>
|
|
Origin: <https://freenginx.org/hg/nginx/rev/199dc0d6b05be814b5c811876c20af58cd361fea>
|
|
---
|
|
auto/modules | 1 +
|
|
src/http/ngx_http_core_module.c | 73 +++++++++++++++++++++++++++++++
|
|
src/http/ngx_http_header_count.hh | 14 ++++++
|
|
src/http/ngx_http_request.c | 24 ++++++++++
|
|
src/http/v2/ngx_http_v2.c | 14 ++++++
|
|
5 files changed, 126 insertions(+)
|
|
create mode 100644 src/http/ngx_http_header_count.hh
|
|
|
|
diff --git a/auto/modules b/auto/modules
|
|
index f5a4597..837d894 100644
|
|
--- a/auto/modules
|
|
+++ b/auto/modules
|
|
@@ -67,6 +67,7 @@ if [ $HTTP = YES ]; then
|
|
ngx_module_name="ngx_http_module \
|
|
ngx_http_core_module \
|
|
ngx_http_log_module \
|
|
+ ngx_http_header_count_module \
|
|
ngx_http_upstream_module"
|
|
ngx_module_incs="src/http src/http/modules"
|
|
ngx_module_deps="src/http/ngx_http.h \
|
|
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
|
|
index 6664fa6..620cc35 100644
|
|
--- a/src/http/ngx_http_core_module.c
|
|
+++ b/src/http/ngx_http_core_module.c
|
|
@@ -8,6 +8,7 @@
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
+#include <ngx_http_header_count.hh>
|
|
|
|
|
|
typedef struct {
|
|
@@ -39,6 +40,10 @@ static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf);
|
|
static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf,
|
|
void *parent, void *child);
|
|
|
|
+static void *ngx_http_header_count_create_srv_conf(ngx_conf_t *cf);
|
|
+static char *ngx_http_header_count_merge_srv_conf(ngx_conf_t *cf,
|
|
+ void *parent, void *child);
|
|
+
|
|
static char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
|
|
void *dummy);
|
|
static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
|
|
@@ -812,6 +817,48 @@ ngx_module_t ngx_http_core_module = {
|
|
NGX_MODULE_V1_PADDING
|
|
};
|
|
|
|
+static ngx_command_t ngx_http_header_count_commands[] = {
|
|
+
|
|
+ { ngx_string("max_headers"),
|
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
|
+ ngx_conf_set_num_slot,
|
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
|
+ offsetof(ngx_http_header_count_conf_t, max_headers),
|
|
+ NULL },
|
|
+
|
|
+ ngx_null_command
|
|
+};
|
|
+
|
|
+static ngx_http_module_t ngx_http_header_count_module_ctx = {
|
|
+ NULL, /* preconfiguration */
|
|
+ NULL, /* postconfiguration */
|
|
+
|
|
+ NULL, /* create main configuration */
|
|
+ NULL, /* init main configuration */
|
|
+
|
|
+ ngx_http_header_count_create_srv_conf, /* create server configuration */
|
|
+ ngx_http_header_count_merge_srv_conf, /* merge server configuration */
|
|
+
|
|
+ NULL, /* create location configuration */
|
|
+ NULL /* merge location configuration */
|
|
+};
|
|
+
|
|
+
|
|
+ngx_module_t ngx_http_header_count_module = {
|
|
+ NGX_MODULE_V1,
|
|
+ &ngx_http_header_count_module_ctx, /* module context */
|
|
+ ngx_http_header_count_commands, /* module directives */
|
|
+ NGX_HTTP_MODULE, /* module type */
|
|
+ NULL, /* init master */
|
|
+ NULL, /* init module */
|
|
+ NULL, /* init process */
|
|
+ NULL, /* init thread */
|
|
+ NULL, /* exit thread */
|
|
+ NULL, /* exit process */
|
|
+ NULL, /* exit master */
|
|
+ NGX_MODULE_V1_PADDING
|
|
+};
|
|
+
|
|
|
|
ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET" };
|
|
|
|
@@ -3562,6 +3609,32 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
|
|
}
|
|
|
|
|
|
+static void *
|
|
+ngx_http_header_count_create_srv_conf(ngx_conf_t *cf)
|
|
+{
|
|
+ ngx_http_header_count_conf_t *hccf;
|
|
+
|
|
+ hccf = ngx_pcalloc(cf->pool, sizeof(ngx_http_header_count_conf_t));
|
|
+ if (hccf == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ hccf->max_headers = NGX_CONF_UNSET_UINT;
|
|
+ return hccf;
|
|
+}
|
|
+
|
|
+static char *
|
|
+ngx_http_header_count_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|
+{
|
|
+ ngx_http_header_count_conf_t *prev = parent;
|
|
+ ngx_http_header_count_conf_t *conf = child;
|
|
+
|
|
+ ngx_conf_merge_uint_value(conf->max_headers, prev->max_headers, 1000);
|
|
+
|
|
+ return NGX_CONF_OK;
|
|
+}
|
|
+
|
|
+
|
|
static ngx_str_t ngx_http_core_text_html_type = ngx_string("text/html");
|
|
static ngx_str_t ngx_http_core_image_gif_type = ngx_string("image/gif");
|
|
static ngx_str_t ngx_http_core_image_jpeg_type = ngx_string("image/jpeg");
|
|
diff --git a/src/http/ngx_http_header_count.hh b/src/http/ngx_http_header_count.hh
|
|
new file mode 100644
|
|
index 0000000..6a8b86d
|
|
--- /dev/null
|
|
+++ b/src/http/ngx_http_header_count.hh
|
|
@@ -0,0 +1,14 @@
|
|
+#ifndef _NGX_HTTP_HEADER_COUNT_H_INCLUDED_
|
|
+#define _NGX_HTTP_HEADER_COUNT_H_INCLUDED_
|
|
+
|
|
+typedef struct {
|
|
+ ngx_uint_t count;
|
|
+} ngx_http_header_count_t;
|
|
+
|
|
+typedef struct {
|
|
+ ngx_uint_t max_headers;
|
|
+} ngx_http_header_count_conf_t;
|
|
+
|
|
+extern ngx_module_t ngx_http_header_count_module;
|
|
+
|
|
+#endif /* _NGX_HTTP_HEADER_COUNT_H_INCLUDED_ */
|
|
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
|
|
index 404aa77..d6515b6 100644
|
|
--- a/src/http/ngx_http_request.c
|
|
+++ b/src/http/ngx_http_request.c
|
|
@@ -8,6 +8,7 @@
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
+#include <ngx_http_header_count.hh>
|
|
|
|
|
|
static void ngx_http_wait_request_handler(ngx_event_t *ev);
|
|
@@ -545,6 +546,7 @@ ngx_http_alloc_request(ngx_connection_t *c)
|
|
ngx_http_connection_t *hc;
|
|
ngx_http_core_srv_conf_t *cscf;
|
|
ngx_http_core_main_conf_t *cmcf;
|
|
+ ngx_http_header_count_t *hc_ctx;
|
|
|
|
hc = c->data;
|
|
|
|
@@ -606,6 +608,13 @@ ngx_http_alloc_request(ngx_connection_t *c)
|
|
return NULL;
|
|
}
|
|
|
|
+ hc_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_header_count_t));
|
|
+ if (hc_ctx == NULL) {
|
|
+ ngx_destroy_pool(r->pool);
|
|
+ return NULL;
|
|
+ }
|
|
+ ngx_http_set_ctx(r, hc_ctx, ngx_http_header_count_module);
|
|
+
|
|
#if (NGX_HTTP_SSL)
|
|
if (c->ssl) {
|
|
r->main_filter_need_in_memory = 1;
|
|
@@ -1378,6 +1387,8 @@ ngx_http_process_request_headers(ngx_event_t *rev)
|
|
ngx_http_request_t *r;
|
|
ngx_http_core_srv_conf_t *cscf;
|
|
ngx_http_core_main_conf_t *cmcf;
|
|
+ ngx_http_header_count_conf_t *hccf;
|
|
+ ngx_http_header_count_t *hc_ctx;
|
|
|
|
c = rev->data;
|
|
r = c->data;
|
|
@@ -1451,6 +1462,10 @@ ngx_http_process_request_headers(ngx_event_t *rev)
|
|
rc = ngx_http_parse_header_line(r, r->header_in,
|
|
cscf->underscores_in_headers);
|
|
|
|
+
|
|
+ hccf = ngx_http_get_module_srv_conf(r, ngx_http_header_count_module);
|
|
+ hc_ctx = ngx_http_get_module_ctx(r, ngx_http_header_count_module);
|
|
+
|
|
if (rc == NGX_OK) {
|
|
|
|
r->request_length += r->header_in->pos - r->header_name_start;
|
|
@@ -1468,6 +1483,15 @@ ngx_http_process_request_headers(ngx_event_t *rev)
|
|
|
|
/* a header line has been parsed successfully */
|
|
|
|
+ if (hc_ctx->count++ >= hccf->max_headers) {
|
|
+ r->lingering_close = 1;
|
|
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
|
+ "client sent too many header lines");
|
|
+ ngx_http_finalize_request(r,
|
|
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
|
|
+ break;
|
|
+ }
|
|
+
|
|
h = ngx_list_push(&r->headers_in.headers);
|
|
if (h == NULL) {
|
|
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
|
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
|
|
index 291677a..0c6ae96 100644
|
|
--- a/src/http/v2/ngx_http_v2.c
|
|
+++ b/src/http/v2/ngx_http_v2.c
|
|
@@ -9,6 +9,7 @@
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
#include <ngx_http_v2_module.h>
|
|
+#include <ngx_http_header_count.hh>
|
|
|
|
|
|
typedef struct {
|
|
@@ -1749,6 +1750,8 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,
|
|
ngx_http_v2_header_t *header;
|
|
ngx_http_core_srv_conf_t *cscf;
|
|
ngx_http_core_main_conf_t *cmcf;
|
|
+ ngx_http_header_count_conf_t *hccf;
|
|
+ ngx_http_header_count_t *hc_ctx;
|
|
|
|
static ngx_str_t cookie = ngx_string("cookie");
|
|
|
|
@@ -1860,6 +1863,17 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,
|
|
}
|
|
|
|
} else {
|
|
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
|
+ hccf = ngx_http_get_module_srv_conf(r, ngx_http_header_count_module);
|
|
+ hc_ctx = ngx_http_get_module_ctx(r, ngx_http_header_count_module);
|
|
+
|
|
+ if (hc_ctx->count++ >= hccf->max_headers) {
|
|
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
+ "client sent too many header lines");
|
|
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
h = ngx_list_push(&r->headers_in.headers);
|
|
if (h == NULL) {
|
|
return ngx_http_v2_connection_error(h2c,
|
|
--
|
|
2.44.0
|
|
|