Opened 23 months ago
Closed 22 months ago
#2435 closed defect (fixed)
static locations longer than 255 characters lead to unexpected behaviors
Reported by: | Max Laverse | Owned by: | |
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | documentation | Version: | 1.23.x |
Keywords: | Cc: | Max Laverse | |
uname -a: | Darwin Maximes-MacBook-Pro.local 22.2.0 Darwin Kernel Version 22.2.0: Fri Nov 11 02:03:51 PST 2022; root:xnu-8792.61.2~4/RELEASE_ARM64_T6000 arm64 | ||
nginx -V: |
nginx version: nginx/1.23.4
built by clang 14.0.0 (clang-1400.0.29.202) configure arguments: --with-debug |
Description
Description
Defining location
s with paths longer than 255 characters leads to unexpected behaviors with some or almost all locations being ignored.
Probable Root Cause
When Nginx starts, it copies the location
s read from the configuration into a tree. The locations are initially stored in a structure allowing large strings, but once copied in ngx_http_create_locations_tree()
, the variable holding the length of the location is cast to an u_char
. For static locations having a long path, the original size is eventually altered leading to a misrepresentation of the data and unexpected results.
How to reproduce
Using the following configuration:
daemon off; master_process off; error_log /dev/stderr debug; events { worker_connections 4096; } http { server { server_name localhost; location = /43210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321 { return 200; } location = /543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321 { return 200; } location = /6543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321 { return 200; } location / { return 500; } } }
The expected result would be 200 if you call any of the first 3 URLs. The actual result in my environment is 200, 200, 500.
If I add this additional block:
location = /76543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321 { return 200; }
all URLs now returns 500.
Change History (7)
comment:1 by , 22 months ago
Status: | new → accepted |
---|
comment:2 by , 22 months ago
Thanks for the quick reply.
I don't know if more changes are needed to fix a similar problem in different conditions, but those two changes make sense to me. I confirmed with my test case and additional debugging log lines that it fixes the issue I was seeing. I also tried to increase the location's size close to 4096 which worked without trouble until I eventually caused a configuration loading error "too long parameter", and that's expected according to your previous message.
comment:3 by , 22 months ago
Thanks for testing. Could you please share some details about the use case where you've encountered the issue? It's somewhat uncommon to use locations with such long prefixes, and it would be interesting to know how these are used in practice.
comment:4 by , 22 months ago
Sure. We're using Nginx within our Ingress Controllers instance for Kubernetes clusters. It's not the version provided by nginx.com, but the one from the Kubernetes project: https://github.com/kubernetes/ingress-nginx
The Ingress Controller offers an external authentication feature based on the ngx_http_auth_request_module. When enabled, this feature exposes the authentication service through an internal location block under the same server{}. The path of the location is generated by the controller, and it's the base64 value of the path you actually want to protect.
In our case, we encountered this issue with a regexp that contained a lot of different patterns: "/base-path/(pattern1|pattern2|pattern3|...)". With a long regular expression and base64 encoding on top of it + some prefixes and suffixes added by the controller, you eventually end up with this internal authentication location being longer than 255 characters.
comment:5 by , 22 months ago
Thanks for the details.
Just for the record, I've submitted the patch for review:
https://mailman.nginx.org/pipermail/nginx-devel/2023-January/VHNST4UCT7ARWHKIQ4CDFSBHDPULUFHN.html
comment:7 by , 22 months ago
Resolution: | → fixed |
---|---|
Status: | accepted → closed |
Fix committed, thanks for reporting this.
Thanks for the report.
Indeed, the code does not expect a location prefix to be longer than 255 bytes, and handles this incorrectly. The most simple fix would be to use u_short instead. Patch:
Review and testing appreciated.