Opened 11 months ago

Last modified 7 months ago

#1330 accepted defect

OCSP stapling non-functional on IPv6-only host

Reported by: ramcq@… Owned by:
Priority: major Milestone:
Component: nginx-core Version: 1.10.x
Keywords: Cc:
uname -a: Linux front.flathub.org 3.10.0-514.26.2.el7.x86_64 #1 SMP Tue Jul 4 15:04:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: ginx version: nginx/1.10.2 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) built with OpenSSL 1.0.1e-fips 11 Feb 2013 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_geoip_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-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'

Description

I have an IPv6-only host running CentOS 7. I have a Lets Encrypt certificate on the host and I've enabled OCSP stapling per the Mozilla preferred SSL stuff. My provider has NAT64 set-up so I've configured their NAT64 resolvers in the resolve entry in nginx.conf.

        # OCSP Stapling ---
        # fetch OCSP records from URL in ssl_certificate and cache them
        ssl_stapling on;
        ssl_stapling_verify on;

        # verify chain of trust of OCSP response using Root CA and Intermediate certs
        ssl_trusted_certificate /etc/dehydrated/certs/flathub.org/chain.pem;

        resolver [2a00:1098:0:80:1000:3b:0:1] [2a00:1098:0:82:1000:3b:0:1];

I see this error:

2017/07/24 14:02:23 [error] 16637#0: connect() to 88.221.134.147:80 failed (101: Network is unreachable) while requesting certificate status, responder: ocsp.int-x3.letsencrypt.org

I believe that it's because this host returns two A and two AAAA results:

[root@front nginx]# host ocsp.int-x3.letsencrypt.org
ocsp.int-x3.letsencrypt.org is an alias for ocsp.int-x3.letsencrypt.org.edgesuite.net.
ocsp.int-x3.letsencrypt.org.edgesuite.net is an alias for a771.dscq.akamai.net.
a771.dscq.akamai.net has address 88.221.134.114
a771.dscq.akamai.net has address 88.221.134.147
a771.dscq.akamai.net has IPv6 address 2a02:26f0:e8::6856:6fb0
a771.dscq.akamai.net has IPv6 address 2a02:26f0:e8::6856:6f88

However the SSL stapling code only attempts to connect the first one: https://github.com/nginx/nginx/blob/9197a3c8741a8832e6f6ed24a72dc5b078d840fd/src/event/ngx_event_openssl_stapling.c#L1028

I've tried to work around with /etc/hosts but that seems unused, and OCSP stapling seems to disable itself if I have no resolver configuration entry. I can't seem to place an IPv6 address in the ssl_stapling_responder either.

Change History (5)

comment:1 follow-up: Changed 11 months ago by mdounin

  • Status changed from new to accepted

The problem is as follows:

  • OCSP stapling connection handling is very basic, and simply uses the first address returned. This is enough in most cases, even if some of the addresses returned are not reachable, because addresses returned by a resolver usually rotated.
  • This does not work with IPv6-only hosts though, as for compatibility reasons nginx always places IPv4 addresses first.

As a result, as long as a name resolves to both IPv4 and IPv6 addresses, an IPv4 address is always tried first. It is not reachable though, hence OCSP request fails.

The following workarounds should work out of the box though:

  • Remove the resolver directive from the configuration, and configure system resolver to only IPv6 addresses (e.g., via /etc/hosts). This way nginx will complain about ... [warn] ... no resolver defined to resolve ... on each request to the OCSP responder, though should work fine using addresses obtained from system resolver during configuration parsing.
  • Configure ssl_stapling_responder with an reachable address. Note though, that just using an IPv6 address of a real responder likely won't work, as the resulting request will contain an invalid Host header and likely will be rejected. It can be easily worked around using nginx itself though, with something like this:
    server {
        ...
        ssl_stapling on;
        ssl_stapling_responder http://[::1]:8082;
        ...
    }
    
    server {
        listen [::1]:8082;
    
        location / {
            proxy_pass http://ocsp.int-x3.letsencrypt.org;
        }
    }
    
    Proxy is smart enough to re-try requests if a connection fails, so it should work without additional filtering of addresses.

Proper fix would be to introduce fallback to different addresses in OCSP and/or something like resolver ... ipv4=off (similar to resolver ... ipv6=off we have now).

comment:2 Changed 10 months ago by mdounin

See also #1363.

comment:3 Changed 10 months ago by v10lator.myway.de@…

I wanted to add another workaround: Use a DNS A record filter proxy ( https://tools.ietf.org/html/draft-hazeyama-sunset4-dns-a-filter-00 ).
Sadly I couldn't find any implementation for this, so I used the howto at https://peteris.rocks/blog/dns-proxy-server-in-node-js-with-ui/ and changed the codes a bit. Here's the result: https://pastebin.com/W49EGiMV

So far this seems to have no drawbacks when used on a IPv6 only server but fixes quirks in other software, too.

EDIT: New codes: https://pastebin.com/2NTfJu84

Last edited 10 months ago by v10lator.myway.de@… (previous) (diff)

comment:4 in reply to: ↑ 1 ; follow-up: Changed 7 months ago by ramcq@…

Replying to mdounin:

The following workarounds should work out of the box though:

  • Configure ssl_stapling_responder with an reachable address. Note though, that just using an IPv6 address of a real responder likely won't work, as the resulting request will contain an invalid Host header and likely will be rejected. It can be easily worked around using nginx itself though, with something like this:
    server {
        ...
        ssl_stapling on;
        ssl_stapling_responder http://[::1]:8082;
        ...
    }
    
    server {
        listen [::1]:8082;
    
        location / {
            proxy_pass http://ocsp.int-x3.letsencrypt.org;
        }
    }
    

This workaround worked, with one tweak - as I mentioned in the original ticket the ssl_stapling_responder seemed unwilling to accept a bare IPv6 address in []s, so I put http://localhost in there instead.

comment:5 in reply to: ↑ 4 Changed 7 months ago by mdounin

Replying to ramcq@…:

This workaround worked, with one tweak - as I mentioned in the original ticket the ssl_stapling_responder seemed unwilling to accept a bare IPv6 address in []s, so I put http://localhost in there instead.

You have to use URL, not just an IP address. The configuration as written (with ssl_stapling_responder http://[::1]:8082;) works fine here.

Note: See TracTickets for help on using tickets.