Friday 7 February 2014

DIY clone of Netflix Tunlr/Unblock-Us/UnoTelly on cheap US based VPS



TL;DR head to netflix-proxy on GitHub or subscribe to Unzoner VPN service to un-block:





(for posterity) If you are in the UK and would like to access Netflix US catalogue, which is about three times bigger, read on.

There are commercial services out there, such as Unblock-Us and Smart DNS Proxy to name a few, so you could pay for the privilege to use their services or you can knock together a similar solution quickly yourself for a cost of a cheap Virtual Private Server (VPS). How cheap, depends on where you decide to host your VPS.
In essence, the following solution works by selectively proxying only certain requests (*.netflix.com) via a server located in the US, thus bypassing Netflix application level geo-restrictions. This solution does not proxy the actual video delivery, which uses different domain(s).

Please note, this basic solution doesn't work on devices which don't support the use of SNI during SSL handshake (e.g. Netflix on Chromecast). To support such devices, you will need to extend this solution to selectively proxy individual Netflix hosts using something akin to HAProxy, go with a commercial provider or switch to black.box unzoner.

Also, if you are looking how to watch BBC iPlayer abroad, please see the flowing guide: http://blog.belodedenko.me/2014/12/watch-bbc-iplayer-in-australia-for-free.html

Update 04/2015: You can also watch HBO NOW on your Apple TV outside the US using this solution, here's how: http://blog.belodedenko.me/2015/04/watch-hbo-now-in-uk-for-free-apple.html

Update 05/2015: You can also watch Hulu Plus on your Apple TV outside the US using this solution, here's how: http://anton.belodedenko.me/watch-hulu-plus-outside-of-the-us/

However, if you still want to build the system manually, perhaps to understand how it all works, please read on...

Prerequisites


You'll need the following:
  • A working Internet connection at home
  • A Netflix subscription
  • A US based VPS with root access and clean CentOS x86 6.x image
  • Basic knowledge in navigating a Linux console
For my hosting I choose Digital Ocean with the VPS hosted in a New York data centre for $5 USD per month. If you sign up with the above link, you will receive $10 USD credit, which is enough for two months hosting. Note, Netflix has blocked Digital Ocean IP address ranges, so don't expect it to work. Other services may still work though.

Configuration


The configuration depends on the following two components:
  • DNS server
  • HTTP/SSL proxy
For the purpose of this demo, we assume your VPS's public Internet IP is 162.243.x.y and your host is called myhost. We also assume your ISP has assigned 86.144.x.y to your home router.

You can check what your IP is by going to http://www.ifconfig.io/. Note, if you ISP assigns you a dynamic IP, you will need to update your configuration every time this changes. Better ask them for a static IP address. When creating your configuration files, please change the place holder IPs to your actual IPs.

Base VPS Deployment


This step depends on the VPS provider you choose, but broadly consists of the following steps:
  • Create account with VPS provider
  • Deploy a CentOS x64 6.x image to a US based data centre, ideally on the east coast.
Once your VPS server is created, you will get a public IP address and SSH credentials to use to login.

First, establish an SSH session to your server and install additional repositories:
[root@myhost ~]# yum -y install epel-release
[root@myhost ~]# yum -y update

Install certain pre-requisites to make your life a little easier:
[root@myhost ~]# yum install openssl vim git wget curl bind-utils telnet -y

Install development tools to enable you to build packages later:
[root@myhost ~]# yum groupinstall "Development Tools" -y
[root@myhost ~]# yum install rpmbuild autoconf automake curl libev libev-devel pcre pcre-devel perl udns-devel

IP Tables (firewall)


Since our VPS is on a public Internet, we are going to firewall it off from all the unpleasantness out there. Add rules to allow DNS, HTTP and HTTPS inbound from your home's public IP:
[root@myhost ~]# iptables -A INPUT -s 86.144.x.y/32 -p udp -m udp --dport 53 -j ACCEPT
[root@myhost ~]# iptables -A INPUT -s 86.144.x.y/32 -p tcp -m tcp --dport 80 -j ACCEPT
[root@myhost ~]# iptables -A INPUT -s 86.144.x.y/32 -p tcp -m tcp --dport 443 -j ACCEPT
[root@myhost ~]# service iptables save
[root@myhost ~]# service iptables restart

BIND (DNS server)


BIND is going to be our DNS server. It will respond with our VPS IP to DNS requests for netflix.com domain. Note, we are configuring the DSN server to run in a chroot jail and as a non-root user, to provide added security and minimise the potential effects of a security compromise.

Install BIND:
[root@myhost ~]# yum install bind -y

Edit your /etc/named.conf and make it look as follows:
options {
        listen-on port 53 { any; };
        listen-on-v6 port 53 { any; };
        directory       "/var/named";
        dump-file       "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";

        allow-query { trusted; };
        allow-recursion { trusted; };
        allow-transfer  { none; };
        recursion yes;
        additional-from-auth yes;
        additional-from-cache yes;

        auth-nxdomain no;    # conform to RFC1035
        dnssec-enable yes;
        dnssec-validation yes;
        dnssec-lookaside auto;

        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.iscdlv.key";
        managed-keys-directory "/var/named/dynamic";

        forwarders {
                8.8.8.8;
                8.8.4.4;
        };
};

