Nginx proxy based on SNI without decryption
Solution 1:
It looks like this is now supported using the ngx_stream_ssl_preread_module module
Solution 2:
NO, you can't do with Nginx. By default, Nginx is always decrypting content, so Nginx can apply request routing. Some solution that can be tried:
There are 3rd party module called nginx_tcp_proxy_module. I haven't tried it yet. Because that module do proxy on network layer, so it will passing request without decryption.
The preferred solution is use HAProxy. This tutorial suggest that you can do TCP proxy with SNI capabilities.
Sidenote
By default, Nginx always act as SSL offloading/decryption process on proxy. Here some the advantages doing SSL offloading (taken from here)
- Improved performance
- Better utilization of the backend servers
- Intelligent routing
- Certificate management
- Security patches
Solution 3:
There are some things you have to note about doing this:
- You're going to have to use a
stream
block rather than anhttp
block, which means: - You can't set
error_page
directives because HTTP error codes are at the application layer and you're not decrypting that layer - You can't
proxy_pass
to anhttp://
server because you're not doing SSL termination any more - You can't
proxy_set_header
because you can't inject plaintext into an encrypted stream
If you accept those limitations and e.g. you're going to do the work in another server that is either the final destination or an intermediate proxy that is doing SSL termination, then here's what you need to do:
First, note that most nginx server configurations automatically wrap everything in an
http
block. So if e.g. you're used to just dropping a file into/etc/nginx/conf.d
on Red Hat-type installs or/etc/nginx/sites-available
with a symlink in/etc/nginx/sites-enabled
on Debian-type installs, that's not going to work here. You're going to have to make at least some minor changes to the master configuration file/etc/nginx.conf
, if only to create a stream block that includes other files like this:stream { include /etc/nginx/stream.d/*.conf; }
Next, you need to create a stream server. You're limited to the directives that are in the
ngx_stream_*
modules, which excludes all the HTTP-specific stuff. As Robert Wagner's answer pointed out, you'll need to use thengx_stream_ssl_preread
module which doesn't exist in nginx versions older than 1.11.5. Create a.conf
file in/etc/nginx/stream.d
or whatever other directory you chose, and define your severs. The following is taken verbatim from what's in the module documentation, and gives an example of SNI-based filtering (there's also ALPN and SSL version filtering available):map $ssl_preread_server_name $name { backend.example.com backend; default backend2; } upstream backend { server 192.168.0.1:12345; server 192.168.0.2:12345; } upstream backend2 { server 192.168.0.3:12345; server 192.168.0.4:12345; } server { listen 12346; proxy_pass $name; ssl_preread on; }
Now, you have a server that doesn't decrypt traffic but does determine where to send it based on SNI. If a client connects and requests
backend.example.com
then it will be routed tobackend
and otherwise it will be routed tobackend2
.
There are other things you may want to consider as well, like setting tcp_nodelay
or defining a stream log_format
and enabling access_log
for the stream
block, but of course the format and variables will be different from an HTTP access log.