Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#2198 closed defect (invalid)

Authorization from auth_request

Reported by: B3r3n06@… Owned by:
Priority: major Milestone:
Component: nginx-core Version:
Keywords: Cc:
uname -a: Linux vigrid-gns3 5.4.0-74-generic #83-Ubuntu SMP Sat May 8 02:35:39 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.21.0
built by gcc 9.3.0 (Ubuntu 9.3.0-10ubuntu2)
built with OpenSSL 1.1.1f 31 Mar 2020
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.21.0/debian/debuild-base/nginx-1.21.0=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

Description

Hello,

I got a situation that cant be explained.
On 2 servers exactly the same, both are:

  • with Ubuntu 20.04.2LTS from scratch
  • NGinx 1.21.0 from apt after added your repo
  • Same PHP-FPM (7.4) configuration & modules
  • Same NGinx configs (main & auth server)
  • Same permissions/ownership at all levels (sock, config files etc).

Despite this, one auth_request sent to the backoffice server (that updates the Authorization header) works but the other fails.

FAILS means the Authorization header is changed at the back office auth site, but the master server does not receive the header updated, still keeping and sending the old one from client to the proxied server.

I tried with more_set_headers as well, no change.

The dump & configs details you have are EXACTLY the same on both servers, IP included...
I faced this already once, but NGinx was 1.14. Once updated to 1.19.0, the issues was solved, so I consider it was a NGinx version issue. Here .21 (>.19) should be ok then...

Thanks for your feedback

Brgrds

CALLING CONFIG:

location /subdir
{

auth_request /auth;
auth_request_set $auth_status $upstream_status;

proxy_pass http://172.29.0.254:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;

}

location = /auth
{

internal;
proxy_pass http://localhost:8001;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;

}

## Back office Authenticating site:

server {

listen 127.0.0.1:8001;
server_name localhost;

access_log /var/log/nginx/vigrid_auth-access.log;
error_log /var/log/nginx/vigrid_auth-error.log;

root /home/gns3/vigrid/www/auth;
index vigrid-auth.php;

# hide version
server_tokens off;

location ~ /\.ht {

deny all;

}

location /
{

# cleaning
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { log_not_found off; }

location ~ \.css { add_header Content-Type text/css; }
location ~ \.js { add_header Content-Type application/x-javascript; }
location ~ \.eot { add_header Content-Type application/vnd.ms-fontobject; }
location ~ \.woff { add_header Content-Type font/woff; }

location ~* \.(htm|html|php)$
{

try_files $uri =404;
fastcgi_split_path_info (.+\.php)(/.+)$;
fastcgi_index vigrid-auth.php;
fastcgi_pass_header Authorization;
include /etc/nginx/fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

}

location ~ /(images|javascript|js|css|flash|media|static|font)/ {

expires 7d;

}

location ~ /\.ht {

deny all;

}

try_files $uri $uri/ /vigrid-auth.php?$args;

}

}

Change History (6)

comment:1 by B3r3n06@…, 3 years ago

"I faced this already once, but NGinx was 1.14. Once updated to 1.19.0, the issues was solved, so I consider it was a NGinx version issue. Here .21 (>.19) should be ok then..."

I face it another time of course :-)

comment:2 by Maxim Dounin, 3 years ago

FAILS means the Authorization header is changed at the back office auth site, but the master server does not receive the header updated, still keeping and sending the old one from client to the proxied server.

Could you please clarify a bit more what do you mean here?