acl "trusted" {
        any;
};

logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};

zone "." IN {
        type hint;
        file "named.ca";
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
include "/etc/named/zones.override";

Edit your /etc/named/db.override and make it look as follows:
$TTL  86400

@   IN  SOA ns1 root (
            YYYYMMDD01  ; serial
            604800      ; refresh 1w
            86400       ; retry 1d
            2419200     ; expiry 4w
            86400       ; minimum TTL 1d
            )

    IN  NS  ns1
ns1 IN  A   162.243.x.y
@   IN  A   162.243.x.y
*   IN  A   162.243.x.y

* Note, change the above IP address to your the public IP of your VPS and update the datestamp. 

Edit your /etc/named/zones.override and make it look as follows:
zone "netflix.com." {
    type master;
    file "/etc/named/db.override";
};

Note, you may need to change the ownership of the new files you created from root to named:
[root@myhost ~]# chown named:named /etc/named/zones.override
[root@myhost ~]# chown named:named /etc/named/db.override

Install BIND (chroot package):
[root@myhost ~]# yum install bind-chroot -y

Finally, start BIND:
[root@myhost ~]# rndc-confgen -a -r /dev/urandom -t /var/named/chroot
[root@myhost ~]# chkconfig named on
[root@myhost ~]# service named start

From one of your home machines, run the following quick test:
$ nslookup www.netflix.com 162.243.x.y
Server:         162.243.x.y
Address:        162.243.x.y#53

Name:   www.netflix.com
Address: 162.243.x.y

You should get the IP address of your VPS in the DNS response.

SNI Proxy (HTTP/SSL proxy)


SNI Proxy is going to be our HTTP/SSL proxy, which supports for TLS SNI extensions.

Build and install SNI Proxy:
[root@myhost ~]# cd /opt
[root@myhost ~]# git clone https://github.com/dlundquist/sniproxy.git
[root@myhost ~]# cd ./sniproxy
[root@myhost ~]# ./autogen.sh && ./configure && make dist
[root@myhost ~]# rpmbuild --define "_sourcedir `pwd`" -ba --nodeps redhat/sniproxy.spec
[root@myhost ~]# yum install $(ls /root/rpmbuild/RPMS/x86_64/sniproxy-[0-9]*.rpm) -y

Note: if the RPM fails to install, check the exact name of the file in the /root/rpmbuild/RPMS/x86_64/directory.

Also, please review the ./configure command output to make sure LIBUDNS or libudns is detected, if it hasn't, you will not be able to get SNI Proxy to work with the below configuration file. On a Red Hat/CentOS server, the library should be available from EPEL repository (yum install udns-devel), however for other distributions, you will need to build and install it manually.

Edit your sniproxy.conf and make it look as follows:
[root@myhost ~]# vim /etc/sniproxy.conf

user daemon
pidfile /var/tmp/sniproxy.pid

error_log {
        filename /var/log/sniproxy_error.log
        priority notice
}

listener 162.243.x.y 80 {
        proto http
        access_log {
                filename /var/log/sniproxy_access.log
        }
}

listener 162.243.x.y 443 {
        proto tls
        access_log {
                filename /var/log/sniproxy_access.log
        }
}

table {
        netflix\.com *
}

Finally, install the start-up script and start SNI Proxy:
[root@myhost ~]# cp ./redhat/sniproxy.init /etc/init.d/sniproxy
[root@myhost ~]# chmod +x /etc/init.d/sniproxy
[root@myhost ~]# chkconfig sniproxy on
[root@myhost ~]# service sniproxy start

Note, if the proxy complains about ports being in use, check if you got another web server already running and if so, disable it or move it to a different port.

From one of your home machines, run the following quick test:
$ telnet 162.243.x.y 80
Trying 162.243.x.y...
Connected to 162.243.x.y.
Escape character is '^]'.
^C
Connection closed by foreign host.

$ openssl s_client -servername www.netflix.com -connect 162.243.x.y:443
CONNECTED(00000003)
depth=2 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/C=US/ST=CALIFORNIA/L=Los Gatos/O=Netflix, Inc./OU=Operations/CN=www.netflix.com
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3
 2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MII...

You should get a socket connection on port 80 and get an SSL handshake with Netflix.

Putting It All Together


The last step is to change the DNS server on the device you are planning to watch US Netflix on. Set it to your VPS public IP (i.e. 162.243.x.y) Fire up Netflix and you should see the US catalogue and if you do - enjoy!

Note, this won't work on devices which don't support the use of SNI during SSL handshake, you'll have no choice but to use a system described in this guide or use black.box unzoner.

References


I've used the following reference material to prepare the solution described in this article. Many thanks to the respective authors.