mruby-ipaddress_matcherを使ってh2oでIPアドレスベースのアクセス制御をする Nov 1

h2o

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表記で記述することで容易にアクセス制御が実現される。

非常にいい感じだ。


このエントリーをはてなブックマークに追加