Like most people (OK, maybe not most…), I have a range of locally hosted services that I like to access remotely. Because of the joy that is IPv4 depletion (long live IPv6) I’m forced to use different ports for internet access, which is hard to remember and annoying to set up. And because the internet is a bad place, I prefer to use encryption where possible to keep everything safe on it’s journey across the internet. Sure, something like SSH tunnels would work, but that would still require remembering the different port numbers, and isn’t compatible with all devices (such as mobile clients).
Enter SNI Proxy
After some research, I decided to put an end to both these problems, with an install of SNI Proxy. SNI Proxy is a generic HTTP and TLS proxy that identifies the internal host from TLS’ server name indication (SNI). This allows it to seamlessly proxy multiple services on the same port, and heavily increase the WAF for services like https://coffeemachine.jacobmansfield.co.uk and https://photos.jacobmansfield.co.uk .
It also makes a nice place to centrally manage SSL certificates and Let’s Encrypt renewals I was under the impression that SNI Proxy could also terminate an HTTPS/TLS connection, though this now appears to be incorrect. (Though I do this on each host, as SNI Proxy doesn’t handle my IPv6 traffic). So, let’s get installing.
I’m a fan of keeping services isolated, so I chose to install SNI Proxy on it’s own VM. Debian is my go-to default for most things nowdays, but these instructions should work on most modern Linux OSes. Setting up the VM and OS itself it a bit out-of-scope for this, so I’ll leave it as an exercise for the reader.
Precompiled packages aren’t available for Debian, so we’ll have to compile it from source. This is easily done. First, install the build requirements:
apt install git build-essential autotools-dev cdbs debhelper dh-autoreconf gettext libev-dev libpcre3-dev libudns-dev pkg-config devscripts
Unfortunately, the dh packages pull a lot of perl dependencies, so this might take a while. On my fresh install, there was about 200MB to download. Once this has finished, download the source using Git:
git clone https://github.com/dlundquist/sniproxy.git
From here, the actual compilation is relatively simple. Just switch to the directory with the source code, and run the autogen.sh script. This handles most of the setup. Then run dpkg-buildpackage to go through the build process.
cd sniproxy ./autogen.sh dpkg-buildpackage
Once that’s done, we’re ready to install it. Because we used dpkg-buildpackage, the compilation process produced a .deb file ready to be installed with dpkg. So, that makes the install as simple as:
dpkg --install ../sniproxy_*.deb
Now that’s done, SNI Proxy can be configured to proxy the services you like.
The SNI Proxy package provides an example configuration file in /etc/sniproxy.conf, which provides a great base to use. There’s preconfigured sections for HTTP traffic on port 80, and TLS traffic on 443. These are set to use the http_hosts and https_hosts sections in the file. To create a proxy destination, add a line to the relevant section with a regular expression and a target, separated by a space. The targets can be an IP address or DNS name, with an optional port. Or use an asterisk (*) to resolve the host name the client requested, and use that. You can also use Unix sockets as a target with unix:/path/to/server.sock or similar. Once you’re done, restart the service using systemctl restart sniproxy.service
N.B. The hostname is always interpreted as a regular expression. As such, keep an eye out for things like hostname.example.com , because that will also match hostname.example.com.badguy.com. It’s wise to add regex’s start and end markers to each entry, i.e. ^hostname\.example\.com$. Thanks to TeenageProgrammer for pointing this out.
Overall the setup for SNI Proxy is fairly simple, and it provides a lot of flexibility, particularly when you’re restricted to using a single public IP address. And remember, because it can proxy any TLS traffic, you’re not just restricted to HTTPS. You could also route MQTT or Zabbix through it!