Search code examples
httpnginxsslhttpspass-through

Need help configuring an Nginx SSL-Passthrough Reverse Proxy that allows HTTP and HTTPS connections


I am attempting to create a reverse proxy to accept all HTTPS and HTTP connections to various different web servers being hosted, I've managed to get the SSL passthrough to work correctly, and initially we believed we could just redirect all HTTP traffic to HTTPS, however due to some equipment limitations we require HTTP traffic to actually use port 80.

I have tried a few times to get this to work, but everything I've done pretty much results in the same error. Viewing the access.log and error.log I noticed all HTTP traffic did not receive any address information from the upstream, and thus would pass no data to proxy_pass. I don't believe it would be helpful to list all the ways I've attempted to do this, but if anyone feels it would indeed be helpful I can elaborate on all of the methods.

I've included the modular .conf into the nginx.conf already to simplify reading it but the SSL passthrough portion (stream block) is in a different .conf if this matters for whatever reason.

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

stream {

        map $ssl_preread_server_name $name {
                site1.com site1_backend;
                site2.com site2_backend;
                site3.com site3_backend;
        }


        upstream site1_backend {
                server 1.1.1.1:443;
                server 1.1.1.2:443;
        }

        upstream site2_backend {
                server 2.1.1.1:443;
        }

       upstream site3_backend {
                server 3.1.1.1:443
                server 3.1.1.2:443
       }

        server {
                listen 443;
                proxy_pass $name;
                ssl_preread on;
        }
}

events {
        worker_connections  1024;
}

http {

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

        log_format  main   '$remote_addr - $remote_user [$time_local] "$request" '
                           '$status $body_bytes_sent "$http_referer" '
                           '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;
        sendfile        on;
        keepalive_timeout  90;

        server {
                listen 80;
                server_name _;
                return 301 https://$host$request_uri;
        }
}

Solution

  • So I figured out how to do this, it was a little bit less elegant than what I wanted and could possibly be refined but, as it is. It works as an SSL passthrough for HTTPS connections and SSL is being terminated for HTTP connections. Allowing for equipment that communicates using HTTP to redirect properly.

    I've included a separation line for both includes as the configuration is modular, but I think its easier to understand as a complete configuration.

    The load balancers are acting as redundancy, and you can scale it to whatever number of web servers your proxy can handle.

    I believe my error was attempting to deal with the HTTP connections in the same block as stream{} instead of http{}. Theoretically since map is a directive of http and not stream, but apparently works regardless, it should be possible to clean up the configuration a bit by creating a single proxy_pass with the variable for whatever server was requested.

    user  nginx;
    worker_processes  auto;
    error_log  /var/log/nginx/error.log notice;
    pid        /var/run/nginx.pid;
    
    events {
            worker_connections  1024;
    }
    
    #################################################################################
    #   include /etc/nginx/conf.d/ssl-passthrough.conf
    #################################################################################
    stream {
    
        map $ssl_preread_server_name $name {
            site1.com https_site1;
            site2.com https_site2;
            site3.com https_site3;
        }
    
        upstream https_site1 {
            server 1.1.1.1:443;
            server 1.1.1.2:443 backup;
        }
    
        upstream https_site2 {
            server 2.1.1.1:443;
        }
        
        upstream http_site3 {
            server 3.1.1.1:443;
            server 3.1.1.2:443 backup;
        }
    
        log_format basic '$remote_addr [$time_local] '
            '$protocol $status $bytes_sent $bytes_received '
            '$session_time "$upstream_addr" '
            '"$upstream_bytes_sent" "$upstream_bytes_received"
            "$upstream_connect_time"';
    
        access_log /var/log/nginx/stream_access.log basic;
        error_log  /var/log/nginx/stream_error.log;
    
        server {
            listen 443;
            ssl_preread on;
            proxy_pass $name;
        }
    }
    #################################################################################
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
    
        log_format  main   '$remote_addr - $remote_user [$time_local] "$request" '
                           '$status $body_bytes_sent "$http_referer" '
                           '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  /var/log/nginx/access.log  main;
        sendfile        on;
        keepalive_timeout  65;
        
    #################################################################################
    #   include /etc/nginx/conf.d/proxy_http.conf
    #################################################################################
        upstream http_site1 {
            server 1.1.1.1;
            server 1.1.1.2 backup;
        }
    
        upstream http_site2 {
            server 2.1.1.1;
        }
        
        upstream http_site3 {
            server 3.1.1.1;
            server 3.1.1.2 backup;
        }
        
        server {
            listen 80;
            server_name site1.com;
            location / {
                proxy_pass http://http_site1;
            }
        }
    
        server {
            listen 80;
            server_name site2.com;
            location / {
                proxy_pass http://http_site2;
            }
        }
        
            server {
            listen 80;
            server_name site3.com;
            location / {
                proxy_pass http://http_site3;
            }
        }
    #################################################################################
    }
    

    I hope this helps someone as I could not find anyone asking about both ssl-passthrough and ssl-termination in this manner. If anyone has any ideas for optimization or just things that may be wrong any feedback is welcome!