From mdounin at mdounin.ru Tue Jun 4 14:08:55 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 4 Jun 2024 17:08:55 +0300 Subject: freenginx-1.27.1 Message-ID: Changes with freenginx 1.27.1 04 Jun 2024 *) Feature: the "max_headers" directive. Thanks to Maksim Yevmenkin. *) Feature: the $upstream_cache_key variable. Thanks to Kirill A. Korinsky. *) Feature: XOAUTH2 and OAUTHBEARER authentication mechanisms support in the mail proxy module. Thanks to Rob Mueller. *) Bugfix: graceful shutdown of old worker processes might be delayed when using HTTP/2. Thanks to Kasei Wang. *) Bugfix: a segmentation fault might occur in a worker process when using HTTP/3. *) Bugfix: in HTTP/3. *) Bugfix: in the mail proxy module. -- Maxim Dounin http://freenginx.org/ From cello86 at gmail.com Wed Jun 12 13:47:52 2024 From: cello86 at gmail.com (Marcello Lorenzi) Date: Wed, 12 Jun 2024 15:47:52 +0200 Subject: error_page custom 404 page Message-ID: Hi all, I tried to configure an error_page to a specific react 404 page defined by our development group and the configuration was: error_page 404 =404 https://$host/404/; We noticed that the client receives a 302 code from the initial page to the 404 page and the 404 page gives a 200 code. Into the access_log we can't identify the 404 errors. Can you help us to identify a possible configuration to cover the 404 pages into the access_log? Thanks, Marcello -------------- next part -------------- An HTML attachment was scrubbed... URL: From me at nanaya.net Wed Jun 12 14:39:02 2024 From: me at nanaya.net (nanaya) Date: Wed, 12 Jun 2024 23:39:02 +0900 Subject: error_page custom 404 page In-Reply-To: References: Message-ID: <71235fc5-fa17-48bb-ad39-44d511fda10f@app.fastmail.com> Hi, On Wed, Jun 12, 2024, at 22:47, Marcello Lorenzi wrote: > error_page 404 =404 https://$host/404/; > > We noticed that the client receives a 302 code from the initial page to the 404 page and the 404 page gives a 200 code. Into the access_log we can't identify the 404 errors. > As per documentation when a url is defined for error page: > In this case, by default, the response code 302 is returned to the client. It can only be changed to one of the redirect status codes (301, 302, 303, 307, and 308). Either make it just /404/, or if it's on a different server, make an internal location block which proxies to that page. From cello86 at gmail.com Wed Jun 12 15:11:50 2024 From: cello86 at gmail.com (Marcello Lorenzi) Date: Wed, 12 Jun 2024 17:11:50 +0200 Subject: error_page custom 404 page In-Reply-To: <71235fc5-fa17-48bb-ad39-44d511fda10f@app.fastmail.com> References: <71235fc5-fa17-48bb-ad39-44d511fda10f@app.fastmail.com> Message-ID: Hi; the 404 page is hosted on the same server. Marcello Il giorno mer 12 giu 2024 alle ore 16:39 nanaya ha scritto: > Hi, > > On Wed, Jun 12, 2024, at 22:47, Marcello Lorenzi wrote: > > error_page 404 =404 https://$host/404/; > > > > We noticed that the client receives a 302 code from the initial page to > the 404 page and the 404 page gives a 200 code. Into the access_log we > can't identify the 404 errors. > > > > As per documentation when a url is defined for error page: > > > In this case, by default, the response code 302 is returned to the > client. It can only be changed to one of the redirect status codes (301, > 302, 303, 307, and 308). > > Either make it just /404/, or if it's on a different server, make an > internal location block which proxies to that page. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Wed Jun 12 15:54:35 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 12 Jun 2024 18:54:35 +0300 Subject: error_page custom 404 page In-Reply-To: References: <71235fc5-fa17-48bb-ad39-44d511fda10f@app.fastmail.com> Message-ID: Hello! On Wed, Jun 12, 2024 at 05:11:50PM +0200, Marcello Lorenzi wrote: > the 404 page is hosted on the same server. This doesn't matter. When you write > > > error_page 404 =404 https://$host/404/; in your configuration, that is, as long as the error page URI starts with something like "https://" or "http://", the "=404" part does nothing: it is simply ignored, and [free]nginx returns a 302 redirect to the user. (When returning redirections, it is only possible to change the status code to other redirection status codes: 301, 302, 303, 307, and 308. See http://freenginx.org/r/error_page for details.) If/when the client follows the redirect and requests the URL specified, it would be a new request, which is served according to the configuration for this request. That is, if the is a static file, it will be returned with the status code 200, much like any normal static file. If you want [free]nginx to respond with the particular content and the 400 status code, consider using error_page URI as an absolute path within the server in question, that is, starting with "/": error_page 404 /404/; This will instruct [free]nginx to do an internal redirect to the specified URI, and return it along the status code 404. Hope this helps. -- Maxim Dounin http://mdounin.ru/ From kirill at korins.ky Sat Jun 15 11:02:28 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Sat, 15 Jun 2024 12:02:28 +0100 Subject: Allow response with AD bit in resolver Message-ID: Greetings, Here a trivial patch which allows DNS responses with enabled AD bit from used resolver. Index: src/core/ngx_resolver.c --- src/core/ngx_resolver.c.orig +++ src/core/ngx_resolver.c @@ -1774,7 +1774,7 @@ ngx_resolver_process_response(ngx_resolver_t *r, u_cha (response->nar_hi << 8) + response->nar_lo); /* response to a standard query */ - if ((flags & 0xf870) != 0x8000 || (trunc && tcp)) { + if ((flags & 0xf850) != 0x8000 || (trunc && tcp)) { ngx_log_error(r->log_level, r->log, 0, "invalid %s DNS response %ui fl:%04Xi", tcp ? "TCP" : "UDP", ident, flags); -- wbr, Kirill From mdounin at mdounin.ru Sun Jun 16 01:29:51 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 16 Jun 2024 04:29:51 +0300 Subject: Allow response with AD bit in resolver In-Reply-To: References: Message-ID: Hello! On Sat, Jun 15, 2024 at 12:02:28PM +0100, Kirill A. Korinsky wrote: > Greetings, > > Here a trivial patch which allows DNS responses with enabled AD bit > from used resolver. > > Index: src/core/ngx_resolver.c > --- src/core/ngx_resolver.c.orig > +++ src/core/ngx_resolver.c > @@ -1774,7 +1774,7 @@ ngx_resolver_process_response(ngx_resolver_t *r, u_cha > (response->nar_hi << 8) + response->nar_lo); > > /* response to a standard query */ > - if ((flags & 0xf870) != 0x8000 || (trunc && tcp)) { > + if ((flags & 0xf850) != 0x8000 || (trunc && tcp)) { > ngx_log_error(r->log_level, r->log, 0, > "invalid %s DNS response %ui fl:%04Xi", > tcp ? "TCP" : "UDP", ident, flags); > Looks good to me, pushed with an appropriate commit log, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Jun 21 01:25:55 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 21 Jun 2024 04:25:55 +0300 Subject: Nginx ignores proxy_no_cache In-Reply-To: References: <87sezxpfpm.wl-kirill@korins.ky> Message-ID: Hello! On Sun, Apr 07, 2024 at 04:56:20PM +0300, Maxim Dounin wrote: > On Sun, Apr 07, 2024 at 01:36:21PM +0200, Kirill A. Korinsky wrote: > > > Greetings, > > > > Let assume that I would like behavior on LB from the backend and force it to > > cache only resposnes that have a X-No-Cache header with value NO. > > > > Nginx should cache a response with any code, if it has such headers. > > > > This works well until the backend is unavailable and nginx returns a > > hardcoded 502 that doesn't have a control header, but such a response is > > cached anyway. > > > > Here is the config that allows to reproduce the issue: > > > > http { > > default_type application/octet-stream; > > > > proxy_cache_path /tmp/nginx_cache keys_zone=the_zone:1m; > > proxy_cache the_zone; > > proxy_cache_valid any 15m; > > proxy_cache_methods GET HEAD POST; > > > > add_header X-Cache-Status $upstream_cache_status always; > > > > map $upstream_http_x_no_cache $no_cache { > > default 1; > > "NO" 0; > > } > > > > proxy_no_cache $no_cache; > > > > upstream echo { > > server 127.127.127.127:80; > > } > > > > server { > > listen 1234; > > server_name localhost; > > > > location / { > > proxy_pass http://echo; > > } > > } > > } > > > > when I run: > > > > curl -D - http://127.0.0.1:1234/ > > > > it returns MISS on the first request, and HIT on the second one. > > > > Here I expect both requests to return MISS. > > Thanks for the report. > > Indeed, proxy_no_cache is only checked for proper upstream > responses, but not when caching errors, including internally > generated 502/504 in ngx_http_upstream_finalize_request(), and > intercepted errors in ngx_http_upstream_intercept_errors(). > > Quick look suggests there will be also issues with caching errors > after proxy_cache_bypass (errors won't be cached even if they > should), as well as issues with proxy_cache_max_range_offset after > proxy_cache_bypass (it will be ignored). > > This needs cleanup / fixes, added to my TODO list. I've posted a patch to address proxy_no_cache issues along with relevant tests here: https://freenginx.org/pipermail/nginx-devel/2024-June/000382.html https://freenginx.org/pipermail/nginx-devel/2024-June/000383.html Please take a look if it works for you. -- Maxim Dounin http://mdounin.ru/ From showerheadsuk at hotmail.com Mon Jun 24 07:06:23 2024 From: showerheadsuk at hotmail.com (Dave Kennard) Date: Mon, 24 Jun 2024 08:06:23 +0100 Subject: undefined symbol: CRYPTO_chacha_20 when built with boringssl Message-ID: This is probably me doing something stupid, but I can't get nginx to run when built to use boringssl. When trying to run it (nginx -t) I get the error: undefined symbol: CRYPTO_chacha_20 I think it's just that it isn't loading the boringssl shared libs. Nginx is configured as follows: ./configure --prefix=/opt/nginx-1.27.1 \ ??? --with-pcre={{ tarballs_path }}/pcre2-{{ pcre_version }} \ ??? --with-pcre-jit \ ??? --without-http_autoindex_module \ ??? --without-http_empty_gif_module \ ??? --without-http_ssi_module \ ??? --with-http_ssl_module \ ??? --with-http_v2_module \ ??? --with-http_v3_module \ ??? --with-ipv6 \ ??? --with-http_gzip_static_module \ ??? --with-http_realip_module \ ??? --add-module=../ngx_http_geoip2_module \ ??? --with-http_perl_module --with-perl_modules_path=perl/lib \ ??? --with-cc-opt='-I/opt/GeoIP/include -I/opt/boringssl/include' \ ??? --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -L/opt/GeoIP/lib -L/opt/boringssl/lib' And boringssl: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_PREFIX=/opt/boringssl-{{ ansible_date_time.date }} (/opt/boringssl is symlinked to /opt/boringssl-{{ ansible_date_time.date }}) Can anyone suggest what the problem might be? Thanks Dave From mdounin at mdounin.ru Mon Jun 24 22:19:40 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 25 Jun 2024 01:19:40 +0300 Subject: undefined symbol: CRYPTO_chacha_20 when built with boringssl In-Reply-To: References: Message-ID: Hello! On Mon, Jun 24, 2024 at 08:06:23AM +0100, Dave Kennard wrote: > This is probably me doing something stupid, but I can't get nginx to run > when built to use boringssl. When trying to run it (nginx -t) I get the > error: undefined symbol: CRYPTO_chacha_20 > > I think it's just that it isn't loading the boringssl shared libs. > > Nginx is configured as follows: > > ./configure --prefix=/opt/nginx-1.27.1 \ > ??? --with-pcre={{ tarballs_path }}/pcre2-{{ pcre_version }} \ > ??? --with-pcre-jit \ > ??? --without-http_autoindex_module \ > ??? --without-http_empty_gif_module \ > ??? --without-http_ssi_module \ > ??? --with-http_ssl_module \ > ??? --with-http_v2_module \ > ??? --with-http_v3_module \ > ??? --with-ipv6 \ > ??? --with-http_gzip_static_module \ > ??? --with-http_realip_module \ > ??? --add-module=../ngx_http_geoip2_module \ > ??? --with-http_perl_module --with-perl_modules_path=perl/lib \ > ??? --with-cc-opt='-I/opt/GeoIP/include -I/opt/boringssl/include' \ > ??? --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -L/opt/GeoIP/lib > -L/opt/boringssl/lib' > > And boringssl: > > cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 > -DCMAKE_INSTALL_PREFIX=/opt/boringssl-{{ ansible_date_time.date }} > > (/opt/boringssl is symlinked to /opt/boringssl-{{ ansible_date_time.date }}) > > Can anyone suggest what the problem might be? Options you use suggest that you are building with shared BorinSSL library installed in a non-default location. The error you are seeing is likely a result of loading OpenSSL library from the default library path instead. Using "-R/opt/boringssl/lib" in ld options might be the way to go, similarly to how you already do with the GeoIP library. -- Maxim Dounin http://mdounin.ru/ From kirill at korins.ky Tue Jun 25 10:37:01 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Tue, 25 Jun 2024 11:37:01 +0100 Subject: Nginx ignores proxy_no_cache In-Reply-To: References: <87sezxpfpm.wl-kirill@korins.ky> Message-ID: <6d1bc1b869f8d1db@mx2.catap.net> On Fri, 21 Jun 2024 02:25:55 +0100, Maxim Dounin wrote: > > I've posted a patch to address proxy_no_cache issues along with > relevant tests here: > > https://freenginx.org/pipermail/nginx-devel/2024-June/000382.html > https://freenginx.org/pipermail/nginx-devel/2024-June/000383.html > > Please take a look if it works for you. > Confirmed that the patch addressed known issue. Thanks! -- wbr, Kirill From showerheadsuk at hotmail.com Tue Jun 25 14:49:21 2024 From: showerheadsuk at hotmail.com (Dave Kennard) Date: Tue, 25 Jun 2024 15:49:21 +0100 Subject: undefined symbol: CRYPTO_chacha_20 when built with boringssl In-Reply-To: References: Message-ID: Thanks very much for the help. I tried adding "-R/opt/boringssl/lib" in ld options, so it looked like: --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -R/opt/boringssl/lib -L/opt/GeoIP/lib -L/opt/boringssl/lib' But this gave the same result, probably the above is wrong because I don't really know what I am doing with linker options. You are correct though, I tried replacing the system libcrypto.so and libssl.so with links to the boringssl ones and that got rid of the issue. So it is just not looking for them in the correct place. I will have to read up on linker options then hopefully I can get it working properly. Thanks for pointing me in the right direction. Dave > Hello! > > On Mon, Jun 24, 2024 at 08:06:23AM +0100, Dave Kennard wrote: > >> This is probably me doing something stupid, but I can't get nginx to run >> when built to use boringssl. When trying to run it (nginx -t) I get the >> error: undefined symbol: CRYPTO_chacha_20 >> >> I think it's just that it isn't loading the boringssl shared libs. >> >> Nginx is configured as follows: >> >> ./configure --prefix=/opt/nginx-1.27.1 \ >> ??? --with-pcre={{ tarballs_path }}/pcre2-{{ pcre_version }} \ >> ??? --with-pcre-jit \ >> ??? --without-http_autoindex_module \ >> ??? --without-http_empty_gif_module \ >> ??? --without-http_ssi_module \ >> ??? --with-http_ssl_module \ >> ??? --with-http_v2_module \ >> ??? --with-http_v3_module \ >> ??? --with-ipv6 \ >> ??? --with-http_gzip_static_module \ >> ??? --with-http_realip_module \ >> ??? --add-module=../ngx_http_geoip2_module \ >> ??? --with-http_perl_module --with-perl_modules_path=perl/lib \ >> ??? --with-cc-opt='-I/opt/GeoIP/include -I/opt/boringssl/include' \ >> ??? --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -L/opt/GeoIP/lib >> -L/opt/boringssl/lib' >> >> And boringssl: >> >> cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 >> -DCMAKE_INSTALL_PREFIX=/opt/boringssl-{{ ansible_date_time.date }} >> >> (/opt/boringssl is symlinked to /opt/boringssl-{{ ansible_date_time.date }}) >> >> Can anyone suggest what the problem might be? > Options you use suggest that you are building with shared BorinSSL > library installed in a non-default location. The error you are > seeing is likely a result of loading OpenSSL library from the > default library path instead. > > Using "-R/opt/boringssl/lib" in ld options might be the way to go, > similarly to how you already do with the GeoIP library. -------------- next part -------------- An HTML attachment was scrubbed... URL: From noloader at gmail.com Tue Jun 25 15:20:28 2024 From: noloader at gmail.com (Jeffrey Walton) Date: Tue, 25 Jun 2024 11:20:28 -0400 Subject: undefined symbol: CRYPTO_chacha_20 when built with boringssl In-Reply-To: References: Message-ID: On Tue, Jun 25, 2024 at 10:57?AM Dave Kennard wrote: > > Thanks very much for the help. I tried adding "-R/opt/boringssl/lib" in ld options, so it looked like: --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -R/opt/boringssl/lib -L/opt/GeoIP/lib -L/opt/boringssl/lib' But this gave the same result, probably the above is wrong because I don't really know what I am doing with linker options. > > You are correct though, I tried replacing the system libcrypto.so and libssl.so with links to the boringssl ones and that got rid of the issue. So it is just not looking for them in the correct place. > > I will have to read up on linker options then hopefully I can get it working properly. > > Thanks for pointing me in the right direction. > > On Mon, Jun 24, 2024 at 08:06:23AM +0100, Dave Kennard wrote: > > This is probably me doing something stupid, but I can't get nginx to run > when built to use boringssl. When trying to run it (nginx -t) I get the > error: undefined symbol: CRYPTO_chacha_20 > > I think it's just that it isn't loading the boringssl shared libs. > > Nginx is configured as follows: > > ./configure --prefix=/opt/nginx-1.27.1 \ > ??? --with-pcre={{ tarballs_path }}/pcre2-{{ pcre_version }} \ > ??? --with-pcre-jit \ > ??? --without-http_autoindex_module \ > ??? --without-http_empty_gif_module \ > ??? --without-http_ssi_module \ > ??? --with-http_ssl_module \ > ??? --with-http_v2_module \ > ??? --with-http_v3_module \ > ??? --with-ipv6 \ > ??? --with-http_gzip_static_module \ > ??? --with-http_realip_module \ > ??? --add-module=../ngx_http_geoip2_module \ > ??? --with-http_perl_module --with-perl_modules_path=perl/lib \ > ??? --with-cc-opt='-I/opt/GeoIP/include -I/opt/boringssl/include' \ > ??? --with-ld-opt='-Wl,-R,/opt/GeoIP/lib -L/opt/GeoIP/lib > -L/opt/boringssl/lib' > > And boringssl: > > cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 > -DCMAKE_INSTALL_PREFIX=/opt/boringssl-{{ ansible_date_time.date }} > > (/opt/boringssl is symlinked to /opt/boringssl-{{ ansible_date_time.date }}) > > Can anyone suggest what the problem might be? > > Options you use suggest that you are building with shared BorinSSL > library installed in a non-default location. The error you are > seeing is likely a result of loading OpenSSL library from the > default library path instead. > > Using "-R/opt/boringssl/lib" in ld options might be the way to go, > similarly to how you already do with the GeoIP library. Because this happens at runtime (and not compile time, and not link time): > When trying to run it (nginx -t) I get the > error: undefined symbol: CRYPTO_chacha_20 It appears you have a runtime path problem. /opt/boringssl/lib is not on-path. Try this: LD_LIBRARY_PATH="/opt/boringssl/lib:${LD_LIBRARY_PATH}" nginx -t If that fixes the issue, then add the following to your linker options to permanently solve the issue: -Wl,-R/opt/boringssl/lib -Wl,--enable-new-dtags The "-Wl" tells the compiler driver to pass the option to the linker. You can omit the "-Wl" if you are directly invoking the `ld` linker. You need "-Wl` if you are driving link through `gcc` (or other compiler driver). You should also add "-Wl,--enable-new-dtags" to the linker options to enable RUNPATHs rather than RPATHs. RUNPATHs allow runtime overrides. Jeff From mdounin at mdounin.ru Tue Jun 25 19:04:00 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 25 Jun 2024 22:04:00 +0300 Subject: Nginx ignores proxy_no_cache In-Reply-To: <6d1bc1b869f8d1db@mx2.catap.net> References: <87sezxpfpm.wl-kirill@korins.ky> <6d1bc1b869f8d1db@mx2.catap.net> Message-ID: Hello! On Tue, Jun 25, 2024 at 11:37:01AM +0100, Kirill A. Korinsky wrote: > On Fri, 21 Jun 2024 02:25:55 +0100, > Maxim Dounin wrote: > > > > I've posted a patch to address proxy_no_cache issues along with > > relevant tests here: > > > > https://freenginx.org/pipermail/nginx-devel/2024-June/000382.html > > https://freenginx.org/pipermail/nginx-devel/2024-June/000383.html > > > > Please take a look if it works for you. > > > > Confirmed that the patch addressed known issue. > > Thanks! Committed, thanks for testing. -- Maxim Dounin http://mdounin.ru/ From kirill at korins.ky Tue Jun 25 22:22:14 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Tue, 25 Jun 2024 23:22:14 +0100 Subject: Send original headers to the upstream Message-ID: Greetings, Here is a patch that adds a string to proxy_pass_request_headers as a possible value. Such a string is used as a prefix for headers which is overwritten by Nginx when it proxies the request to upstream. As a nice side effect, this feature preserves the original order of HTTP headers, which might be needed sometimes. It is only implemented on the proxy module because there is no other way to deliver the original request without touching it. diff --git src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_proxy_module.c index 536482ec5..6a722042b 100644 --- src/http/modules/ngx_http_proxy_module.c +++ src/http/modules/ngx_http_proxy_module.c @@ -117,6 +117,8 @@ typedef struct { ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; + ngx_str_t request_headers_prefix; + #if (NGX_HTTP_SSL) ngx_uint_t ssl; ngx_uint_t ssl_protocols; @@ -215,6 +217,8 @@ static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #if (NGX_HTTP_CACHE) static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -445,9 +449,9 @@ static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_pass_request_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_http_proxy_pass_request_headers, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers), + 0, NULL }, { ngx_string("proxy_pass_request_body"), @@ -1389,7 +1393,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) if (ngx_hash_find(&headers->hash, header[i].hash, header[i].lowcase_key, header[i].key.len)) { - continue; + if (plcf->request_headers_prefix.len == 0) { + continue; + } + len += plcf->request_headers_prefix.len; } len += header[i].key.len + sizeof(": ") - 1 @@ -1525,7 +1532,12 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) if (ngx_hash_find(&headers->hash, header[i].hash, header[i].lowcase_key, header[i].key.len)) { - continue; + if (plcf->request_headers_prefix.len == 0) { + continue; + } + b->last = ngx_copy(b->last, + plcf->request_headers_prefix.data, + plcf->request_headers_prefix.len); } b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); @@ -3349,6 +3361,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) * conf->body_values = NULL; * conf->body_source = { 0, NULL }; * conf->redirects = NULL; + * conf->request_headers_prefix = { NULL, 0 }; * conf->ssl = 0; * conf->ssl_protocols = 0; * conf->ssl_ciphers = { 0, NULL }; @@ -3727,6 +3740,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.intercept_errors, prev->upstream.intercept_errors, 0); + ngx_conf_merge_str_value(conf->request_headers_prefix, + prev->request_headers_prefix, ""); + #if (NGX_HTTP_SSL) if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) { @@ -4772,6 +4788,36 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + + if (plcf->upstream.pass_request_headers != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.pass_request_headers = 0; + return NGX_CONF_OK; + } + + plcf->upstream.store = 1; + + if (ngx_strcmp(value[1].data, "on") == 0) { + return NGX_CONF_OK; + } + + plcf->request_headers_prefix = value[1]; + + return NGX_CONF_OK; +} + + #if (NGX_HTTP_CACHE) static char * -- wbr, Kirill From mdounin at mdounin.ru Wed Jun 26 01:42:01 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 26 Jun 2024 04:42:01 +0300 Subject: Send original headers to the upstream In-Reply-To: References: Message-ID: Hello! On Tue, Jun 25, 2024 at 11:22:14PM +0100, Kirill A. Korinsky wrote: > Greetings, > > Here is a patch that adds a string to proxy_pass_request_headers as a > possible value. > > Such a string is used as a prefix for headers which is overwritten by Nginx > when it proxies the request to upstream. > > As a nice side effect, this feature preserves the original order of HTTP > headers, which might be needed sometimes. > > It is only implemented on the proxy module because there is no other way to > deliver the original request without touching it. Could you please provide some more details about specific use cases for such a feature? As far as I see, using appropriate proxy_set_header X-Original-Foo $http_foo; directives should be mostly equivalent, except it won't preserve the headers order and will merge duplicate headers. But these aren't really guaranteed by HTTP anyway, and merging/reorder can happen on any intermediate hosts. > > diff --git src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_proxy_module.c > index 536482ec5..6a722042b 100644 > --- src/http/modules/ngx_http_proxy_module.c > +++ src/http/modules/ngx_http_proxy_module.c > @@ -117,6 +117,8 @@ typedef struct { > ngx_uint_t headers_hash_max_size; > ngx_uint_t headers_hash_bucket_size; > > + ngx_str_t request_headers_prefix; > + > #if (NGX_HTTP_SSL) > ngx_uint_t ssl; > ngx_uint_t ssl_protocols; > @@ -215,6 +217,8 @@ static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, > void *conf); > static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, > void *conf); > +static char *ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, > + void *conf); > #if (NGX_HTTP_CACHE) > static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, > void *conf); > @@ -445,9 +449,9 @@ static ngx_command_t ngx_http_proxy_commands[] = { > > { ngx_string("proxy_pass_request_headers"), > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, > - ngx_conf_set_flag_slot, > + ngx_http_proxy_pass_request_headers, > NGX_HTTP_LOC_CONF_OFFSET, > - offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers), > + 0, > NULL }, > > { ngx_string("proxy_pass_request_body"), > @@ -1389,7 +1393,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) > if (ngx_hash_find(&headers->hash, header[i].hash, > header[i].lowcase_key, header[i].key.len)) > { > - continue; > + if (plcf->request_headers_prefix.len == 0) { > + continue; > + } > + len += plcf->request_headers_prefix.len; > } > > len += header[i].key.len + sizeof(": ") - 1 > @@ -1525,7 +1532,12 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) > if (ngx_hash_find(&headers->hash, header[i].hash, > header[i].lowcase_key, header[i].key.len)) > { > - continue; > + if (plcf->request_headers_prefix.len == 0) { > + continue; > + } > + b->last = ngx_copy(b->last, > + plcf->request_headers_prefix.data, > + plcf->request_headers_prefix.len); > } > > b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); Note that as implemented, you won't be able to distinguish original requests headers from the ones with prefix added. E.g., assuming prefix "X-Original-", and original request headers: Host: foo X-Original-Host: bar you'll get X-Original-Host: foo X-Original-Host: bar in the upstream request, and you won't be able to tell which one is real. Following the exiting proxy_set_header behaviour, I would rather suggests that such a feature, if implemented, should drop all the headers with the configured prefix. > @@ -3349,6 +3361,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) > * conf->body_values = NULL; > * conf->body_source = { 0, NULL }; > * conf->redirects = NULL; > + * conf->request_headers_prefix = { NULL, 0 }; > * conf->ssl = 0; > * conf->ssl_protocols = 0; > * conf->ssl_ciphers = { 0, NULL }; > @@ -3727,6 +3740,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) > ngx_conf_merge_value(conf->upstream.intercept_errors, > prev->upstream.intercept_errors, 0); > > + ngx_conf_merge_str_value(conf->request_headers_prefix, > + prev->request_headers_prefix, ""); > + > #if (NGX_HTTP_SSL) > > if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) { > @@ -4772,6 +4788,36 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) > } > > > +static char * > +ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) > +{ > + ngx_http_proxy_loc_conf_t *plcf = conf; > + > + ngx_str_t *value; Style: wrong indentation of variable name, should be ngx_str_t *value; > + > + if (plcf->upstream.pass_request_headers != NGX_CONF_UNSET) { > + return "is duplicate"; > + } > + > + value = cf->args->elts; > + > + if (ngx_strcmp(value[1].data, "off") == 0) { > + plcf->upstream.pass_request_headers = 0; > + return NGX_CONF_OK; > + } > + > + plcf->upstream.store = 1; Seems to be a cut-n-paste leftover. > + > + if (ngx_strcmp(value[1].data, "on") == 0) { > + return NGX_CONF_OK; > + } > + > + plcf->request_headers_prefix = value[1]; Not sure if reusing "proxy_pass_request_headers" for such a feature is a good idea. From the behaviour of "proxy_pass_request_headers off;", I would rather assume that "proxy_pass_request_headers X-Original-;" will pass all the headers with the specified prefix, and not just the headers which were modified. > + > + return NGX_CONF_OK; > +} > + > + > #if (NGX_HTTP_CACHE) > > static char * > > -- > wbr, Kirill -- Maxim Dounin http://mdounin.ru/ From kirill at korins.ky Wed Jun 26 09:32:29 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Wed, 26 Jun 2024 10:32:29 +0100 Subject: Send original headers to the upstream In-Reply-To: References: Message-ID: <6d1bcbbadc5c7f6c@mx2.catap.net> Greetings, On Wed, 26 Jun 2024 02:42:01 +0100, Maxim Dounin wrote: > > Could you please provide some more details about specific use > cases for such a feature? > > As far as I see, using appropriate > > proxy_set_header X-Original-Foo $http_foo; > > directives should be mostly equivalent, except it won't preserve > the headers order and will merge duplicate headers. But these > aren't really guaranteed by HTTP anyway, and merging/reorder can > happen on any intermediate hosts. > Well, this application needs to preserve HTTP header order and Nginx is used here as a way to cache some requests. Nginx runs with a module that parses requests to extract some value that can be used as cache key, and an application behind Nginx controls when request can be safely cached via special header in response. Before Nginx it uses load balancers, but all of them in TCP mode because headers order is quite important here. So, provided patch allows to send the headers in the original order. Thus, it uses one more patch which bring an option to tolerate invalid and mallformed URI in requests, which I plan to share as the next step. -- wbr, Kirill From mdounin at mdounin.ru Thu Jun 27 19:18:10 2024 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 27 Jun 2024 22:18:10 +0300 Subject: Send original headers to the upstream In-Reply-To: <6d1bcbbadc5c7f6c@mx2.catap.net> References: <6d1bcbbadc5c7f6c@mx2.catap.net> Message-ID: Hello! On Wed, Jun 26, 2024 at 10:32:29AM +0100, Kirill A. Korinsky wrote: > On Wed, 26 Jun 2024 02:42:01 +0100, > Maxim Dounin wrote: > > > > Could you please provide some more details about specific use > > cases for such a feature? > > > > As far as I see, using appropriate > > > > proxy_set_header X-Original-Foo $http_foo; > > > > directives should be mostly equivalent, except it won't preserve > > the headers order and will merge duplicate headers. But these > > aren't really guaranteed by HTTP anyway, and merging/reorder can > > happen on any intermediate hosts. > > > > Well, this application needs to preserve HTTP header order and Nginx is used > here as a way to cache some requests. > > Nginx runs with a module that parses requests to extract some value that can > be used as cache key, and an application behind Nginx controls when request > can be safely cached via special header in response. > > Before Nginx it uses load balancers, but all of them in TCP mode because > headers order is quite important here. > > So, provided patch allows to send the headers in the original order. > > Thus, it uses one more patch which bring an option to tolerate invalid and > mallformed URI in requests, which I plan to share as the next step. Well, this looks like highly specialized use case focusing on non-guaranteed HTTP properties. While it might be nice to support this use case as well, it seems to be at most optional. And I can't say I like the proposed solution. -- Maxim Dounin http://mdounin.ru/ From kirill at korins.ky Thu Jun 27 19:30:03 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Thu, 27 Jun 2024 20:30:03 +0100 Subject: Send original headers to the upstream In-Reply-To: References: <6d1bcbbadc5c7f6c@mx2.catap.net> Message-ID: <00ce61c794e2fb4a@mx1.catap.net> On Thu, 27 Jun 2024 20:18:10 +0100, Maxim Dounin wrote: > > Well, this looks like highly specialized use case focusing on > non-guaranteed HTTP properties. > > While it might be nice to support this use case as well, it seems > to be at most optional. And I can't say I like the proposed > solution. > It should defently be optional, and at least make differences between HTTP headers that are added by Nginx and HTTP headers that are sent by the client... seems like a good idea to me, doesn't it? It makes things cleaner and allows to avoid injections of some unexpected headers, like X-Real-IP, which might be used by complicated application by some side effect. P.S. I'm testing an updated patch, I think to send it soon. -- wbr, Kirill From kirill at korins.ky Fri Jun 28 00:03:13 2024 From: kirill at korins.ky (Kirill A. Korinsky) Date: Fri, 28 Jun 2024 01:03:13 +0100 Subject: Send original headers to the upstream In-Reply-To: References: Message-ID: <00ce6397ae61d4c4@mx1.catap.net> Greetings, Retesting the patch requires some time. On Wed, 26 Jun 2024 02:42:01 +0100, Maxim Dounin wrote: > > Note that as implemented, you won't be able to distinguish > original requests headers from the ones with prefix added. E.g., > assuming prefix "X-Original-", and original request headers: > > Host: foo > X-Original-Host: bar > > you'll get > > X-Original-Host: foo > X-Original-Host: bar > > in the upstream request, and you won't be able to tell which one > is real. > > Following the exiting proxy_set_header behaviour, I would rather > suggests that such a feature, if implemented, should drop all the > headers with the configured prefix. > [...] > > Not sure if reusing "proxy_pass_request_headers" for such a > feature is a good idea. From the behaviour of > "proxy_pass_request_headers off;", I would rather assume that > "proxy_pass_request_headers X-Original-;" will pass all the > headers with the specified prefix, and not just the headers which > were modified. > I agree that design when "proxy_pass_request_headers X-Original-;" simple maps all original headers into X-Original-BlaBla is cleaner. It also allows to avoid need to drop any header as well. So, here the updated patch which address all minor remarks as well: https://freenginx.org/pipermail/nginx/2024-June/000238.html diff --git src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_proxy_module.c index 536482ec5..7622360d2 100644 --- src/http/modules/ngx_http_proxy_module.c +++ src/http/modules/ngx_http_proxy_module.c @@ -117,6 +117,8 @@ typedef struct { ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; + ngx_str_t request_headers_prefix; + #if (NGX_HTTP_SSL) ngx_uint_t ssl; ngx_uint_t ssl_protocols; @@ -215,6 +217,8 @@ static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #if (NGX_HTTP_CACHE) static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -445,9 +449,9 @@ static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_pass_request_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_http_proxy_pass_request_headers, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers), + 0, NULL }, { ngx_string("proxy_pass_request_body"), @@ -1386,9 +1390,11 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) i = 0; } - if (ngx_hash_find(&headers->hash, header[i].hash, - header[i].lowcase_key, header[i].key.len)) - { + if (plcf->request_headers_prefix.len > 0) { + len += plcf->request_headers_prefix.len; + } else if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, + header[i].key.len)) { continue; } @@ -1522,9 +1528,13 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) i = 0; } - if (ngx_hash_find(&headers->hash, header[i].hash, - header[i].lowcase_key, header[i].key.len)) - { + if (plcf->request_headers_prefix.len > 0) { + b->last = ngx_copy(b->last, + plcf->request_headers_prefix.data, + plcf->request_headers_prefix.len); + } else if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, + header[i].key.len)) { continue; } @@ -3349,6 +3359,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) * conf->body_values = NULL; * conf->body_source = { 0, NULL }; * conf->redirects = NULL; + * conf->request_headers_prefix = { NULL, 0 }; * conf->ssl = 0; * conf->ssl_protocols = 0; * conf->ssl_ciphers = { 0, NULL }; @@ -3727,6 +3738,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.intercept_errors, prev->upstream.intercept_errors, 0); + ngx_conf_merge_str_value(conf->request_headers_prefix, + prev->request_headers_prefix, ""); + #if (NGX_HTTP_SSL) if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) { @@ -4772,6 +4786,36 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + + if (plcf->upstream.pass_request_headers != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.pass_request_headers = 0; + return NGX_CONF_OK; + } + + plcf->upstream.pass_request_headers = 1; + + if (ngx_strcmp(value[1].data, "on") == 0) { + return NGX_CONF_OK; + } + + plcf->request_headers_prefix = value[1]; + + return NGX_CONF_OK; +} + + #if (NGX_HTTP_CACHE) static char * -- wbr, Kirill