Opened 7 years ago
Closed 7 years ago
#1684 closed defect (fixed)
documentation for ngx_http_geo_module is inconsistent with the module's behavior when the server is accessed through a unix domain socket
| Reported by: | Owned by: | ||
|---|---|---|---|
| Priority: | minor | Milestone: | |
| Component: | nginx-module | Version: | 1.15.x |
| Keywords: | Cc: | ||
| uname -a: | Linux debdeb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 GNU/Linux | ||
| nginx -V: |
nginx version: nginx/1.15.7
built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) configure arguments: --with-debug --prefix=/home/ngx/built_from_source --without-http_gzip_module |
||
Description
The documentation for ngx_http_geo_module states that "By default, the address is taken from the $remote_addr variable[...]", and that "If the value of a variable does not represent a valid IP address then the “255.255.255.255” address is used."
By this description, one could expect the following two blocks to behave exactly the same way:
geo $foo {
255.255.255.255 0;
default 1;
}
geo $remote_addr $bar {
255.255.255.255 0;
default 1;
}
but when the server is accessed through a unix domain socket, the variables $foo and $bar evaluate to two different values: 1 and 0, respectively.
Attached are a simple nginx config file and a python script which can be used to reproduce this issue.
Attachments (2)
Change History (8)
by , 7 years ago
| Attachment: | nginx.conf added |
|---|
by , 7 years ago
| Attachment: | fetch_values.py added |
|---|
follow-up: 2 comment:1 by , 7 years ago
follow-up: 3 comment:2 by , 7 years ago
Replying to mdounin:
This seems to be a bug in the geo module, it doesn't know that there can be AF_UNIX addresses, and treats such addresses as AF_INET. The following patch should fix this:
Thanks for your response. I just applied the patch, and with limited testing, it seems to fix the issue for the default cidr mode, but apparently in the ranges mode, there's a different issue, which makes it impossible to define rules which would match 255.255.255.255. For example, with a configuration like this:
geo $foo {
ranges;
default 1;
255.255.255.255-255.255.255.255 0;
}
it exits with the message invalid range "255.255.255.255-255.255.255.255".
It's likely not related to this issue at all, but it just happens to get in the way when attempting to test the patch, because it's not possible to verify that connecting via a unix domain socket correctly makes the module treat the connection as coming from 255.255.255.255.
The module tests the start and end of the range for equality with INADDR_NONE, which is obsolete and the documentation (https://www.gnu.org/software/libc/manual/html_node/Host-Address-Functions.html) says this:
It is obsolete because INADDR_NONE is a valid address (255.255.255.255)
Should this be tackled in another ticket?
follow-up: 4 comment:3 by , 7 years ago
Replying to gdrbyKo1@…:
Thanks for your response. I just applied the patch, and with limited testing, it seems to fix the issue for the default
cidrmode
Thanks for testing.
but apparently in the
rangesmode, there's a different issue, which makes it impossible to define rules which would match255.255.255.255.
This is because the 255.255.255.255 address cannot be a valid IP address except as a broadcast destination, and used to indicate errors when parsing addresses.
In the ranges mode, you can match 255.255.255.255 by using the default parameter. The particular problem can be tested as follows:
geo $rfoo {
ranges;
default 0;
0.0.0.0-255.255.255.254 1;
}
geo $remote_addr $rbar {
ranges;
default 0;
0.0.0.0-255.255.255.254 1;
}
Should this be tackled in another ticket?
No, thank you. I do not think there are practical reasons to allow 255.255.255.255 in ranges, and it is not needed to test the particular problem with unix sockets.
comment:4 by , 7 years ago
Replying to mdounin:
That makes sense, thank you. Originally, I wanted to (ab?)use this module to create special locations accessible only locally, pretty much like it can be done with the ngx_http_access_module, except I wanted the returned status code to be 404 rather than 403, which, as far as I know, cannot be done with that module. So I ended up using ngx_http_geo_module like this:
geo $access_denied {
default 1;
255.255.255.255 0; # unix domain socket
127.0.0.1 0;
}
location = /secret {
if ($access_denied) {
return 404;
}
return 200 "secret";
}
So by intuition, for the ranges mode, I came up with this:
geo $access_denied {
ranges;
default 1;
255.255.255.255-255.255.255.255 0; # unix domain socket
127.0.0.1-127.0.0.1 0;
}
Which resulted in the issue I described in my previous comment. But taking what you've said into consideration, I think the same could be achieved like this:
geo $access_denied {
ranges;
default 0; # unix domain socket
127.0.0.1-127.0.0.1 0;
0.0.0.0-126.255.255.255 1;
127.0.0.2-255.255.255.254 1;
}
I think this issue is resolved now, thank you for your prompt replies and help :)

This seems to be a bug in the geo module, it doesn't know that there can be AF_UNIX addresses, and treats such addresses as AF_INET. The following patch should fix this:
# HG changeset patch # User Maxim Dounin <mdounin@mdounin.ru> # Date 1544114666 -10800 # Thu Dec 06 19:44:26 2018 +0300 # Node ID 92140d36b98b9a38ccc2ac8a8e975792d76336f0 # Parent 2117637f64e981e0e14c3a4b0509252fefd8a78a Geo: fixed handling of AF_UNIX client addresses (ticket #1684). Previously, AF_UNIX client addresses were handled as AF_INET, leading to unexpected results. diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -215,6 +215,13 @@ ngx_http_geo_cidr_variable(ngx_http_requ break; #endif +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + vv = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE); + break; +#endif + default: /* AF_INET */ sin = (struct sockaddr_in *) addr.sockaddr; inaddr = ntohl(sin->sin_addr.s_addr); @@ -277,6 +284,12 @@ ngx_http_geo_range_variable(ngx_http_req break; #endif +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + inaddr = INADDR_NONE; + break; +#endif + default: /* AF_INET */ sin = (struct sockaddr_in *) addr.sockaddr; inaddr = ntohl(sin->sin_addr.s_addr);