NGiNX RTMP module exec_xxx commands not working - nginx

I a having trouble nginx 1.10.3 and rtmp module exec_xxx commands. I have nginx.conf
with the following code.
rtmp {
...
server {
...
application dash { #creates a rtmp application
exec_options on;
exec_pull /bin/bash /usr/local/nginx/conf/ping.sh pull;
exec_push /bin/bash /usr/local/nginx/conf/ping.sh push;
exec_static /bin/bash /usr/local/nginx/conf/ping.sh static;
exec_publish /bin/bash /usr/local/nginx/conf/ping.sh publish;
...
I can read the rtmp DASH video externally that I publish internally from local host. So I know the conf file is working. I can also verify the directives are active with 'sudo -i nginx -T | grep exec_'. But the ping.sh (shown below) is not being executed.
#!/bin/bash
touch ./test.txt
/bin/echo "got message 1=$1 2=$2 3=$3"
/bin/echo "got message 1=$1 2=$2 3=$3" >>/usr/local/nginx/conf/exec_log.txt
The ping.sh command works when executed from ~/nginx/. How can I tell why the exec_pull static and other 'exec_xxx' commands are not working?

Related

Nginx - Run command on HTTP request

How can I run a command like shutdown when my Nginx receives an HTTP request?
I read about writing modules for this, but I find it overkilling, I just need to shutdown the computer when an HTTP request is answered.
There is an impressively creative solution to implement some kind of shell webhooks I found recently on GitHub (all credits goes to the author).
It consist of three parts.
A systemd unit to run a socat utility listening the loopback network interface (don't open used TCP port to the outer world in any case!):
[Unit]
Description=socat-based cmd api
After=network.target
[Service]
User=nobody
Group=nobody
ExecStart=/usr/bin/socat UDP-LISTEN:50333,fork,bind=127.0.0.1 EXEC:/opt/bin/shell-webhook
Restart=always
[Install]
WantedBy=multi-user.target
A shell script to be placed somewhere (the above unit assumes it to be the /opt/bin/shell-webhook):
#!/bin/bash
LOG=/var/log/shell-webhook.log
declare -a DATA
read -a DATA
cmd=${DATA[4]}
case ${cmd} in
reboot)
echo "Executing reboot" >> ${LOG}
shutdown -r now
;;
shutdown)
echo "Executing shutdown" >> ${LOG}
shutdown -P now
;;
*)
echo "Unknown command $cmd" >> ${LOG}
;;
esac
An nginx-relevant configuration part (consider preventing access to this location using some kind of access control - allow/deny rules, basic auth, etc.):
log_format shellwebhook '$cmd\n';
server {
...
location ~* ^/cmd/(\w+)$ {
set $cmd $1;
return 200 "$cmd";
access_log syslog:server=127.0.0.1:50333,facility=local7,tag=nginx,severity=info,nohostname shellwebhook;
}
}

Is it possible to execute a bash script with root permission from NGINX and get the output?

I'm running Nginx under Openresty build so Lua scripting is enabled. I want to create a URI location (which will be secured with SSL +authentication in addition to IP whitelisting) which allows webhooks calls from authorized sources to execute bash scripts on the server using root permission. e.g.
https://someserver.com/secured/exec?script=script.sh&param1=uno&param2=dos
NGINX would use the 'script' and 'param#' GET request arguments to execute "script.sh uno dos" in a shell. It captures the script output and bash return code (if that's possible).
I understand the security implications of running NGINX as root and running arbitrary commands but as mentioned access to the URI would be secured.
Is this possible via native NGINX modules or maybe Lua scripting? Any sample code to get me started?
Thank you.
There is another possible solution which won't need extra nginx lua plugins. This is using socat. You start a socat on port 8080 which on every connection executes a bash script
socat TCP4-LISTEN:8080,reuseaddr,fork EXEC:./test.sh
test.sh
#!/bin/bash
recv() {
echo "< $#" >&2;
}
read line
line=${line%%$'\r'}
recv "$line"
read -r REQUEST_METHOD REQUEST_URI REQUEST_HTTP_VERSION <<<"$line"
declare -a REQUEST_HEADERS
while read -r line; do
line=${line%%$'\r'}
recv "$line"
# If we've reached the end of the headers, break.
[ -z "$line" ] && break
REQUEST_HEADERS+=("$line")
done
eval $(echo $REQUEST_URI | awk -F? '{print $2}' | awk -F'&' '{for (i=1;i<=NF;i++) print $i}')
cat <<END1
HTTP/1.1 200 OK
Content-Type: plain/text
REQUEST_METHOD=$REQUEST_METHOD
REQUEST_URI=$REQUEST_URI
REQUEST_HTTP_VERSION=$REQUEST_HTTP_VERSION
REQUEST_HEADERS=$REQUEST_HEADERS
script=$script
param1=$param1
param2=$param2
END1
And test on curl is as below
$ curl "localhost:8080/exec?script=test2.sh&param1=abc&param2=def"
REQUEST_METHOD=GET
REQUEST_URI=/exec?script=test2.sh&param1=abc&param2=def
REQUEST_HTTP_VERSION=HTTP/1.1
REQUEST_HEADERS=Host: localhost:8080
script=test2.sh
param1=abc
param2=def
So you can easily use this for a proxy_pass in nginx.
If you need see complete server in bash using socat, have a look at https://github.com/avleen/bashttpd/blob/master/bashttpd

Openresty Hello world with docker

I'm trying make my application dockerize for that I've been following official openresty dockerfile. Os in my system is Ubuntu 16.04 64 bit.
I have already pull that image using this cmd.
docker pull openresty/openresty:1.11.2.3-xenial
Now I want to use this image and want make simple hello world application. For that I have created my work directory, create one custom dockerfile and build my custom image with that. And finaly I run that image. Below is my dockerfile content.
FROM openresty/openresty:1.11.2.3-xenial
EXPOSE 8080
CMD nginx -p `pwd` -c nginx.conf
nginx.conf
worker_processes 1;
error_log stderr notice;
events {
worker_connections 1024;
}
http {
include /usr/local/openresty/nginx/conf/mime.types;
server {
listen 8888;
location / {
default_type text/html;
content_by_lua_file "app.lua";
}
}
}
app.lua
ngx.say('Hello World!')
ngx.exit(200)
Build image
docker build -t user/openresty .
Start container
docker run rahul/openresty
When I try to start container, it gives me an error like nginx: invalid option: "/bin/sh"
I have no idea that I'm going on right or wrong direction.
Update:
docker run -it -p 8888:80 -v /home/software/docker/openresty:/usr/local/openresty/nginx/html:ro openresty/openresty:1.11.2.3-xenial
I just used this CLI and it's start showing index.html that I have created. Again I tried to link my custom nginx.conf using below CLI but it's not working.
docker run -it -p 8888:8888 -v /home/software/docker/openresty:/usr/local/openresty/nginx/html:ro -v /home/software/docker/openresty/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro openresty/openresty:1.11.2.3-xenial
docker run -it -p 8888:8888 -v $(pwd):/app openresty/openresty:1.11.2.3-xenial -p /app -c nginx.conf
With below command it starts working but can anyone please explain it?
docker run -it -p 8888:8888 -v $(pwd):/app openresty/openresty:1.11.2.3-xenial -p /app -c nginx.conf
You have wrong port settings, look:
EXPOSE 8080
!=
listen 8888;

Fail to restream with nginx-rtmp-module

I'm unable to start restream using command exec. Also I tried exec_pull.
It didn't help. My target is to restream rtmp://ktv.s4c.link/live/livestream url to my local nginx, to rtmp://localhost:1935/hls.
/tmp/logs/ffmpeg.log is empty.
I guess that exec not even called, but why?
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
allow play all;
live on;
record off;
#pull rtmp://ktv.s4c.link/live/livestream;
exec ffmpeg -i rtmp://localhost:1935/live -f flv rtmp://localhost:1935/hls 2>>/tmp/logs/ffmpeg.log;
}
application hls {
allow play all;
live on;
record off;
}
}
I'm using nginx-1.12.0.
I followed by this tutorial, watched this
So, first of all, be aware of that Nginx run all your exec commands as nobody user. And you have to test it in command line like this:
sudo -u nobody ffmpeg -i rtmp://ktv.s4c.link/live/livestream -f flv rtmp://localhost:1935/hls
And then if this command line works - print to nginx.conf.
Secondly, use the full path. It's very important, because a child process than Nginx runs has different environment compared with your current user environment.
sudo -u nobody /usr/bin/ffmpeg -i rtmp://ktv.s4c.link/live/livestream -f flv rtmp://localhost:1935/hls
Thirdly, my mistake was that I used FFmpeg from the repository and that FFmpeg has 2.8.11-0ubuntu0.16.04.1 version. Related to this answer
FFmpeg can be compiled with librtmp, that's why FFmpeg can require live=1 and quotes("). So the final command that I copied to nginx.conf was:
sudo -u nobody /usr/bin/ffmpeg -i "rtmp://ktv.s4c.link/live/livestream live=1" -f flv rtmp://localhost:1935/hls
My final nginx.conf
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
pull rtmp://ktv.s4c.link/live/livestream;
exec_play /usr/bin/ffmpeg -i "rtmp://ktv.s4c.link/live/livestream live=1" -f flv rtmp://localhost:1935/hls 2>>/tmp/ffmpeg-$name.log;
}
application hls {
live on;
record off;
}
}
As you can mention I added FFmpeg logs 2>>/tmp/ffmpeg-$name.log.

