#848 closed defect (fixed)
HTTP2 domain redirect error with ssl_verify_client
Reported by: | Jan Trejbal | Owned by: | Valentin V. Bartenev |
---|---|---|---|
Priority: | major | Milestone: | |
Component: | nginx-module | Version: | 1.9.x |
Keywords: | http2 client-certificate | Cc: | |
uname -a: | Linux bbb6fdf82301 3.10.0-229.20.1.el7.x86_64 #1 SMP Tue Nov 3 19:10:07 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux | ||
nginx -V: |
nginx version: nginx/1.9.7
built by gcc 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC) built with OpenSSL 1.0.1e-fips 11 Feb 2013 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --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-http_ssl_module --with-http_realip_module --with-http_addition_module --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_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' |
Description
server { server_name www.domain.tld; listen 443 ssl; listen 80; ssl_certificate /etc/nginx/certs/...; ssl_certificate_key /etc/nginx/certs/...; include ssl.conf; ssl_trusted_certificate /etc/nginx/certs/...; rewrite ^ https://domain.tld$request_uri? permanent; } server { server_name domain.tld; listen 443 ssl http2; listen 80; ssl_certificate /etc/nginx/certs/...; #trusted ssl_certificate_key /etc/nginx/certs/...; include ssl.conf; ssl_trusted_certificate /etc/nginx/certs/...; ssl_client_certificate /etc/nginx/certs/.../ca.crt; #not trusted ssl_verify_client optional; #use this produce error location /log { if ($ssl_client_verify != SUCCESS) { return 403; } ... } location / { ... } }
ssl.conf:
ssl_dhparam dhparam.pem; ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4'; ssl_session_cache shared:SSL:50m; ssl_session_timeout 5m; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s;
Redirect in Chrome from https://www.domain.tld to https://domain.tld produce 400 Bad Request (on https://domain.tld) and browser do not ask for certificate.
In log I see:
2015/12/04 00:38:38 [notice] 48#48: *448 "^" matches "/", client: MY_IP, server: www.domain.tld, request: "GET / HTTP/2.0", host: "www.domain.tld" 2015/12/04 00:38:38 [notice] 48#48: *448 rewritten redirect: "https://domain.tld/", client: MY_IP, server: www.domain.tld, request: "GET / HTTP/2.0", host: "www.domain.tld" 2015/12/04 00:38:38 [info] 48#48: *448 client attempted to request the server name different from that one was negotiated while processing HTTP/2 connection, client: MY_IP, server: www.domain.tld, host: "domain.tld"
This occurs when ssl_verify_client is set to optional|optional_no_ca|on
With not used http2 all is well. (Browser redirect and ask for client certificate)
Change History (14)
comment:1 by , 9 years ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:2 by , 9 years ago
Please try the following patch:
diff -r b1858fc47e3b -r 4b24c76a65ef src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c Fri Nov 06 15:22:43 2015 +0300 +++ b/src/http/ngx_http_header_filter_module.c Mon Dec 07 16:02:22 2015 +0300 @@ -100,7 +100,7 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "418 unused" */ /* ngx_null_string, */ /* "419 unused" */ /* ngx_null_string, */ /* "420 unused" */ - /* ngx_null_string, */ /* "421 unused" */ + ngx_string("421 Misdirected Request"), /* ngx_null_string, */ /* "422 Unprocessable Entity" */ /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ diff -r b1858fc47e3b -r 4b24c76a65ef src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Fri Nov 06 15:22:43 2015 +0300 +++ b/src/http/ngx_http_request.c Mon Dec 07 16:02:22 2015 +0300 @@ -2062,7 +2062,7 @@ ngx_http_set_virtual_server(ngx_http_req ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client attempted to request the server name " "different from that one was negotiated"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); return NGX_ERROR; } } diff -r b1858fc47e3b -r 4b24c76a65ef src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h Fri Nov 06 15:22:43 2015 +0300 +++ b/src/http/ngx_http_request.h Mon Dec 07 16:02:22 2015 +0300 @@ -95,6 +95,7 @@ #define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 +#define NGX_HTTP_MISDIRECTED_REQUEST 421 /* Our own HTTP codes */
comment:4 by , 9 years ago
Now it produce HTTP code 421 instead of 400, but same problem (do not show content of website)
comment:5 by , 9 years ago
Well, if it doesn't work with 421 either, it's probably nothing that can be done on nginx side to prevent such inappropriate connection reuse. You should complain to browser vendors instead.
comment:6 by , 9 years ago
Solved adding
server { server_name www.domain.tld; listen 443 ssl; http2_idle_timeout 10ms; }
comment:7 by , 9 years ago
I have the same problem.
I'm using ssl_verify_client optional and different subdomains to identify different projects.
If someone has a valid certificate and then changes subdomain to access a different project, authentication is required again and everything works fine.
If someone hasn't any valid certificate, he can access the first subdomain without problems, but if he changes subdomain, he gets a 400 error (client attempted to request the server name different from that one was negotiated while reading client request headers).
I think that the server should simply authenticate again if subdomain changes in order to solve this.
If ssl_verify_client is set to on or off, the problem disappears but I have to make changes to my web application to use only/avoid certificate authentication.
This happens with both Firefox and Chrome so I think I can't complain to browser vendors.
Meanwhile, I set http2_idle_timeout to 1s, but this reduces http2 advantages.
comment:11 by , 9 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
follow-up: 13 comment:12 by , 9 years ago
I am sure you're right, but I think the problem is a bit different from what you wrote.
I recap:
ssl_verify_client off, all subdomains can be browsed
ssl_verify_client on, all subdomains can be browsed
ssl_verify_client optional and client with certificate, all subdomains can be browsed
ssl_verify_client optional and client without certificate, error 400 (not 421) with the second subdomain (page appears without problems when I browse to the first subdomain).
This happens with latest versions of all browsers. Are you sure that this is on client's side?
Thanks again for your help.
comment:13 by , 9 years ago
ssl_verify_client off, all subdomains can be browsed
In this case client authorization isn't required and browser is allowed to request any domain in connection.
ssl_verify_client on, all subdomains can be browsed
ssl_verify_client optional and client with certificate, all subdomains can be browsed
It seems the fact that the client certificate is used prevents browser from connection reuse. In these cases browser uses separate connections as it should be.
ssl_verify_client optional and client without certificate
But in this case browser tries to reuse connection and gets 400. If you upgrade to 1.11.0, then the browser will get 421. This fixes Firefox since it handles 421 well.
Unfortunately, Chrome has a ticket about 421: https://bugs.chromium.org/p/chromium/issues/detail?id=546991
Browsers should either don't try to reuse connection, or handle the 421 response and resend request over a separate connection.
No connection reuse between different SNI-selected server{} blocks is allowed by nginx as long as SSL client certificates are used. It looks like HTTP/2 authors didn't learn anything from early versions of SPDY and again try to reuse connections to different servers.
Valentin, please take a look, see https://tools.ietf.org/html/rfc7540#section-9.1.1 for details.