nginx as reverse proxy with upstream SSL

I'm building a proxy for an internal API to allow clients to connect without having to have the self-signed certificates installed.

Clients (built, owned and used only internally) will connect over SSL to the nginx box, where I'm using XSendfile to validate credentials at the application level (a rails app). If the credentials are valid, the connection is passed back up to nginx where it uses proxy_pass to send the connection onto the upstream server.

Now this works great for standard http connections, but I'm trying to figure out how to add our certificates into the mix.

This question is almost identical to this one, but with awkward certificate requirements.

Is this even possible with nginx? Is there a better solution?

I'd also settle for http from client -> nginx, and self-signed certificate from nginx to the API.


Solution 1:

For anybody stumbling across this question that wants to use nginx you can set this up like any normal proxy, and to accept a self-signed certificate from the backend you need to provide the exported pem certificate (and perhaps a key) and set ssl verification off. For example:

...

server {
    listen       10.1.2.3:80;
    server_name  10.1.2.3 myproxy.mycompany.com;

    location / {
         proxy_pass                    https://backend.server.ip/;
         proxy_ssl_trusted_certificate /etc/nginx/sslcerts/backend.server.pem;
         proxy_ssl_verify              off;

         ... other proxy settings
    }

If your secure back end is using Server Name Identification SNI with multiple hosts being served per IP/Port pair you may also need to include proxy_ssl_server_name on; in the configuration. This works on nginx 1.7.0 and later.

Solution 2:

I think you probably want something like this (obviously simplified for this example):

worker_processes  1;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    upstream backend {
        server mybackendserver:443;
    }

    server {
        server_name localhost;
        listen 443 ssl;
        ssl_certificate /etc/nginx/server.crt;
        ssl_certificate_key /etc/nginx/server.key;
        ssl_verify_client off;
        location / {
            proxy_pass  https://backend;
            proxy_set_header Host $http_host;
            proxy_set_header X_FORWARDED_PROTO https;
        }
    }
}

The only thing you may have to change would be to make the "Host" explicit - if, for example, your proxied host name wasn't the same as the host name used on the nginx proxy server.

Solution 3:

For anyone that comes across this in the future, I ended up not using nginx for this.

Instead, I ended up using stunnel in "client mode". Very easy to set up, and does exactly what I need.