docker custom nginx container failed to start

I am trying to build a nginx image from scratch (instead of using the official nginx image)
FROM ubuntu
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -v /etc/nginx/nginx.conf
ADD nginx.conf /etc/nginx/
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80
COPY ./files/ /var/www/html/
CMD service nginx start
And this is my nginx.conf file under current directory.
server {
root /var/www/html
location / {
index.html
}
}
And my dummy index.html file under ./files folder
<p1>hello world</p1>
I run this command
docker build -t hello-world .
And
docker run -p 80:80 hello-world
But I got error saying
* Starting nginx nginx
...fail!
What maybe the issue?
Don't use "service xyz start"
To run a server inside a container, don't use the service command. That is a script which will run the requested server in the background, and then exit. When the script exits, the container will stop (because that script was the primary process).
Instead, directly run the command that the service script would have started for you. Unless it exits or crashes, the container should remain running.
CMD ["/usr/sbin/nginx"]
nginx.conf is missing the events section
This is required. Something like:
events {
worker_connections 1024;
}
The server directive is not a top-level element
You have server { } at the top level of the nginx.conf, but it has to be inside a protocol definition such as http { } to be valid.
http {
server {
...
nginx directives end with a semicolon
These are missing at the end of the root statement and your index.html line.
Missing the "index" directive
To define the index file, use index, not just the filename by itself.
index index.html;
There is no HTML element "p1"
I assume you meant to use <p> here.
<p>hello world</p>
Final result
Dockerfile:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -v /etc/nginx/nginx.conf
ADD nginx.conf /etc/nginx/
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80
COPY ./files/ /var/www/html/
CMD ["/usr/sbin/nginx"]
nginx.conf:
http {
server {
root /var/www/html;
location / {
index index.html;
}
}
}
events {
worker_connections 1024;
}
daemon off;
one can use directly the official image of nginx in docker hub, just start your docker file with this line : FROM nginx
here is an example of docker file that you can use :
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY static-html-directory /usr/share/nginx/html
EXPOSE 80
as you see there is no need to use a CMD to run your nginx server

Resources