Skip to content

Instantly share code, notes, and snippets.

@nguyenchilong
Forked from mrbar42/README.md
Created February 3, 2021 10:57
Show Gist options
  • Save nguyenchilong/aee3c1701781554b10dd1c7bb39c6c83 to your computer and use it in GitHub Desktop.
Save nguyenchilong/aee3c1701781554b10dd1c7bb39c6c83 to your computer and use it in GitHub Desktop.

Revisions

  1. @mrbar42 mrbar42 revised this gist Oct 28, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -71,7 +71,7 @@ var crypto = require('crypto');
    function generateSecurePathHash(expires, client_ip, secret) {
    if (!expires || !client_ip || !secret) throw new Error('Must provide all token components');

    var input = expires + client_ip + ' ' + secret;
    var input = expires + ' ' + client_ip + ' ' + secret;
    var binaryHash = crypto.createHash('md5').update(input).digest();
    var base64Value = new Buffer(binaryHash).toString('base64');
    return base64Value.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  2. @mrbar42 mrbar42 revised this gist May 21, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # Secured HLS setup with Nginx as media server

    This example is part of [this article](https://blog.peer5.com/practical-approaches-for-securing-your-video-streams/).

    This is an example for an HLS delivery with basic security.
    Nginx compiled with nginx-rtmp-module & secure-link is used as media server.
    Features:
  3. @mrbar42 mrbar42 revised this gist May 15, 2018. 2 changed files with 17 additions and 0 deletions.
    16 changes: 16 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,22 @@ Features:
    Throughout this example the host is assumed to be `example.com`.
    if you want to use this configurations, be sure to replace all instances of `example.com` with your domain.

    ### Compiling Nginx
    ```sh
    # install deps (Ubuntu)
    sudo apt-get install -y build-essential libpcre3 libpcre3-dev libssl-dev

    wget http://nginx.org/download/nginx-1.10.1.tar.gz
    tar -xf nginx-1.10.1.tar.gz
    cd nginx-1.10.1

    ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module --with-http_secure_link_module

    make -j
    sudo make install
    # nginx is now installed in /usr/local/nginx
    ```

    ### Pushing video to Nginx
    In order to push video to nginx i'm going to use ffmpeg which well supports RTMP as its output.
    I'm going to create an `I frame` roughly every 2 seconds which will allow nginx to achieve the 4s segment target.
    1 change: 1 addition & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -14,6 +14,7 @@
    <div id="player" style="width:640px"></div>
    <script>
    var player = new window.Clappr.Player({
    // this is an example url - for this to work you'll need to generate fresh token
    source: 'https://example.com/video/hls/CIfZTIu8ygWzhXBSoQfPIQ/1526373226/live.m3u8',
    parentId: '#player'
    });
  4. @mrbar42 mrbar42 created this gist May 15, 2018.
    73 changes: 73 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    # Secured HLS setup with Nginx as media server

    This is an example for an HLS delivery with basic security.
    Nginx compiled with nginx-rtmp-module & secure-link is used as media server.
    Features:
    - Domain filtering
    - Referrer filtering
    - Embed buster
    - Session token for playlist, segments and AES keys
    - AES encryption
    - HTTPS only

    Throughout this example the host is assumed to be `example.com`.
    if you want to use this configurations, be sure to replace all instances of `example.com` with your domain.

    ### Pushing video to Nginx
    In order to push video to nginx i'm going to use ffmpeg which well supports RTMP as its output.
    I'm going to create an `I frame` roughly every 2 seconds which will allow nginx to achieve the 4s segment target.
    For simplicity i'll be using a static mp4 file and ingest it in infinite loop.

    ```sh
    ffmpeg -hide_banner \
    -stream_loop -1 \
    -re -i test-video.mp4 \
    -c:a aac -c:v h264 -g 48 \
    -f flv rtmp://localhost:1935/show/live
    ```
    I'm using `live` as the stream name, the output hls will carry that same name - e.g. `live.m3u8`.

    ### Generating Session token
    The session token is based on this format (note the spaces):
    `MD5("EXPIREY_DATE_IN_SECONDS CLIENT_IP_ADDRESS SECRET")`

    here are several examples of generating the token:

    BASH
    ```sh
    get_customer_url() {
    local IP=${1:-127.0.0.1}
    local SECRET=${2:-VERY_COOL_SECRET}
    local EXPIRES="$(date -d "today + 30 minutes" +%s)";
    local token="$(echo -n "${EXPIRES} ${IP} ${SECRET}" | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =)"
    echo "https://example.com/video/hls/${token}/${EXPIRES}/live.m3u8"
    }

    get_customer_url 10.20.1.55 "uigfp(@#tfpIUDGPFiouGDF"
    ```

    Node.JS (Javascript)
    ```js
    var crypto = require('crypto');

    function generateSecurePathHash(expires, client_ip, secret) {
    if (!expires || !client_ip || !secret) throw new Error('Must provide all token components');

    var input = expires + client_ip + ' ' + secret;
    var binaryHash = crypto.createHash('md5').update(input).digest();
    var base64Value = new Buffer(binaryHash).toString('base64');
    return base64Value.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
    }

    function getStreamUrl(ip, secret) {
    const expiresTimestamp = new Date(Date.now() + (1000 * 60 * 30)).getTime();
    const expires = String(Math.round(expiresTimestamp / 1000));

    const token = generateSecurePathHash(expires, ip, secret);

    return `https://example.com/video/hls/${token}/${expires}/live.m3u8`;
    }

    getStreamUrl('127.0.0.1', 'uigfp(@#tfpIUDGPFiouGDF');
    // https://example.com/video/hls/LdS-kcC-JGVHGNTFlX-6Sw/1526373776/live.m3u8
    ```
    22 changes: 22 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>example.com</title>
    <script>
    if (top != self) {
    top.location.replace(self.location.href);
    }
    </script>
    <script src="//cdn.jsdelivr.net/clappr/latest/clappr.min.js"></script>
    </head>
    <body>
    <div id="player" style="width:640px"></div>
    <script>
    var player = new window.Clappr.Player({
    source: 'https://example.com/video/hls/CIfZTIu8ygWzhXBSoQfPIQ/1526373226/live.m3u8',
    parentId: '#player'
    });
    </script>
    </body>
    </html>
    110 changes: 110 additions & 0 deletions nginx.conf
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    worker_processes auto;
    events {
    worker_connections 4096;
    }

    # RTMP configuration
    rtmp {
    server {
    listen 1935; # Listen on standard RTMP port
    chunk_size 4000;

    application show {
    live on;
    # Turn on HLS
    hls on;
    hls_path /mnt/hls/;
    hls_fragment 4;
    hls_playlist_length 60;
    # Setup AES encryption
    hls_keys on;
    hls_key_path /mnt/hls/keys;
    hls_key_url keys/;
    hls_fragments_per_key 10;
    # disable consuming the stream from nginx as rtmp
    deny play all;
    }
    }
    }

    http {
    sendfile off;
    tcp_nopush on;
    directio 512;
    default_type application/octet-stream;

    access_log off;
    error_log off;

    # HTTPS certificate and key
    ssl_certificate ssl/example.com.cert;
    ssl_certificate_key ssl/example.com.key;

    server {
    listen 443 ssl;
    server_name example.com;
    root /mnt/;
    # Disable cache
    add_header 'Cache-Control' 'no-cache';
    index index.html;
    default_type "text/html";

    types {
    application/dash+xml mpd;
    application/vnd.apple.mpegurl m3u8;
    video/mp2t ts;
    plain/text key;
    }

    location =/ {
    # CORS setup
    add_header 'Access-Control-Allow-Origin' 'https://example.com' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length' always;
    add_header 'X-Frame-Options' 'DENY' always;
    }

    location /video {
    rewrite /hls/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.(ts|m3u8|key)$ /hls/$3.$4?token=$1&expires=$2;
    root /mnt/not-exist;
    }

    location /hls {
    internal;
    # The secure link is base on the folowing format
    # MD5("EXPIREY_DATE_IN_SECONDS CLIENT_IP_ADDRESS SECRET")
    # here is a BASH function that generates a secure link
    # get_token() {
    # local expires="$(date -d "today + 30 minutes" +%s)";
    # local token="$(echo -n "${expires} 127.0.0.1 VERY_COOL_SECRET" | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =)"
    # echo "${token}/${expires}"
    # }
    # echo "https://example.com/video/hls/$(get_token)/live.m3u8"

    secure_link $arg_token,$arg_expires;
    secure_link_md5 "$secure_link_expires $remote_addr VERY_COOL_SECRET";
    if ($secure_link = "") { return 403; }
    if ($secure_link = "0") { return 410; }

    # Referrer protection
    valid_referers server_names;
    if ($invalid_referer) {
    return 403;
    }

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

    server {
    listen 443 ssl default;
    server_name _;
    return 444;
    }
    }