Domain based proxy selection on NAT-routers

by

Introduction

It seems to become normal for service providers in the world wide web to reduce their functionality when the user is not accessing the service through the "right" IP range. This usually reflects the license agreements the service provider has with the actual content producers or copyright holders in this specific region. There are many well known users of GeoIP based restrictions, e.g. YouTube, Netflix or Hulu.

YouTube
The (currently) monopolistic collecting society in Germany and Google don't seem to be able to find acceptable conditions for both parties to correctly license the music in YouTube videos. This is especially problematic because GEMA can currently just assume that it has to receive royalties even without proving that the music is covered by the GEMA catalogue. Google started to block music videos in Germany as countermeasure against possible copyright claims (and maybe to create awareness of the problem).
Netflix
Netflix seems to be the biggest Video-on-Demand service at the moment. There are even rumors about Netflix planning to expand to more european states in the near future. But the UK version of Netflix already showed that it doesn't have world-wide streaming rights for its complete portfolio.
Hulu
Hulu is a free (ad-supported) streaming service with many content partners. Interestingly, it tries to provide access to person in foreign countries. These are for example U.S. military bases outside the U.S. or Japan. It easily shows that these IP restrictions not only work on large entities like countries and they are not necessarily limitations of the infrastructure.

There are many other reasons for a IP based block/restriction by a service provider. I will concentrate here on a very specific case: increasing security by reducing the attack vector. (I will not discuss whether this is a good strategy by the service provider)

Many projects were started to provide easy one-click solutions to avoid such IP restrictions on every kind of device. Unfortunately, these products are often extreme limited to a specific service (the ones mentioned above). Also the servers used as proxy are not the ones I would trust with sensible data. This makes them useless for my actual problem.

Problem

I have to use a service which only provides developer/administration access for specific IPs. But my ISP changes my IP after each authorization process and has a 24h automatic disconnect. This basically makes it impossible for me to access the advanced features directly from my systems without getting the whole IP range of this ISP whitelisted. But this would be inacceptable for the service provider.

The service has following important properties:

  • Full developer access is only granted when accessing it from specific IP (+username+password)
  • Service runs on multiple servers and IPs of these server can change anytime
  • All servers with restricted content are behind the same domain name
  • HTTP(s) is not only the restricted service which has to be accessed
  • Service creates nearly no traffic (formular based) and high latency is not a problem
  • External resources are included from other domains (and create a rather large amount of traffic)

The final setup should make it easy to use (nearly) any device without extra setup

  • Traffic to the service should sent through the proxy
  • Traffic to unrelated servers should not be sent through the proxy
  • Proxy must be transparent for the client

Single computer solution

The easiest setup for a single desktop system is probably based on browser extensions like FoxyProxy and tools like tsocks. A simple proxy can be created using SSH when the remote server has a running sshd with enabled AllowTcpForwarding.

FoxyProxy has to be changed to "Use proxies based on their pre-defined patterns and priorities" and a new Socks 5 proxy has to be added. This proxy connection should be configured with a wildcard pattern like "*foobar.com/*"

This setup has the obvious problem that it only works on devices which have support for these programs. Also adding new devices requires to modify them to get them working with the target service.

NAT-router solution

It is quite easy to move the complete proxy setup to a Linux based NAT-router. Only common tools like ipset, redsocks and dnsmasq are required. Before the transparent proxy for the clients can be created, the normal iptables MASQUERADE setup should be up and running, ssh proxy already started (localhost:1080) and the clients have to use the DNS server of the NAT-router.

The first component to use is ipset. This project provides some kernel modules (since Linux 2.6.39) and a userspace tool. These can be used together to create sets of IPs which can be used (amongst other) by iptables rules. This set (here called fbar) has to be created after each reboot (or after the module was unloaded):

$ ipset create fbar iphash

Of course, such a set is relative useless when it is empty and no one adds new IP-addresses to it. Luckily, dnsmasq 2.66 got support to add resolved IP addresses automatically to a ipset. Only a single line has to be added to /etc/dnsmasq.conf to add all known IPs of foobar.com to the ipset fbar.

ipset=/foobar.com/fbar

The nat table of iptables can be used to compare the destination IP of a new connection against the IPs in the fbar ipset. These connection can then be redirected to another port on the local machine which will have a proxy redirector listening. This redirector is necessary because iptables itself cannot directly redirect a connection to a socks proxy.

$ iptables -t nat -I PREROUTING 1 -m set --match-set fbar dst -p tcp -j REDIRECT --to-ports 1081
# only use following line when the NAT gateway should also use the proxy
$ iptables -t nat -A OUTPUT -m set --match-set fbar dst -p tcp -j REDIRECT --to-ports 1081

The redsocks redirector has to be configured to listen to the port 1081 (as configured in the iptables rules) and the local_ip "0.0.0.0". The preconfigured 127.0.0.1 doesn't work because client connection are not allowed to access it when redsocks only listens on the lo device. All that can be done by creating a new redsocks section in /etc/redsocks.conf and restarting redsocks. On Debian it is also necessary to change START in /etc/default/redsocks to yes.

redsocks {
    local_ip = 0.0.0.0;
    local_port = 1081;

    // the ssh proxy
    ip = 127.0.0.1;
    port = 1080;
    type = socks5;
}