Opened 8 months ago
Last modified 8 months ago
#2625 new defect
nginx proxy_pass variable DNS resolution not updated when there is another proxy_pass with same domain and without variable
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | nginx-module | Version: | 1.14.x |
Keywords: | proxy_pass dns | Cc: | lkgendev@… |
uname -a: | Linux dba-tlv-wkrlxy 4.18.0-477.10.1.el8_8.x86_64 #1 SMP Wed Apr 5 13:35:01 EDT 2023 x86_64 x86_64 x86_64 GNU/Linux | ||
nginx -V: |
nginx version: nginx/1.14.1
built by gcc 8.2.1 20180905 (Red Hat 8.2.1-3) (GCC) built with OpenSSL 1.1.1 FIPS 11 Sep 2018 (running with OpenSSL 1.1.1k FIPS 25 Mar 2021) TLS SNI support enabled configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E' |
Description (last modified by )
according to the documentation of nginx dns disccovery, when using proxy_pass with a variable for the domain, the variable should be reresolved upon DNS change of IP
however when there are two proxy_pass locations, one with variable and another without variable for the same domain name, it appears that the proxy_pass with variable does not re-resolves the DNS IP changes of the domain name and stays with the IP at the beginning even if DNS changed the domain IP
To reproduce on Redhat 8
- install nginx: sudo yum install nginx
- start local dns that can read /etc/hosts entries: sudo systemctl start systemd-resolved
- modify the nginx configuration file /etc/nginx/nginx.conf
3a. add to the log_format $upstream_addr e.g.
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" "$upstream_addr"';
3b. add two proxy_pass, one with variable resolver of 127.0.0.53 with valid of 1 second and the other without variable
location /test1 { resolver 127.0.0.53 valid=1s; set $testbackend https://test.local; proxy_pass $testbackend; } location /test2 { proxy_pass https://test.local; }
- add to /etc/hosts an entry for test.local pointing to 127.0.0.200
127.0.0.200 test.local
- use dig to check the DNS resolution of test.local: dig @127.0.0.53 test.local
check IP is resolved to 127.0.0.200
- restart nginx for configuration to take effect: sudo systemctl start nginx
- use curl to connect to the test1 location: curl localhost:80/test1
- check nginx access log last lines, there should be upstream address of 127.0.0.200:443
tail -20 /var/log/nginx/access.log
...
::1 - - [04/Apr/2024:15:07:46 +0300] "GET /test1 HTTP/1.1" 502 4020 "-" "curl/7.61.1" "-" "127.0.0.200:443"
upstram IP is 127.0.0.200
- change the /etc/hosts entry for test.local to have a different IP 127.0.0.201 instead of the previous 127.0.0.200
127.0.0.201 test.local
- use dig to check the DNS resolution of test.local: dig @127.0.0.53 test.local
check IP is resolved to 127.0.0.201
- wait for 30 seconds so the valid time for the DNS entry should expire for the resolver
- use curl to connect to the test1 location: curl localhost:80/test1
- check nginx access log last lines, the upstream address should change to 127.0.0.201:443 but it will remain as before
tail -20 /var/log/nginx/access.log
...
::1 - - [04/Apr/2024:15:17:27 +0300] "GET /test1 HTTP/1.1" 502 4020 "-" "curl/7.61.1" "-" "127.0.0.200:443"
Attachments (1)
Change History (7)
by , 8 months ago
Attachment: | nginx.conf added |
---|
comment:1 by , 8 months ago
Description: | modified (diff) |
---|
comment:3 by , 8 months ago
When using variables in proxy_pass
, nginx always tries to find an existing upstream by the name evaluated in runtime. If the upstream exists, it's used, otherwise DNS resolve is performed and a temporary upstream is created on the fly. In your case such an upstream always exists since it's created in config-time by the second proxy_pass
.
Also, editing /etc/hosts
would not help anyway since nginx does not read this file in runtime. Instead, when resolving names in runtime, it sends a request directly to the resolver. In contrast, when resolving names in config-time, nginx uses libc resolve functions which do read /etc/hosts
.
comment:4 by , 8 months ago
According to the documentation, the resolver valid keyword specifies that variables need to re-resolve the DNS name after the time set in valid
"
When you use a variable to specify the domain name in the proxy_pass directive, NGINX re‑resolves the domain name when its TTL expires. You must include the resolver directive to explicitly specify the name server (NGINX does not refer to /etc/resolv.conf as in the first two methods). By including the valid parameter to the resolver directive, you can tell NGINX to ignore the TTL and re‑resolve names at a specified frequency instead. Here we tell NGINX to re‑resolve names every 10 seconds.
"
The same issue will happen also with a regular DNS so it is not /etc/hosts related, using the 127.0.0.53 as a local DNS was used just to simplify the use case reproduction
nginx configuration to reproduce the DNS resolution problem with domain name test.local