Replacing sniproxy with Nginx for unlocking OTT content

Most current solutions for OTT content unlock use sniproxy. Unfortunately, sniproxy has become another open-source project abandoned by the original author. The maintainer is no longer active, and the last commit was merged over 1 year ago despite multiple pull requests. The future of sniproxy does not look promising.

One of the most annoying issues of sniproxy is that sometimes the software completely hangs for no obvious reason, and a restart is required for recovery. I haven't tested it yet, but this fork probably has fixed the issue - that is, if you insist on using sniproxy. Switching to Nginx might be a better solution, as it is actively maintained, and is well-known for its high performance.

Nginx provides most of the features of sniproxy, except the fine control over resolver behavior. The resolver of Nginx merely supports IPv4-only mode and a IPv6-on mode which randomly switches between IPv4 and IPv6 addresses. By comparison, sniproxy has ipv4_only, ipv4_first, ipv6_only, and ipv6_first modes. These modes come handy when your proxy server can only unlock OTT content over IPv6. Fortunately, by modifying several lines of Nginx source code, it is possible to have a ipv6_only mode for Nginx. However, ipv6_first or ipv4_first mode requires a lot more effort.

Nginx Installation

You will need to compile your own version of Nginx with stream_ssl and stream_ssl_preread modules for proxying HTTPS traffic by SNI. Here is a step-by-step tutorial.

sudo apt install -y nginx build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev libgd-dev libxml2 libxml2-dev uuid-dev
wget https://nginx.org/download/nginx-1.21.3.tar.gz
tar -xf nginx-1.21.3.tar.gz
cd nginx-1.21.3/
./configure --prefix=/var/www/html --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --with-pcre  --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-http_ssl_module --with-http_image_filter_module=dynamic --modules-path=/etc/nginx/modules --with-http_v2_module --with-stream=dynamic --with-http_addition_module --with-http_mp4_module --with-stream_ssl_preread_module --with-stream_ssl_module
make
sudo make install
sudo apt-mark hold nginx

Example Configuration of /etc/nginx/nginx.conf

SECURITY WARNING: I use wildcard for hostname, which means Nginx will proxy ANYTHING from ANYWHERE.

Please use a firewall, otherwise it becomes a public proxy that ANYONE can access!

user www-data;
worker_processes auto;
pid /run/nginx.pid;
load_module /etc/nginx/modules/ngx_stream_module.so;

error_log  /var/log/nginx/error.log;

events {
    worker_connections 2048;
}

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    gzip on;
    include /etc/nginx/conf.d/*.conf;

    server {
        listen 80;
        # Better practice: only listen to localhost, or use a firewall.
        resolver 1.1.1.1 ipv6=on;
        resolver_timeout 1s;
        location / {
            proxy_set_header Host $http_host;
            proxy_pass http://$http_host;
        }
    }
}

stream {
    map $ssl_preread_server_name $ssl_target {
        ~^(.*|)netflix\.com    $ssl_preread_server_name:443;
        ~^(.*|)fast\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflix\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflix\.net    $ssl_preread_server_name:443;
        ~^(.*|)nflxext\.com    $ssl_preread_server_name:443;
        ~^(.*|)nflximg\.com    $ssl_preread_server_name:443;
        ~^(.*|)nflximg\.net    $ssl_preread_server_name:443;
        ~^(.*|)nflxso\.net    $ssl_preread_server_name:443;
        ~^(.*|)nflxvideo\.net    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest0\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest1\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest2\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest3\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest4\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest5\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest6\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest7\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest8\.com    $ssl_preread_server_name:443;
        ~^(.*|)netflixdnstest9\.com    $ssl_preread_server_name:443;
    }

    server {
        listen 443;
        resolver 1.1.1.1 ipv6=on;
        resolver_timeout 1s;
        proxy_pass $ssl_target;
        ssl_preread on;
    }
}

Nginx Tuning

Add the option LimitNOFILE=65536 under [Service] section of /lib/systemd/system/nginx.service.

Save the file and reload: systemctl daemon-reload && systemctl restart nginx.

Increase maximum open file descriptors: ulimit -n 65536

You can also tune kernel parameters in /etc/sysctl.conf, especially the ones related to TCP connection. For example:

fs.file-max = 65536
net.core.wmem_default = 37500000
net.core.wmem_max = 75000000
net.core.rmem_default = 37500000
net.core.rmem_max = 75000000
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_window_scaling = 2
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 4096

Note: wmem can be calculated by bandwidth-delay product.

IPv6-only Mode for Nginx

See https://github.com/apernet/nginx. The only modification is to zeroize the counter for DNS A record.


Was this article helpful?

mood_bad Dislike 0
mood Like 1
visibility Views: 387