ngx_mrubyでリダイレクタを作る


この記事は

  • しょぼいHTTPリダイレクタをngx_mrubyで作ってみたのでメモです

やりたかったこと

  • ある内部向けサイトと外部向けサイトの2つのサイトがありました
  • 下記のような感じでおなじリダイレクタにアクセスしてるんだけど拠点によって振り分け先を変えたかったです
    • 社内 => リダイレクタ => 内部向けサイト
    • 社外 => リダイレクタ => 外部向けサイト
  • 内部向けサイトはIPで縛ったりしているので、ProxyではなくRedirectorで実装したかったです

それAWSでできない?

  • 最初はAWSの設定で適当にやろうとおもっていたのですが、調べた限りにはできなかったです
  • ALBの場合
    • 対応してる条件: ホスト名が一致する場合またはパスが一致する場合
    • 可能な処理処理: リダイレクト
  • WAFの場合
    • 対応してる条件: ソースIPが一致する場合
    • 可能な処理処理: ブロック
  • ていう感じで、ソースIPが一致する場合リダイレクトっていう処理はできそうでできませんでした

そこで

  • できそうでできないAWSにむしゃくしゃしたので面舵を180度振り切って、ngx-mrubyを使って、上記のリダイレクタをスクラッチで作ってしまえ!と思い立ちました
  • ちなみに下記の案も頭をよぎりましたが、却下しました
    • 普通のnginx.confでRewrite地獄を頑張る・・・地獄はいやだ
    • rails立ててredirect_toとかで適当にコーディングする・・・後々セキュリティfix取り込んで保守するのとか(ry

やり方

ecsでやります

  • 最初からngx-mrubyが動く状態のdocker imageが公開されていることがわかったので、ecsで行くことにしました
  • 別に普通のecsなので諸々割愛

dockerイメージの準備

  • 作者様謹製のDockerfileをcloneしてきます
git clone https://github.com/matsumotory/docker-ngx_mruby.git

Dockerfile

  • DockerfileはそのままでOKです
Dockerfile
FROM matsumotory/ngx_mruby:master
MAINTAINER matsumotory

#
# matsumotory/ngx-mruby image supports ONBUILD for below commands.
#
# ONBUILD ADD docker/hook /usr/local/nginx/hook
# ONBUILD ADD docker/conf /usr/local/nginx/conf
# ONBUILD ADD docker/conf/nginx.conf /usr/local/nginx/conf/nginx.conf
  • ここのコメントに書かれている通り、docker/*の下にある各種ファイルがdocker build時に勝手にイメージに取り込まれます

nginx.conf

  • こんな感じにして、全アクセスをmruby_content_handlerに振り分けます
  • ここで指定しているredirect.rbがmrubyのリダイレクタの書かれたファイルです
docker/conf/nginx.conf
user daemon;
daemon off;
master_process off;
worker_processes 1;
error_log stderr notice;
#access_log /dev/stdout;

events {
    worker_connections  1024;
}

http {
    access_log /dev/stdout;
    server {
        listen 80;

        location ~ ^/.*$ {
            mruby_content_handler /usr/local/nginx/hook/redirect.rb;
        }
    }
}

mrubyコード

  • mrubyのコードはこんな感じです
  • さいしょからそれっぽいクラスが定義されていて各種情報の受け取りとかリダイレクト処理とかできます
  • いい感じにClass説明のドキュメントも準備されてて感謝です
docker/hook/redirect.rb
INTERNAL_IPS = [
  "localhost"
]
PRIVATE_URL = 'https://private.example.com'
PUBLIC_URL = 'https://public.example.com'

r = Nginx::Request.new
path = r.unparsed_uri
header = r.headers_in

source_ip = header['X-Forwarded-For']
source_ip ||= Nginx::Connection.new.remote_ip

if INTERNAL_IPS.include?(source_ip)
  Nginx.redirect("#{PRIVATE_URL}#{path}", Nginx::HTTP_MOVED_PERMANENTLY)
else
  Nginx.redirect("#{PUBLIC_URL}#{path}", Nginx::HTTP_MOVED_PERMANENTLY)
end

普通にbuild

  • buildしてrunします
  • 普通にローカルで動きます
docker build -t nginx-mruby-redirect .
docker run -d nginx-mruby-redirect
  • ちなみにdocker execしてこのmrubyコードを書き換えると再起動等一切なしで反映されます
  • めっちゃデバッグしやすくてちょっと感動した

デプロイすると

  • ecsの設定もろもろして、デプロイしてみたら普通に動きました!

終わりに

  • 想像よりもだいぶお手軽にできました!
  • gem使おう等するとbuildからやり直しっぽくて、docker image作るところからになりそうだったのでスキップしたけどそのうちやってみたい。