Opened 3 years ago

Closed 3 years ago

#2259 closed defect (invalid)

Variables in proxy_pass url lead to unresovable host if host is only defined in /etc/hosts

Reported by: func0der Owned by:
Priority: major Milestone: nginx-1.21
Component: nginx-module Version:
Keywords: ngx_http_proxy_module Cc:
uname -a: Linux nginx-host 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.21.3
built with OpenSSL 1.1.1d 10 Sep 2019
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-1.21.3=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module

Description

Setup:

Have a /etc/hosts files on your nginx machine:

127.0.0.1 nginx-host

127.0.0.1 nginx-proxy-hostname # just as an example, the real mirror would be on another server

Now create an nginx server /etc/nginx/sites-enabled/default config like the following

server {
   server_name nginx-host;

   location / {
     proxy_pass            http://nginx-proxy-hostname/dummy$is_args$args;
     proxy_read_timeout    90s;
     proxy_connect_timeout 90s;
     proxy_send_timeout    90s;
     proxy_set_header      Host $host;
     proxy_set_header      X-Real-IP $remote_addr;
     proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header      Proxy "";
  }

  location /dummy {
     return 200 OK;
  }
}

No we do a request towards: http://nginx-host/?cmd=dosomething&another=parameter&foo=bar

curl 'http://nginx-host/?cmd=dosomething&another=parameter&foo=bar'

Now there is an error message in the error.log:

2021/10/18 15:45:23 [error] 31052#31052: *1 no resolver defined to resolve nginx-proxy-hostname, client: 127.0.0.1, server: nginx-host, request: "GET /?cmd=dosomething&another=parameter&foo=bar HTTP/1.1", host: "nginx-host"

Nginx is not able to resolve nginx-proxy-hostname.

If we remove the variables from proxy_pass in the directive of /, it works without a problem.

  location / {
    proxy_pass            http://nginx-proxy-hostname/dummy;

    #....

The hostname is resolved, the request reaches the correct server.

This behavior leaves two possible scenarios in the context of mirror requests:

  1. The resolver is not involved everytime a hostname has to be resolved in proxy_pass. It gets involved as soon as we use variables in the proxy_pass string. This is supported by the fact, that a resolver can be added in the nginx config, the variables are still removed from proxy_pass and everything is working fine.
  1. There is a bug in the resolve process of proxy_pass as soon as variables are involved.

Either way, this behavior is inconsistent.

Personally I would rather have nginx consider the entries in /etc/hosts all the time, so one does not have the hassle to setup a dns resolver.
If that is the solution to this specific problem: Idk

Change History (2)

comment:1 by func0der, 3 years ago

Seems like this is intended behavior and I did just overread the part in the documentation:

https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

Parameter value can contain variables. In this case, if an address is specified as a domain name, the name is searched among the described server groups, and, if not found, is determined using a resolver.

My bad, sorry. This can probably be closed then.

comment:2 by Maxim Dounin, 3 years ago

Resolution: invalid
Status: newclosed

Indeed, this behaviour is as intended and explicitly documented.

Just in case it is not clear, the root cause is as follows. System resolver interface, getaddrinfo() (or gethostname() on systems without IPv6 support), is blocking. Since nginx is event-based server, it cannot afford blocking at runtime. So it can only use getaddrinfo() while parsing the configuration, but not at runtime / while processing requests. On the other hand, variables in the proxy_pass directive imply that nginx has to somehow resolves names at runtime. To be able to resolve names at runtime nginx implements a simple DNS client, configured with the resolver directive, which is able to resolve names without blocking. This DNS client is not identical to the system resolver, and does not check non-DNS name resolution interfaces, such as local files.

Note: See TracTickets for help on using tickets.