1

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.

Dana Robinson
  • 4,304
  • 8
  • 33
  • 41
bobbybay
  • 11
  • 1
  • 4
  • Use https://github.com/jprjr/lua-resty-exec or https://github.com/juce/lua-resty-shell. Start Sockproc as root. – Alexander Altshuler Sep 28 '17 at 14:13
  • Thank you. I wonder if there's a way that doesn't require running a daemon. Seems lua os.execute can run a shell command but can't return its output or status code. I thought of creating a bash wrapper script which takes the script name & it's args (as it's own args) and runs it. Then dumps the output and exit code to text files so they can be read by Lua. Prob should pass along a unique ID to the wrapper script to be used as filenames to ensure no two calls write over the same file. I wonder if anyone can think of a cleaner solution. – bobbybay Sep 28 '17 at 15:06
  • https://stackoverflow.com/questions/132397/get-back-the-output-of-os-execute-in-lua – Alexander Altshuler Sep 28 '17 at 16:36

1 Answers1

10

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

bgdnlp
  • 1,031
  • 9
  • 12
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265