在 NGINX-RTMP 上接收 RTMPS 流

在 NGINX-RTMP 上接收 RTMPS 流

RTMP 的标准做法仍然是在线路上使用纯文本流密钥。

我想接受从编码器到 NGINX 的 RTMPS 流,但是 RTMP 模块还没有 RTMPS。

我对所有允许获取 RTMP 流并通过 RTMPS 发送到 Facebook 等地方的中继解决方案都不感兴趣,因为相同的安全漏洞仍然存在,因为在某些时候您会通过纯文本传递密钥。

我的问题是,在哪里可以找到有关 RTMPS 的参考规范?我想知道在 RTMPS 源(例如 OBS)和 NGINX 之间进行正确握手需要哪些密钥,然后我将使用与 RTMP 模块的连接。服务器上是否可以采用普通密钥和 Let's Encrypt 之类的授权机构,以便与 RTMPS 编码器进行握手?

我见过有人使用 stunnel 将 RTMP 封装在 TLS 中。是否可以反过来做——使用 stunnel 接收 RTMPS,然后为 RTMP 模块转换回 RTMP?

答案1

更新:这是我最初的回答,很好地描述了使用 Nginx 实现 RTMPS 时可能遇到的问题。不过,我添加了一个改良版以实现更精细的访问控制,我建议使用它的配置。


是的,使用 stunnel 可以实现这一点,因为 RTMPS 只是一个包装在标准 TLS 会话内的 RTMP 会话。互联网上的示例大多是 RTMP→RTMPS,即 stunnel 作为纯文本服务器和 TLS 客户端运行,并配置了client = yes。如果没有它,client默认为no,即服务器模式

隧道配置可能如下所示:

[rtmps]
accept = 1935
connect = 127.0.0.1:1936
cert=/etc/letsencrypt/live/rtmp.example.com/fullchain.pem
key=/etc/letsencrypt/live/rtmp.example.com/privkey.pem

有了这个:

  • Nginx 应该正在监听 RTMP本地环回, 港口1936/tcp
  • 由于您无法使用 RTMP 更新 Let's Encrypt 证书,因此您可能也需要一个用于 HTTP-01 挑战的 HTTP 服务器块。
  • 由于与 Nginx 的连接始终来自 stunnel 即127.0.0.1,因此您不能再使用allow/deny指令来限制基于 IP 地址的连接。这意味着您的访问控制将仅限于密钥,但同时问题也更少,因为它是加密传输的。

    但是,这仍然会导致问题,因为您会从与使用该流的客户端相同的 IP 推送流,但您不能允许它们发布到您的流。幸运的是,您不必push使用密钥从应用程序中获取流,但你也可以pull它来自公共应用程序(/live)。

下列Nginx示例配置考虑了以下因素:

rtmp {
    server {
        listen 127.0.0.1:1936;
        chunk_size 4096;

        application app-secret-stream-key {
            live on;
            record off;
            allow publish 127.0.0.1;  # for streaming through stunnel
            allow play 127.0.0.1;     # for the pull from /live
        }

        application live {
            live on;
            record off;
            deny publish all;         # no need to publish on /live
            allow play all;           # playing allowed

            pull rtmp://127.0.0.1:1936/app-secret-stream-key;
        }
    }
}

http {
    server {
        listen 80;
        server_name rtmp.example.com;

        location ^~ /.well-known/acme-challenge/ {
            root /var/www/letsencrypt;
        }
        location / {
            return 404;
        }
    }
}

但是,这只是一个例子,所以你可以也应该修改它来满足你的确切需求。(另外,我没有测试过这个配置,而是完全根据文档编写的,所以如果我有错误,请随时纠正。)

答案2

由于 NGINX 能够终止上游 TCP 服务器的 TLS,因此仅使用 NGINX 就应该可以解决这个问题(只需stream从 @Esa Jokinen 向配置中添加部分即可):

stream {
    upstream backend {
        server 127.0.0.1:1936;
    }
    server {
        listen 1935 ssl;
        proxy_pass backend;
        ssl_certificate /etc/letsencrypt/live/rtmp.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/rtmp.example.com/privkey.pem;
    }
}

rtmp {
    server {
        listen 127.0.0.1:1936;
        chunk_size 4096;

        application app-secret-stream-key {
            live on;
            record off;
            allow publish 127.0.0.1;  # for streaming through stunnel
            allow play 127.0.0.1;     # for the pull from /live
        }

        application live {
            live on;
            record off;
            deny publish all;         # no need to publish on /live
            allow play all;           # playing allowed

            pull rtmp://127.0.0.1:1936/app-secret-stream-key;
        }
    }
}

http {
    server {
        listen 80;
        server_name rtmp.example.com;

        location ^~ /.well-known/acme-challenge/ {
            root /var/www/letsencrypt;
        }
        location / {
            return 404;
        }
    }
}