Note that nginx never changes the Authorization request header, and it is not expected to (unless you've explicitly configured something like proxy_set_header Authorization ...;). The only thing that the auth_request is expected to do is explicitly documented: when access is denied with the 401 error code, it returns the WWW-Authenticate response header to the client, so the client can provide acceptable authentication information via the Authorization request header in the next request.

Overall, this doesn't look like a bug in nginx, but rather a misbehaviour of the code which handles authorization subrequests.

comment:3 by llevier@…, 3 years ago

Hello,

Since mainline might not be 'stable' version, retested with 1.20.0, same thing.

Tested authorization using new config, NGinx behaves better but is missing many URLs it received.
New config (where it changed):

location /subdir
{

auth_request /auth;
auth_request_set $auth_status $upstream_status;

added -> auth_request_set $auth_header $upstream_http_authorization;

proxy_pass http://172.29.0.254:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;

added -> proxy_set_header Authorization $auth_header;

}

in reply to:  2 comment:4 by llevier@…, 3 years ago

Hello Maxim,

Replying to Maxim Dounin:

Could you please clarify a bit more what do you mean here?

Note that nginx never changes the Authorization request header, and it is not expected to (unless you've explicitly configured something like proxy_set_header Authorization ...;). The only thing that the auth_request is expected to do is explicitly documented: when access is denied with the 401 error code, it returns the WWW-Authenticate response header to the client, so the client can provide acceptable authentication information via the Authorization request header in the next request.

Overall, this doesn't look like a bug in nginx, but rather a misbehaviour of the code which handles authorization subrequests.

Let me illustrate with an example. CURL calls for this URL, adding web auth:
curl -kv -u vigrid:vigrid -A "Client" -X GET https://localhost/subdir/version

NGinx receives this request, because of /subdir, it is specically handled by a location.
This location includes an auth_request and so it is sent to the back office auth server.
This auth server (port 8001) receives and validates access.
IT ALSO CHANGES the Authorization header to put another auth (the credentials of the real final server are unique, so the auth part also manages this).
Normally, this updated header is sent back to the 'calling NGinx location' and then proxy does its job, sending the same URL (upgrading it to websocket) with the new Authorization header.

On a fresh Ubuntu 20.04.2LTS, it fails. On the same Ubuntu 20.04.2 LTS but olrder, same software versions etc (tested nginx/PHP Versions a config files, all dev pages etc, diff = zero with failing config), it works.

Since this is NGinx internal to me, I dont except Ubuntu to interfere. Since NGinx/PHP versions are exactly the same as well as config files, I dont expect a different behavior.

That is my point...

Now if that is not expected that changing authorization header at /auth_request site is returned to the caller, it is a real improvement to be considered. With this, NGinx can be turned to an Rproxy with ACLs, just with simple back-end scripts...

Please notice I tried a hack supposed to work, it works more or less. I added to the location /subdir:

auth_request_set $auth_header $upstream_http_authorization;

proxy_set_header Authorization $auth_header;

Thanks

Brgrds

Last edited 3 years ago by llevier@… (previous) (diff)

comment:5 by Maxim Dounin, 3 years ago

Resolution: invalid
Status: newclosed

IT ALSO CHANGES the Authorization header to put another auth (the credentials of the real final server are unique, so the auth part also manages this).

As explained above, this is not how it is expected to work. The Authorization header i the request header, and it is expected to come from the client. If you want to change it to something different in proxied requests, you have to explicitly configure nginx to do so by using appropriate proxy_set_header.

Normally, this updated header is sent back to the 'calling NGinx location' and then proxy does its job, sending the same URL (upgrading it to websocket) with the new Authorization header.

Again, this is not what expected to happen. If this indeed happens on some of your servers, this probably means that you are running locally modified nginx which behaves differently from what it is expected to do. But most likely it happen to work for some other reason. You may want to check what actually happens on the wire - notably, what's in the headers from the client to nginx, from auth backend to nginx, and from nginx to the real backend.

Either way, thank you for the details. Certainly this does not look like a bug in nginx, as nginx is simply not expected to do what you think it should, so closing this. If you need help with configuring nginx, consider using support options available.

in reply to:  5 comment:6 by llevier@…, 3 years ago

Replying to Maxim Dounin:

IT ALSO CHANGES the Authorization header to put another auth (the credentials of the real final server are unique, so the auth part also manages this).

As explained above, this is not how it is expected to work. The Authorization header i the request header, and it is expected to come from the client. If you want to change it to something different in proxied requests, you have to explicitly configure nginx to do so by using appropriate proxy_set_header.

Ok, no problemo, then there is a "bug that is a feature" :-)
I mean it happens in multiple of my hosts, despite it should not.

Normally, this updated header is sent back to the 'calling NGinx location' and then proxy does its job, sending the same URL (upgrading it to websocket) with the new Authorization header.

Again, this is not what expected to happen. If this indeed happens on some of your servers, this probably means that you are running locally modified nginx which behaves differently from what it is expected to do. But most likely it happen to work for some other reason. You may want to check what actually happens on the wire - notably, what's in the headers from the client to nginx, from auth backend to nginx, and from nginx to the real backend.

Either way, thank you for the details. Certainly this does not look like a bug in nginx, as nginx is simply not expected to do what you think it should, so closing this. If you need help with configuring nginx, consider using support options available.

Since that, the location became:

location /subdir
{

auth_request /auth;
auth_request_set $auth_status $upstream_status;
auth_request_set $auth_header $upstream_http_authorization;

proxy_pass http://172.29.0.254:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header Authorization $auth_header;

}

That is supposed to get the Authorization header from the /auth page.
Since that, as I indicated earlier, it seems to be the appropriate way but...it works more or less. In other terms, it does not work at all times as it should be.

Keep digging thes differences...

Version 2, edited 3 years ago by llevier@… (previous) (next) (diff)
Note: See TracTickets for help on using tickets.