Friday, August 18, 2017

Implement Nginx Reverse Proxy with SSL for Live Streaming

After struggling implement NGINX reverse proxy setup, I have decided to share my running config currently and hope this could be of any help to someone.

Here is the scenario of the implementation


First, we need to configure NGINX to enable http_v2_module
sudo ./configure  --with-http_ssl_module --with-http_v2_module  --add-module=../nginx-rtmp-module
make
sudo make install

After configure and compile NGINX, then in server 10.10.16.32 as reverse proxy, the configuration as following as below and don't forget to copy your SSL key and crt to your correct folder, in my case is in /home/livetv/live.

user livetv;
worker_processes  8;

error_log    /var/log/nginx/error.log debug;
worker_rlimit_nofile 65536;  #worker_rlimit_nofile = worker_connections * worker_processes

events {
    worker_connections  8192;
    multi_accept on;
    use epoll;
}

rtmp {
    server {
        listen 1935;
        chunk_size 8192;
        ping 30s;
        notify_method get;
        allow play all;

        application live {
            live on;

            exec_pull ffmpeg -re -i http://10.10.17.50/$app/$name
                 -vcodec copy -f flv -ar 44100 -ab 64 -ac 1 -acodec mp3 rtmp://localhost:1935/show/${name}
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 1024k -b:a 128k -vf "scale=854:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_480
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 776k -b:a 96k -vf "scale=640:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_360
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 128k -b:a 96k -vf "scale=256:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_144;
        }

        application show {
            live on;
            hls on;
            hls_path /home/livetv/live/hls/;
            hls_nested on;
            record off;

            ### Instruct clients to adjust resolution according to bandwidth
            hls_variant _720 BANDWIDTH=2048000 RESOLUTION=1280x720; # High bitrate, HD 720p resolution
            hls_variant _480 BANDWIDTH=1024000 RESOLUTION=852x480;     # High bitrate, higher-than-SD resolution
            hls_variant _360 BANDWIDTH=512000 RESOLUTION=640x360;     # Medium bitrate, SD resolution
            hls_variant _240 BANDWIDTH=307200 RESOLUTION=426x240;     # Low bitrate, sub-SD resolution
            hls_variant _144 BANDWIDTH=131000 RESOLUTION=256x144;     # Low bitrate, 140p resolution
        }
       
        exec_pull /home/livetv/live/chmodTV.sh;
    }
}

http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile on;
    keepalive_timeout   65;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    server {
        listen      8080;

        location /hls {
            # Disable cache
            add_header 'Cache-Control' 'no-cache';

            # CORS setup
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            add_header 'Access-Control-Allow-Headers' 'Range';

            # allow CORS preflight requests
            if ($request_method = 'OPTIONS') {
                    add_header 'Access-Control-Allow-Origin' '*';
                    add_header 'Access-Control-Allow-Headers' 'Range';
                    add_header 'Access-Control-Max-Age' 1728000;
                    add_header 'Content-Type' 'text/plain charset=UTF-8';
                    add_header 'Content-Length' 0;
                    return 204;
            }

            #serve HLS fragments
            types {
                    application/dash+xml mpd;
                    application/vnd.apple.mpegurl m3u8;
                    video/mp2t ts;
            }

            root /home/livetv/live/;
            add_header Cache-Control no-cache;
        }

        #include /usr/local/nginx/conf/sites-enabled/*.conf;
    }

    server {
        listen 443 ssl http2;
        server_name cdn-livetv.metube.id;
        root /home/livetv/live/;

        ssl on;
        ssl_certificate /home/livetv/live/ssl/STAR_metube_id.crt;
        ssl_certificate_key /home/livetv/live/ssl/metube.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;

        ssl_buffer_size 16k;

        location / {
            index index.html;
        }

        location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|txt|xml|js)$ {
            expires 21d;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
            access_log off;
            log_not_found off;
            fastcgi_hide_header Set-Cookie;
            tcp_nodelay off;
            sendfile off;
            break;
        }

        location /hls {
            proxy_set_header x-real-IP $remote_addr;
            proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
            proxy_set_header x-forwarded-proto https;
            proxy_set_header host $host;
            proxy_pass http://172.31.16.26;
           
            #this is will handle problem in mobile when rise protocol exception
            proxy_buffering on;
            proxy_buffer_size 8k;
            proxy_buffers 2048 8k;
        }
    }
}




In server 172.31.16.26 as backend, the configuration as following as below and don't forget to copy your SSL key and crt to your correct folder, in my case is in /home/livetv/live.


user livetv livetv;
worker_processes  20;

error_log  logs/error.log debug;
worker_rlimit_nofile 409600; #worker_rlimit_nofile = worker_connections * worker_processes

events {
    worker_connections  20480;
    multi_accept on;
    use epoll;
}

