Search code examples
sslherokunginxconfigurationreverse-proxy

Nginx reverse proxy to Heroku fails SSL handshake


I'm unfortunately not much of a system administrator and have come upon a problem that has me banging my head against the wall.

The short story is that I'm running Nginx on EC2 (Ubuntu 14.04.4 LTS) to (a) host my company's marketing site (https://example.com, which incidentally is Wordpress) and (b) serve as a reverse proxy to our Rails app running on Heroku (https:// app.example.com), for certain paths. We use the same SSL certificate for both example.com and app.example.com. All of this has worked great for 8-10 months, but I recently switched from Heroku's paid SSL addon to the new free SSL offering, and now our reverse proxy is broken.

In checking the Nginx error logs, I see the following:

SSL_do_handshake() failed (SSL: error:14094438:SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: ipaddress1, server: example.com, request: "GET /proxiedpath/proxiedpage HTTP/1.1", upstream: "https:// ipaddress2:443/proxiedpath/proxiedpage", host: "example.com"

I've tried to search around for some additional guidance - I've upgraded Nginx (1.10.1) and OpenSSL (1.0.2h) with no luck. I suspected the issue might be due to Heroku's use of SNI in the new free SSL feature (https://devcenter.heroku.com/articles/ssl-beta), but haven't been able to determine why this might be a problem.

A few additional points on my exploration to this point:

  • When I switched to the new free Heroku SSL, I changed our app.example.com DNS record to point to app.example.com.herokudns.com, as instructed by the docs. The application can be accessed normally through app.example.com and when I run an nslookup on app.example.com and app.example.com.herokudns.com, I get the same IP address back. However...

  • I cannot access the application through either the IP address returned from nslookup or app.example.com.herokudns.com. I suspect this is normal and expected but don't know enough to say exactly why this is. And...

  • The IP address returned from the nslookup is NOT the same as the IP address referenced in the log's error message above ("ipaddress2"). In fact, "ipaddress2" is not consistent throughout the logs - it seems to change regularly. Again I don't know enough to know what I don't know... load balancing on Heroku's side?

And finally, my Nginx reverse proxy is configured as follows in nginx.conf:

http {

    client_max_body_size 500M;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server_names_hash_bucket_size 64;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

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

    gzip on;
    gzip_disable "msie6";

    server {

        listen 443 default_server;
        server_name example.com;

        root /usr/share/nginx/html;
        index index.php index.html index.htm;

        ssl on;
        ssl_certificate mycompanycert.crt;
        ssl_certificate_key mycompanykey.key;

        ssl_session_timeout 5m;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
        ssl_prefer_server_ciphers on;

        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;

        location / {
            try_files $uri $uri/ /index.php?q=$uri&$args;
        }

        location ^~ /proxiedpath/ {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Proto https;
            proxy_pass https://app.example.com/proxiedpath/;
        }

    }

}

Any help is greatly appreciated - thanks very much!


Solution

  • I was able to solve this today and wanted to post the solution in case others run into the same issue.

    It turns out that the problem was related to SNI after all. I found this ticket on nginx.org:

    https://trac.nginx.org/nginx/ticket/229

    Which led me to the proxy_ssl_server_name directive:

    http://nginx.org/r/proxy_ssl_server_name

    By setting to "on" in your config, you'll be able to proxy to upstream hosts using SNI.

    Thanks to all who commented with suggestions!