Nginx lua plugin for uploads authentication

Sometimes you want to allow your HTTP clients to upload files to a protected location without having to pass data through a secure intermediary proxy node that protect the access credentials. The clients must first request a signed URL from a third-party and then can upload the file using a HTTP POST or PUT, thus reducing latency.

For this example the signed URL has the following structure:

https://<fqdn>/<uri>?e=<timetamp>&s=<signature>

where:

  • <signature>=hmac(<password>, <uri>|<http method>|<expire timestamp>)
  • <timestamp> is the unix time designating the expiration of the URL
  • <password> is a shared secret between the HTTP server and the third party involved.

The authentication scheme is similar to the Amazon AWS signature.

The code is listed bellow (access.lua):

local expire = ngx.var.arg_e
local sign = ngx.var.arg_s
local uri = ngx.var.uri
local method = ngx.var.request_method

local path = ""
local secret = "password"

local hmac = require "resty.hmac"

 

local hmac_sha256 = hmac:new(secret, hmac.ALGOS.SHA256)
   if not hmac_sha256 then
      ngx.log(ngx.STDERR, "failed to create the hmac_sha256 object")
   return
end

 

--ngx.log(ngx.STDERR, "uri= " .. uri .. " method= " .. method .. " expire= " .. expire .. " sign= " .. sign)

if expire ~= nul and sign ~= null and uri:find("upload") and method == "PUT" then

   local digest = hmac_sha256:final(uri..method..expire, true)

   ngx.log(ngx.STDERR, "time="..os.time() .. " inputData= " .. uri.."|"..method.."|"..expire .. " sha256: " .. digest)

   if digest == sign and tonumber(expire) > os.time() then
      ngx.log(ngx.STDERR, "Upload ok")
      return ngx.OK
   end

   elseif method == "GET" then
      return ngx.OK
end

return ngx.exit(403)

And the corresponding nginx server configurations:

location /upload/ {
  lua_code_cache on;
  access_by_lua_file 'sites-available/access.lua';
  root      /var/dav;

  client_body_temp_path /var/dav/temp;
  dav_methods     PUT DELETE;
  create_full_put_path  on;
  dav_access    user:rw group:rw all:rw;
  autoindex     off;
}

The nginx server must be compiled with the openresty/lua-nginx-module.

Compile latest nginx with latest OpenSSL on Raspbian strech

Nginx is a lightweight web server, which can also be used as a reverse proxy, load balancer or HTTP cache. With additional applications it can serve dynamic web pages like those written in PHP.
This tutorial is based on nginx 1.11.10 and openssl 1.1.0e.

First, create a directory:

cd ~
mkdir nginx
cd nginx

Download nginx latest version using wget, then extract the archive:

 wget https://nginx.org/download/nginx-1.11.10.tar.gz
 tar xvf  nginx-1.11.10.tar.gz

Download openssl latest version and extract the archive

 wget https://www.openssl.org/source/openssl-1.1.0e.tar.gz
 tar xvf  openssl-1.1.0e.tar.gz

Switch to the newly created nginx source directory

cd nginx-1.11.10

Start the configuration:

./configure \
--http-client-body-temp-path=/var/run/nginx/body \
--http-fastcgi-temp-path=/var/run/nginx/fastcgi \
--http-proxy-temp-path=/var/run/nginx/proxy \
--http-scgi-temp-path=/var/run/nginx/scgi \
--http-uwsgi-temp-path=/var/run/nginx/uwsgi \
--user=www-data \
--group=www-data \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--with-pcre-jit \
--with-http_v2_module \
--with-debug \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_dav_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-http_xslt_module \
--with-http_ssl_module \
--with-ld-opt=-lrt \
--with-openssl=../openssl-1.1.0e \
--with-http_v2_module \
--with-http_ssl_module

It takes about 1 minute. You can save the configure command parameters for later usage.

If everything looks good and no errors are reported, you can now start the compilation with the command (assuming your CPU has 4 cores)

 make -j4

Depending on the system load, compiling takes about 10 minutes. If no errors are reported, test the resulting binary:

objs/nginx -V

The following output is expected:

nginx version: nginx/1.11.10
built by gcc 6.3.0 20170124 (Raspbian 6.3.0-5+rpi1) 
built with OpenSSL 1.1.0e  16 Feb 2017
TLS SNI support enabled
configure arguments: --http-client-body-temp-path=/var/run/nginx/body --http-fastcgi-temp-path=/var/run/nginx/fastcgi --http-proxy-temp-path=/var/run/nginx/proxy --http-scgi-temp-path=/var/run/nginx/scgi --http-uwsgi-temp-path=/var/run/nginx/uwsgi --user=www-data --group=www-data --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --with-pcre-jit --with-http_v2_module --with-debug --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_dav_module --with-http_gzip_static_module --with-http_sub_module --with-http_xslt_module --with-http_ssl_module --with-ld-opt=-lrt --with-openssl=../openssl-1.1.0e --with-http_v2_module --with-http_ssl_module

Finally, install

make install