Nginx RTMPS + secret publishing key + IP address based access control
I decided to post this as another answer, as my first answer is still a good explanatory answer to keep, and I also wanted to give credits to Danila Vershinin for pointing out using Nginx's stream{}
. However, while both these answers increases security by encrypting the contents including the key, they also remove the ability of access control using allow
/deny
[play|publish] address|subnet|all
of the rtmp{}
module.
The stream{}
i.e. proxied TCP has own access control, but (unlike rtmp{}
) it can't distinguish publishing from playing: with a single stream{}
proxy everyone can both publish & play – or is denied from doing neither one. Therefore, an access control using both keys and IP restrictions requires a structure with separate proxies for both publishing and streaming: a separete TCP port for proxying the publishing with the key. The following diagram demonstrates this design:

Here, I use the standard port 1935/tcp
for the RTMPS-play and an additional 1936/tcp
for the RTMPS-publish. For the internal unencrypted RTMP connections I use similar ports 19351
and 19361
. The red color represents unencrypted connections & untrusted networks, whereas the green color represents encrypted connections & trusted networks.
The proxied TCP now has two (RTMPS) configurations, but both can still use the same certificate:
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)
}
}
Likewise, we now need two separate (local loopback) RTMP servers for each application:
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;
}
}
}
The actual IP based access control is done on the stream{}
section, so only the deny publish all;
is mandatory for preventing direct publishing using the /live
application. I've added the allow
directives to the rtmp{}
section just to clarify (and comment on) the default behaviour of the RTMP access control.