それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

nginx で lua を使う(nginx lua でハローワールド)便利なテクニックをご紹介!

nginx で lua を使う

インストールと準備

deiban の場合は次で一発で終わる。

sudo apt install libnginx-mod-http-lua

最初に第一歩

  location ~ / {
    default_type 'text/html';
    content_by_lua "ngx.say('Hello, ngx_lua World!')";
  }

アクセスしてみる。

lua の実行結果が表示される。

もっと他の例

nginx で lua を使ってハローワールド(ソースを直接に記述)

  location /hello {
    content_by_lua '
      ngx.print("hello-")
      ngx.say("world")
    ';
  }

say( ) と print() がある。改行の違いがある say は改行付き。改行で "hello\n"とできない。sayを使う。

バージョン確認

location /lua_version{
    content_by_lua 'ngx.say(_VERSION)';
}

リクエストメソッドを見る。

location /method {
    content_by_lua '
        ngx.print(ngx.req.get_method())
    ';
}

if 文を使う。

  location /info {
    content_by_lua '
      m = ngx.req.get_method()
      if  m == "GET"  then
        ngx.print("we got GET request")
      elseif m == "POST" then
        ngx.print("we got POST request")
      else
        ngx.print("we got OTHER request")
      end
    ';
  }

テストする

curl  http://127.0.0.1/info
>  we got GET request
curl -X POST -d name  http://127.0.0.1/info
>  we got POST request
curl -X DELETE http://127.0.0.1/info
> we got OTHER requestt

ここまでで、if 文とHTTP メソッドの切り分け、そしてコンテンツの返し方がわかった。

ヘッダ書き出し

ヘッダの書き出し(リダイレクト)

https://github.com/openresty/lua-nginx-module#ngxredirect

location /info {
    content_by_lua '
      m = ngx.req.get_method()
      if m == "POST" then
         return ngx.redirect("/foo", ngx.HTTP_MOVED_TEMPORARILY)
      end
    ';
}

ステータスコードを変える 404 にする

location /info {
    content_by_lua '
      m = ngx.req.get_method()
      if m == "POST" then
        ngx.status = 404
        ngx.print("not found.")
        return;
       end
    ';
}

POSTの中身にアクセス。

https://github.com/openresty/lua-nginx-module#ngxreqget_post_args

  location /info {
    content_by_lua '
      m = ngx.req.get_method()
      if m == "POST" then
          ngx.req.read_body()
          local args, err = ngx.req.get_post_args()

          if err == "truncated" then
            -- one can choose to ignore or reject the current request here
          end

          if not args then
            ngx.say("failed to get post args: ", err)
            return
          end
          for key, val in pairs(args) do
            if type(val) == "table" then
              ngx.say(key, ": ", table.concat(val, ", "))
            else
              ngx.say(key, ": ", val)
            end
        end
      end
    ';
  }

エラー処理が多くてわかりにくい.、コア部分だけを書くと、次のようになる。

ngx.req.read_body()
local args, err = ngx.req.get_post_args()

key="input_name"
val=args[key]
if val then
  ngx.say( key, ": ",val)
end

コマンドの実行結果

適当なコマンドを実行して、結果をブラウザに返す。

 content_by_lua '
        local handle = io.popen("echo hello world")
        local ret = handle:read("*a")
        ngx.say( "ret : ", ret )
        return ;
';

luaにない機能を手早くつかうには、コマンドを呼び出したほうが早いから。

ただし、ユーザーのリクエスト(Cookieなどヘッダも含む)をつ勝ったコマンドのインジェクションには十分に注意すること。

リダイレクト

location /login {
  if ($request_method != POST) {
    return 302 $scheme://$host/auth.html;
  }
  if ($request_method = POST) {
    content_by_lua '
        -- なにか処理
        ngx.header["Set-Cookie"] = {
          string.format("hash=%s; path=/; Expires=%s", hash, expires),
          string.format("key=%s;  path=/; Expires=%s", rand, expires),
        }
        ngx.status=302
      else
        ngx.status=401
      end

      return ngx.redirect("/?"..ngx.var.query_string)  }
}

乱数の使い方。

シードが同じなら、同じ乱数が返される。

math.randomseed( tonumber("4a4",16) )

s = ""
for i = 64,1,-1 do
   s = s..(string.format("%x",math.random(0,15)))
end

print(s)

random と hash の cookie の値を一つにする

lua から nginx 変数をセットする、かつ外部ファイルにする。

lua とnginxは変数を共有することができるのです。

また、Luaは外部ファイルに記述が可能、外部Luaライブラリ・外部Luaファイルを使えば、nginxの設定ファイルから切り離して使い回しができる。

nginx の設定を抽象化してプログラミングっぽくLuaで書くことで可搬性がかなり向上します。

準備

外部ファイルを保存する場所を作る。

## nginx の起動時のディレクトリは /usr/share/nginx/ なので
mkdir -p /etc/nginx/lua
sudo ln -sr /etc/nginx/lua /usr/share/nginx/

server in nginx.conf

  location ~ / {
    default_type 'text/html';
    set_by_lua_file $greeting 'lua/hello.lua';
    return 200 'var from lua = "$greeting"';
  }

lua ファイル /etc/nginx/lua/hello.lua ( nginx からは/usr/share/nginx/lua/hello.luaとして見えている)

return 'Hello from lua file'

実行すると

まとめ

lua を使って nginx の痒いところに手が届く

## コンテンツを帰す場合
content_by_lua "ngx.say('Hello, ngx_lua World!')";
content_by_lua_file "lua/content.lua;
## 変数をセットする場合
set_by_lua  var_name  'return var_value';
set_by_lua_file $greeting 'lua/hello.lua';

これで、「動的に中身を変えた変数」を作ることが出来て、nginx の設定ファイルの中に固定値で設定を書く必要がなくなり、lua スクリプトから変数を自在に設定することができる。

リバプロ先を考えるとき、サーバーの設定を変えずに済むわけである。またCookieなどのcredentials をスタティックに書く必要がなくなり、nginx の設定にパスワード類が記載されないようになる。そのため「余計なものを持たない」ことになり、管理が向上しセキュリティ向上(セキュア)になるという算段である。

nginx再起動は必要か?

再起動の実行は不要だが、リロードは必要。つまり実質的に再起動は必須といえる。

nginx は lua のコンテンツをファイルの中身から読み取ってロードして保持しているので、都度都度ファイルからは読まない。

php とどっちがいいの

本当にちょっとしたことをするくらいなら lua で書いちゃうのがありだと思う。ログインチェックとかヘッダ追加とか。

たとえば、APIを他人に使わせないBearerで固定キー認証くらいならlua で書いてしまえばnginx+luaで片付くので、本当にシンプルでいい。

参考資料