rtmp {
    server {
        listen 1935;
        chunk_size 8192;
        ping 30s;
        notify_method get;
        allow play all;

        application live {
            live on;

            exec_pull ffmpeg -re -i http://172.31.16.27/$app/$name
                 -vcodec copy -f flv -ar 44100 -ab 64 -ac 1 -acodec mp3 rtmp://localhost:1935/show/${name}
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 1024k -b:a 128k -vf "scale=854:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_480
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 776k -b:a 96k -vf "scale=640:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_360
                 -vcodec libx264 -threads 0 -vprofile baseline -acodec aac -strict -2 -b:v 128k -b:a 96k -vf "scale=256:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/show/${name}_144;
        }

        application show {
            live on;
            hls on;
            hls_path /home/livetv/live/hls/;
            hls_nested on;
            record off;

            ### Instruct clients to adjust resolution according to bandwidth
            hls_variant _720 BANDWIDTH=2048000 RESOLUTION=1280x720; # High bitrate, HD 720p resolution
            hls_variant _480 BANDWIDTH=1024000 RESOLUTION=852x480;     # High bitrate, higher-than-SD resolution
            hls_variant _360 BANDWIDTH=512000 RESOLUTION=640x360;     # Medium bitrate, SD resolution
            hls_variant _240 BANDWIDTH=307200 RESOLUTION=426x240;     # Low bitrate, sub-SD resolution
            hls_variant _144 BANDWIDTH=131000 RESOLUTION=256x144;     # Low bitrate, 140p resolution

        }
    }
}


http {

    include mime.types;
    default_type  application/octet-stream;
    sendfile on;
    keepalive_timeout   65;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    server {
        listen      80;

        location /hls {
            # Disable cache
            add_header 'Cache-Control' 'no-cache';

            # CORS setup
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            add_header 'Access-Control-Allow-Headers' 'Range';

            # allow CORS preflight requests
            if ($request_method = 'OPTIONS') {
                    add_header 'Access-Control-Allow-Origin' '*';
                    add_header 'Access-Control-Allow-Headers' 'Range';
                    add_header 'Access-Control-Max-Age' 1728000;
                    add_header 'Content-Type' 'text/plain charset=UTF-8';
                    add_header 'Content-Length' 0;
                    return 204;
            }

            #serve HLS fragments
            types {
                    application/dash+xml mpd;
                    application/vnd.apple.mpegurl m3u8;
                    video/mp2t ts;
            }

            root /home/livetv/live/;
            add_header Cache-Control no-cache;
        }

        include /usr/local/nginx/conf/sites-enabled/*.conf;

    }

    server {
        listen 443 ssl http2;
        server_name cdn-livetv.metube.id;
        #rewrite ^(.*)  cdn-livetv.metube.id$1 permanent;
        root /home/livetv/live/;

        ssl on;
        ssl_certificate /home/livetv/live/ssl/STAR_metube_id.crt;
        ssl_certificate_key /home/livetv/live/ssl/metube.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;

        ssl_buffer_size 16k;
        location / {
            index index.html;
        }

        location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|txt|xml|js)$ {
            expires 21d;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
            access_log off;
            log_not_found off;
            fastcgi_hide_header Set-Cookie;
            tcp_nodelay off;
            sendfile off;
            break;
        }

        location /hls {
            proxy_set_header x-real-IP $remote_addr;
            proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
            proxy_set_header x-forwarded-proto https;
            proxy_set_header host $host;
            proxy_pass http://127.0.0.1;
           
            #this is will handle problem in mobile when rise protocol exception
            proxy_buffering on;
            proxy_buffer_size 8k;
            proxy_buffers 2048 8k;
        }
    }
}

                  
                               
               
               

Other Topics:
               
               
               
               
               
               
               
               

Friday, August 11, 2017

IP Forwarding and Enable UDP Multicast

During few days, I am confused on steaming of live TV that I created on our product. I couldn't forward the streaming of live tv on UDP multicast from eth0 to eth1. Our server has 2 network interface card and let say eth0 with IP 10.10.10.5 and eth1 with IP 172.31.16.26. In my opinion, it should be easy to be done but unfortunately, it is not simple as i thought. In order to make this happen then i created some simple experiment to forward traffic from eth0 to eth1. Here is my step to implement the things on how to forward IP and Port.

Step 1. Make sure the ip_forward is enable. Check on /etc/sysctl.conf and change the value to net.ipv4.ip_forward=1  If the flag is commented then just uncomment the line by removing # or you can do by typing on terminal sudo sysctl -w net.ipv4.ip_forward=1

Step 2. Change net.ipv4.conf.default.rp_filter=1. Check on /proc/sys/net/ipv4/conf/eth0/rp_filter and change the value to net.ipv4.conf.default.rp_filter=2


Step 3. Type this command on terminal 

sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
This command can be explained in the following way:
iptables: the command line utility for configuring the kernel
-t nat: select table "nat" for configuration of NAT rules.
-A POSTROUTING:  Append a rule to the POSTROUTING chain (-A stands for "append").
-o eth0: this rule is valid for packets that leave on the second network interface (-o stands for "output")
-j MASQUERADE: the action that should take place is to 'masquerade' packets, i.e. replacing the sender's address by the router's address.
Then forward packet from eth0 to eth1 
sudo iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT

Step 4. Add route to enable UDP multicast address from 224.0.0.0 to 239.255.255.255
route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

Step 5. Make sure your firewall is inactive or if firewall enable then you must change DEFAULT_INPUT_POLICY="ACCEPT" on /etc/default/ufw 

Step 6. To check if the things is working properly then send data over UDP  by typing command on terminal 
echo "This is my data" > /dev/udp/239.255.0.1/3000
This will send text "This is my data" to UDP with IP 239.255.0.1 and Port 3000 and 
from the other terminal, just dump by typing command 
tcpdump -i eth1 udp port 3000 -vv -X


If you are successfully, then you will receive the text



Other Topics: