RailsでIP制限をPunditで実装してみる
前回に引きつづきRailsのアプリケーションレイヤーでなんとかIPによるアクセス制限をかける方法の模索。今回はAuthrization(認可)用のGemであるPunditを使ったアプローチです。
概要
- いろいろあってアプリケーションレイヤーでIP制限したくなった
- 前回はRailsでやる方法としてrack-attackでやる方法を考えてみた
- 今回はPunditで認可の流れの一環として制限するアプローチをしてみた
- どっちかというとrack-attackのほうが良いと思うけど、punditでも悪くない。でもCloudFlareとかと相性悪い
Pundit is何?
Punditは認可用のモジュールとして広くRailsで使われてるライブラリです。Railsのコントローラとアクションごとにポリシーを定義して制御します。シンプルなのが特徴で読み解くのも使い方に工夫するもの比較的楽にできます。
なぜやったか
- こんなこと(あった | 思った | 必要だった | 困っている)
- 興味をもったきっかけ
- 選択肢が他にもありそうな場合は選定理由も
前回、Rack::Attackで実装して十分に制限はできています。とはいえやはり依存するものは少ないにこしたことはないのでちょっと制限するためだけにRack::Attackをいれるのに抵抗があるかもしれません。PunditならWebアプリケーションとして認可のために入れていることが多いので、どうせならこれでやってみる方法を考えてみました。
前準備
前回の前準備として下記のようにnamespaceをrouting内できっていました。
Rails.application.routes.draw do
root "home#index"
namespace :admin do # admin配下には特定のIPでないとアクセスできないようにしたい
get "/", to: 'admin#index'
end
end
となるとadminで切られてるので、そこにBaseControllerをきってみます。
class Admin::BaseController < ApplicationController
include Pundit
before_action :autorize
end
こうしておけば/admin
以下でpunditの認可メソッドによる検証がおこなわれますね。もちろんAdmin以外でもPunditを使うならApplicationControllerに入れてることもあると思いますので、その場合は不要です。
PunditでIP制限をする
PunditでIP制限をかけてみます。Punditにはもともとcontroller内でuser
メソッドが生えていて、これはpundit_user
というメソッドをオーバーライドすることで変更できます。
class Admin::BaseController < ApplicationController
include Pundit
before_action :autorize
private
# これを追加
def pundit_user
request.ip
end
end
のようにしておきます。
そうするとリクエスト送信元のユーザのipが入ってきますので、あとはPunditのpolicyで
class Admin::BasePolicy < ApplicationPolicy
def index?
admin?
end
def show?
admin?
end
def create?
admin?
end
def new?
create?
end
def update?
admin?
end
def edit?
update?
end
def destroy?
admin?
end
# これで判断
def admin?
[
"127.0.0.1",
"123.456.7.8",
].include?(user)
end
end
みたいにしてやればいいですね。
Policyの詳しい使い方にPunditのドキュメントを参照すると良いでしょう。
やってみた結果
今回はPunditを使って認可によるでIP制限をしてみました。
これを本当に本番で使うか、というとちょっと疑問が残ります。というのもIP制限というログインユーザではなくアクセスしてきた情報を元に認可するのはPunditの責務としてはイマイチ適切ではない気がします。
前回のRack層でやるほうがアプローチとしては良いとは思います。とはいえ、冒頭で書いたようにいろんな理由でPunditでやれなくないですね。もしくはログインしたユーザの情報と併用してみるのも良いかもしれません。
また、こちらももちろんCDNなどでIP元が変われば同じく対応できませんね。
前回と合わせて2アプローチでRails内でIP制限を試してみました。Punditを使ったことがある人も多いと思うので、仕組みを理解していればすぐにできることでしょう。