domUが送受信するパケットのIPアドレスを制限する

前回に引き続きCentOS 5でのXenに関するネタです。今回実現したいのは、例えばdomUIPアドレスが192.168.1.2だった場合、それ以外のIPを利用して送受信するのを防ぐことです。これはdomUのroot権限を持ったユーザが、IPアドレスを詐称して通信する場合などを想定しています。そのためdom0側で通信を制限する方法を考えます。

以下の例では、domU側のeth0をvif1.0としてブリッジに接続している場合を想定しています。またdomUIPアドレスは192.168.1.2とします。

この設定を実現する方法として、ここではブリッジの入出力部分であるvif1.0を出入りするパケットについて、IPアドレスが許可されたものかどうかを確認するようにします。

iptablesでブリッジのフィルタリングをする場合、physdevモジュールを利用することができます。そこでphysdevを利用して、vif1.0から出てくるパケットの送信元が192.168.1.2以外だった場合は破棄するようにします。

# iptables -I FORWARD 1 -m physdev --physdev-in vif1.0 --src ! 192.168.1.2/32 -j DROP

また念のため、vif1.0に入っていく方もフィルタリングしておきます。ただしこの設定ではブロードキャストやマルチキャストのパケットも破棄してしまいますので、必要な場合はそれらに対する許可設定も必要です。

# iptables -I FORWARD 1 -m physdev --physdev-out vif1.0 --dest ! 192.168.1.2/32 -j DROP

このままだと通信できない理由が後で分からなくなる可能性がありますので、実際に利用する場合はリミット付きでログを出力するように設定した方が良いかもしれません。

さてフィルタリングの設定自体はこれで完了です。しかしCentOS 5.2では、残念ながらこのままでは設定が反映されないようです。そこでいつものごとくGoogleしてみたところ、同じ症状の人を見つけることが出来ました(http://bugs.centos.org/view.php?id=2900)。

このページによると、Xenの起動時にnet.bridge.bridge-nf-call-iptablesという設定がOFFにされていることが原因のようです。そこでこの設定をONにしてみたところ無事に動作するようになりました。

# sysctl -w net.bridge.bridge-nf-call-iptables=1

このままでは再起動時に設定が消えてしまいますので、/etc/xen/scripts/xen-network-common.shの該当部分を書き換えておくと便利かもしれません。

そもそもなぜXenの起動時にこの設定をするようになったのか調べてみたところ、それらしきやり取りがメーリングリストにありました(http://lists.xensource.com/archives/html/xen-devel/2006-07/msg00240.html)。

流れとしては、domUから通信できない問題がある、実はiptablesの設定が影響していた、dom0を想定して設定しているiptablesの処理がブリッジにまで適用されるのは紛らわしい、ブリッジにはiptablesの設定が適用されないようにしよう、ということのようです。

本来はXen用のiptables設定をしてくれると良いのですが、複雑になるので根本的にOFFにしてしまったのですね。確かにこのままでは紛らわしいので、OFFにした場合と同じ感じになるように設定を追加してみます。環境により異なると思いますが、CentOS5.2のデフォルト設定の場合は下記を追加しておけば大丈夫なようです。

# iptables -I FORWARD 3 -i xenbr0 -o xenbr0 -j ACCEPT
# iptables -L
...
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  anywhere            !192.168.1.2      PHYSDEV match --physdev-out vif1.0
DROP       all  -- !192.168.1.2          anywhere         PHYSDEV match --physdev-in vif1.0
ACCEPT     all  --  anywhere             anywhere
RH-Firewall-1-INPUT  all  --  anywhere             anywhere
...

デフォルトではRH-Firewall-1-INPUTに飛ぶようになっていますので、今回の設定とRH-Firewall-1-INPUTに飛ぶ設定の間に挿入してやる必要があります。