答案3

Nginx RTMPS + 秘密发布密钥 + 基于 IP 地址的访问控制

我决定将其发布为另一个答案,因为我的第一个答案仍然是一个很好的解释性答案,我也想给予赞扬达尼拉·维尔希宁指出使用 Nginx 的stream{}然而,虽然这两种答案都通过加密包括密钥在内的内容提​​高了安全性,但它们也剥夺了访问控制使用allow/deny [play|publish] address|subnet|all模块rtmp{}

stream{}代理 TCP有自己的访问控制,但(与rtmp{})不同,它无法区分发布和播放:使用单个stream{}代理,每个人都可以发布和播放 - 或者被拒绝执行任何操作。因此,使用两者的访问控制密钥和 IP 限制需要一个具有用于发布和流式传输的单独代理的结构:一个单独的 TCP 端口,用于使用密钥代理发布。下图演示了此设计:

在此处输入图片描述

在这里,我使用标准端口1935/tcp进行 RTMPS 播放,并使用附加端口1936/tcp进行 RTMPS 发布。对于内部未加密的 RTMP 连接,我使用类似的端口1935119361。红色代表未加密的连接和不受信任的网络,而绿色代表加密的连接和受信任的网络。

代理 TCP 现在有两个 (实时多播服务器) 配置,但两者仍然可以使用相同的证书:

stream {
    upstream publish {
        server 127.0.0.1:19361;
    }
    server {
        listen 1936 ssl;        # additional port for publishing
        proxy_pass publish;
        ssl_certificate /etc/letsencrypt/live/rtmp.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/rtmp.example.com/privkey.pem;

        allow 192.0.2.1;        # allow publish from this IP
        allow 192.0.2.0/24;     # -- also supports CIDR notation!
        deny all;               # deny publish from the rest
    }

    upstream live {
        server 127.0.0.1:19351;
    }
    server {
        listen 1935 ssl;        # standard RTMP(S) port
        proxy_pass live;
        ssl_certificate /etc/letsencrypt/live/rtmp.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/rtmp.example.com/privkey.pem;

        allow all;              # this is public (this is also the default)
    }
}

同样,我们现在需要两个独立的(本地环回)实时消息协议每个应用程序的服务器:

rtmp {
    server {
        listen 127.0.0.1:19361;
        chunk_size 4096;

        application secret-key {
            live on;
            record off;
            allow publish 127.0.0.1;  # publishing through rtmps://rtmp.example.com:1936
            allow play 127.0.0.1;     # for the pull from rtmp://localhost:19351/live
        }
    }

    server {
        listen 127.0.0.1:19351;
        chunk_size 4096;

        application live {
            live on;
            record off;
            deny publish all;         # no need to publish on /live -- IMPORTANT!!!
            allow play 127.0.0.1;     # playing through rtmps://rtmp.example.com:1935/live

            pull rtmp://127.0.0.1:19361/secret-key;
        }
    }
}

实际的基于 IP 的访问控制是在stream{}部分中完成的,因此只有 是deny publish all;强制的,以防止使用/live应用程序直接发布。我已将allow指令添加到rtmp{}部分中,只是为了澄清(和评论)RTMP 访问控制的默认行为。

答案4

不确定这是否有效,但我使用了与上述一样简单的 SSL 终止和代理协议来保留客户端 IP 地址。RTMP 模块支持代理协议,因此您仍然可以允许通过 IP 地址拒绝。做了一些基本测试,它似乎运行良好,日志中的正确消息和通过 IP 地址拒绝都有效。

也许我误解了,但它似乎正在做我认为它应该做的事情,即允许通过 RTMPS 进行 SSL 连接并保留客户端 IP 地址以在 RTMP 模块中进行访问控制。

我的流块如下所示:

stream {

    upstream backend {
        server 127.0.0.1:1935;
    }

    server {
        listen 1936 ssl;
        proxy_pass backend;
        proxy_protocol on;
        ssl_certificate /etc/..../fullchain.pem;
        ssl_certificate_key /etc/.../privkey.pem;
    }
}

以及我的 RTMP 部分的相关部分:(注意:直接连接可能不起作用但没有尝试过...在博客中建议这样做(见下文)但我不需要直接连接)。

rtmp {
    server {

        listen 1935 proxy_protocol;
        chunk_size 4000;

     
        application stream_app{
            live on;
            .....
            .....
            .....
            }
        }
    }
}

您可以在 RTMP 博客中阅读相关内容http://nginx-rtmp.blogspot.com

希望它能对某些人有所帮助,并感谢上述回答,因为它们为我指明了正确的方向。

仅供参考,我拒绝播放所有内容,因为我正在推出 HLS,目前不需要播放或任何其他内容。

相关内容