mruby-ipaddress_matcherを使ってh2oでIPアドレスベースのアクセス制御をする Nov 1
h2oなどのウェブサーバで、特定のパスのみアクセスを制限したいといったケースが稀にある。 httpdでSetEnvIfなんかを使って実装したあれだ。
h2o 1.5.0ではmrubyのインタフェースとしてRackが採用され、こういったアクセス制御を以下のように書くことができるようになった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ALLOW_HOSTS = %w( 192.168.0.2 192.168.0.3 192.168.0.4 ) class Acl def call(env) if ALLOW_HOSTS.include?(env['REMOTE_ADDR']) [399, {}, []] else [403, {'Content-Type' => 'text/plain;charset=utf-8'}, ['Forbidden']] end end end Acl.new |
mrubyによりプログラマブルなアクセス制御が可能となり、特定のIPからのみを許可といったものであればこれで十分となった。
しかし、IPアドレスの範囲を指定して許可しようとすると以下のようなコードを書くことになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ALLOW_SEGMENTAIONS = %w( 192.168.0 192.168.1 ) class Acl def call(env) if ALLOW_HOSTS.include?(env['REMOTE_ADDR'].sub(/\.[0-9]{1,3}$/, '')) [399, {}, []] else [403, {'Content-Type' => 'text/plain;charset=utf-8'}, ['Forbidden']] end end end Acl.new |
SetEnvIfを彷彿とさせる柔軟性に欠けるコードである。 細かいアクセス制御を行おうとすると途端に苦しくなり、直感的でなくなる。
このような問題は、 mruby-ipaddress_matcher を使うと解決される。
mruby-ipaddress_matcher
mruby-ipaddress_matcher とは、CIDR表記をそのIPアドレスの範囲がマッチする正規表現へと変換するmrbgemである。
IPAddressMatcher::CIDR オブジェクトを作成し #to_regexp を実行すると、CIDR表記に基づいた正規表現が作成される。
1 2 | p IPAddressMatcher::CIDR.new('10.0.0.0/8').to_regexp #=> /10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ |
これを使うことで、前述のようなアクセス制御にCIDR表記を使用できるようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ALLOW_CIDRS = %w( 128.199.75.124/32 192.168.0.0/24 10.0.0.0/8 ) class Acl def initialize(*args) @allow_regexps = ALLOW_CIDRS.map {|cidr| IPAddressMatcher::CIDR.new(cidr).to_regexp } super end def call(env) if @allow_regexps.select {|allow_regexp| env['REMOTE_ADDR'] =~ allow_regexp }.empty? [403, {'Content-Type' => 'text/plain;charset=utf-8'}, ['Forbidden']] else [399, {}, []] end end end Acl.new |
CIDR表記が可能となったことで、直感的に許可するIPアドレスの範囲を記述できるようになり、同時に前述のアクセス制御では不可能であった、細かいIPの範囲も指定することができるようになった。
以降は、許可したいIPアドレスの範囲をCIDR表記で記述することで容易にアクセス制御が実現される。
非常にいい感じだ。