Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nginx vs Apache proxy pass

Tags:

nginx

I am trying to convert my apache config to nginx. For apache I have following:

<VirtualHost *:443>
    ServerName loc.goout.net
    <Location />
        ProxyPass http://localhost:8080/ retry=0
        ProxyPreserveHost On
    </Location>
    <Location /i/>
        ProxyPass https://dev.goout.net/i/ retry=0
        ProxyPreserveHost Off
    </Location>
    ...

Like this, if I fetch:

https://loc.goout.net/i/user/606456_001_min.jpg

It correctly fetches content from:

https://dev.goout.net/i/user/606456_001_min.jpg

So for nginx I am trying this:

server {
    listen                  443 ssl;
    server_name             loc.goout.net;

    proxy_buffering         off;
    proxy_ssl_session_reuse off;

    proxy_redirect              off;
    proxy_set_header            Host            dev.goout.net;

    location /i/ {
        proxy_pass          https://dev.goout.net:443;
    }

But when I fetch the content, I will always get 502.

In nginx logs I see following:

[error] 7#7: *5 no live upstreams while connecting to upstream, client: 127.0.0.1, server: loc.goout.net, request: "GET /i/user/606456_001_min.jpg HTTP/1.1", upstream: "https://dev.goout.net/i/user/606456_001_min.jpg", host: "loc.goout.net"

Note the link: https://dev.goout.net/i/user/606456_001_min.jpg - which works correctly. It seems to me it still doesn't connect with SSL. I also tried to define the upstream section as:

upstream backend {
    server dev.goout.net:443;
}

But it had no effect.

Note the server is behind CloudFlare gateway, I hope it is not preventing the correct connection, but I guess that wouldn't work in apache either.

like image 250
Vojtěch Avatar asked Nov 27 '22 01:11

Vojtěch


1 Answers

tl;dr: SNI is off by default in nginx, as per http://nginx.org/r/proxy_ssl_server_name, but is required by Cloudflare.


It's generally not the best idea to have home-made proxies on top of Cloudflare — it's supposed to be the other way around.

However, what you're omitting is the actual error message that results from making the request like curl -v localhost:3227/i/user/606456_001_min.jpg — of course it is TLS-related:

2018/07/07 23:18:39 [error] 33345#33345: *3 SSL_do_handshake() failed (SSL: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure) while SSL handshaking to upstream, client: 127.0.0.1, server: loc.goout.net, request: "GET /i/user/606456_001_min.jpg HTTP/1.1", upstream: "https://[2400:cb00:2048:1::6818:7303]:443/i/user/606456_001_min.jpg", host: "localhost:3227"

This is because nginx is not really intended to be used to steal someone else's sites via proxy_pass, so, some features that Cloudflare does require, are turned off by default in nginx for the general use; specifically, it's the SNI, Server Name Indication extension to TLS, that's making the difference here.

As per http://nginx.org/r/proxy_ssl_server_name, putting an extra proxy_ssl_server_name on; to your exact configuration does fix the issue (I've tested this myself — it works) — note that this requires nginx 1.7.0 or newer.

+   proxy_ssl_server_name   on;     # SNI is off by default

Additionally, note that you'll also have to ensure that the domain name resolution gets updated during the run-time, as, by default, it only gets resolved when the configuration is loaded or reloaded; you could use the trick of using variables within your http://nginx.org/r/proxy_pass to make it update the resolution of the host as appropriate, but then this also requires you to use the http://nginx.org/r/resolver directive to specify the server to use for DNS resolutions (during the runtime, after loading the configuration), so, your MVP would then be:

location /i/ {
    resolver                1dot1dot1dot1.cloudflare-dns.com.;
    proxy_ssl_server_name   on;     # SNI is off by default
    proxy_pass              https://dev.goout.net:443$request_uri;
}
like image 113
cnst Avatar answered Nov 28 '22 13:11

cnst