読者です 読者をやめる 読者になる 読者になる

ngx_mrubyを使って特定ホスト以外からのアクセスをメンテナンス画面にする

こんな感じ。(ほんとはもうちょいifが多い)

error_page 503 /maintenance.html;
location = /maintenance.html { internal; }

mruby_access_handler_code '
  c = Nginx::Connection.new
  r = Nginx::Request.new

  Nginx.return -> do
    if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3"
      return Nginx::DECLINED
    end

    if !File.exists?("/var/tmp/maint")
      return Nginx::DECLINED
    end

    # 503
    return Nginx::HTTP_SERVICE_UNAVAILABLE
  end.call
';

これで、

  • /var/tmp/maintが存在したらメンテナンス画面になって
  • /var/tmp/maint_ignore_officeが存在して、且つアクセス元が192.168.1.3であれば、そこは通常通りレスポンス

になる。

元々はひろせさんのこの方式で変数をsetしてコントロールしてたんだけど nginxで特定ホスト以外からのアクセスをメンテナンス画面にする方法 (2) - (ひ)メモ

その後ふじわらさんのフラグファイルみて切り替えるのがよいなぁと思って nginxで特定ホスト以外からのアクセスをメンテナンス画面にする方法 - 酒日記 はてな支店

でも特定ホスト(今回だとオフィス)からのみ開放するのもフラグファイルの有る無しでコントロールするとなると、またNginxの「複数条件できん」がネックで、うーんってなって、

あーそうだ!このNginxはngx_mrubyつかえるようになってるからそれでやろーっと。てなって上記のコードのようになった次第。

ちなみに、最初はこんな感じでハンドラのコードを書いたんだけど、これはうまくくいかなくて

if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3"
  Nginx.return Nginx::DECLINED
end

if !File.exists?("/var/tmp/maint")
  Nginx.return Nginx::DECLINED
end

Nginx.return Nginx::HTTP_SERVICE_UNAVAILABLE

弊社は、同僚に @ がいるという便利な環境なので、聞いたら

Nginx.returnはコードからreturnするわけではないので 全部実行されちゃいます。

と教えてもらって、なるほどん!ってなって、次にこんな感じにしたら

if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3"
  return Nginx.return Nginx::DECLINED
end

if !File.exists?("/var/tmp/maint")
  return Nginx.return Nginx::DECLINED
end

return Nginx.return Nginx::HTTP_SERVICE_UNAVAILABLE

こんどは、LocalJumpError が出てしまって、あーなるほどmrubyのハンドラって関数なわけじゃないので途中で抜けれないのねぇってなって、lambdaにしてreturnする冒頭のコードに落ち着いたのでした。

褒めてもらった。

このラムダな書き方は、mruby_setディレクティブとかでも同様につかえて、なかなかシャレオツ便利です。

尚、今回は大したロジックでもないので、直接nginxの設定にコードを埋め込んでいるけど、複雑なのは、外部ファイルに切り出して、テストも書いた方がいいです。

テストのあたりは@さんのこれがわかりやすい。

How to test code with mruby

構成管理(puppet)リポジトリrake mtestすると全ファイルのテストが実行されるようにしてます。

ngx_mruby大変便利なので、今後もどんどん使っていく所存です。