libvirtが何か吐いとる。¶
メモ2。
[ 44.057211] libvirtd[2295]: segfault at d ip 00007fb4c8f406c1 sp 00007fff1b8d8d48 error 4 in libc-2.13.so[7fb4c8ec4000+17a000
起動時のcall trace。¶
メモ。
[ 2.140912] Call Trace:
[ 2.140914] <IRQ> [<ffffffff81089a79>] ? __report_bad_irq.clone.2+0x30/0x7c
[ 2.140927] [<ffffffff81089bcc>] ? note_interrupt+0x107/0x177
[ 2.140933] [<ffffffff8130ef56>] ? _raw_spin_lock_irqsave+0x1e/0x29
[ 2.140937] [<ffffffff8108a14e>] ? handle_level_irq+0xbc/0x108
[ 2.140942] [<ffffffff81005307>] ? handle_irq+0x17/0x1d
[ 2.140946] [<ffffffff81005030>] ? do_IRQ+0x45/0xad
[ 2.140950] [<ffffffff8130f453>] ? ret_from_intr+0x0/0x15
[ 2.140955] [<ffffffff81042e2c>] ? __do_softirq+0x55/0x177
[ 2.140959] [<ffffffff8100591d>] ? timer_interrupt+0x19/0x23
[ 2.140963] [<ffffffff810887df>] ? handle_IRQ_event+0x4e/0x10b
[ 2.140967] [<ffffffff810037dc>] ? call_softirq+0x1c/0x28
[ 2.140970] [<ffffffff8100534c>] ? do_softirq+0x3f/0x7f
[ 2.140974] [<ffffffff81042fda>] ? irq_exit+0x36/0x7f
[ 2.140977] [<ffffffff8100507f>] ? do_IRQ+0x94/0xad
[ 2.140982] [<ffffffff81231b0a>] ? dmam_coherent_release+0x0/0x73
[ 2.140986] [<ffffffff8130f453>] ? ret_from_intr+0x0/0x15
[ 2.140988] <EOI> [<ffffffff8122cf0f>] ? devres_alloc+0x2f/0x47
[ 2.140996] [<ffffffff8122ceff>] ? devres_alloc+0x1f/0x47
[ 2.141000] [<ffffffff81231d92>] ? dmam_alloc_coherent+0x2b/0xf9
[ 2.141006] [<ffffffffa00e3e35>] ? ahci_port_start+0xf5/0x166 [libahci]
[ 2.141010] [<ffffffff8130ef56>] ? _raw_spin_lock_irqsave+0x1e/0x29
[ 2.141020] [<ffffffffa00b3415>] ? __ata_port_freeze+0x35/0x3b [libata]
[ 2.141027] [<ffffffffa00a9d87>] ? ata_host_start+0xc6/0x151 [libata]
[ 2.141031] [<ffffffffa00e4d01>] ? ahci_interrupt+0x0/0x5b9 [libahci]
[ 2.141040] [<ffffffffa00af3fd>] ? ata_host_activate+0x1f/0xd7 [libata]
[ 2.141046] [<ffffffffa00eebd3>] ? ahci_init_one+0x7c7/0x7e0 [ahci]
[ 2.141051] [<ffffffff8101f5ee>] ? default_spin_lock_flags+0x5/0xb
[ 2.141055] [<ffffffff812316c4>] ? __pm_runtime_set_status+0x12e/0x155
[ 2.141059] [<ffffffff8101f5ee>] ? default_spin_lock_flags+0x5/0xb
[ 2.141063] [<ffffffff8130ef56>] ? _raw_spin_lock_irqsave+0x1e/0x29
[ 2.141068] [<ffffffff8119e260>] ? local_pci_probe+0x49/0x95
[ 2.141072] [<ffffffff8130ef35>] ? _raw_spin_lock+0x5/0x8
[ 2.141076] [<ffffffff8119e837>] ? pci_device_probe+0xc4/0xf3
[ 2.141080] [<ffffffff8122ae07>] ? driver_sysfs_add+0x66/0x8d
[ 2.141085] [<ffffffff8122b084>] ? driver_probe_device+0xa8/0x138
[ 2.141088] [<ffffffff8122b163>] ? __driver_attach+0x4f/0x6f
[ 2.141092] [<ffffffff8122b114>] ? __driver_attach+0x0/0x6f
[ 2.141095] [<ffffffff8122a388>] ? bus_for_each_dev+0x47/0x72
[ 2.141099] [<ffffffff8122aa14>] ? bus_add_driver+0xae/0x1fe
[ 2.141105] [<ffffffffa00f5000>] ? ahci_init+0x0/0x22 [ahci]
[ 2.141108] [<ffffffff8122b398>] ? driver_register+0x8d/0xf5
[ 2.141114] [<ffffffffa00f5000>] ? ahci_init+0x0/0x22 [ahci]
[ 2.141117] [<ffffffff8119ea92>] ? __pci_register_driver+0x50/0xbb
[ 2.141123] [<ffffffffa00f5000>] ? ahci_init+0x0/0x22 [ahci]
[ 2.141126] [<ffffffff810002e6>] ? do_one_initcall+0x78/0x136
[ 2.141132] [<ffffffff8106c8aa>] ? sys_init_module+0x9c/0x1d7
[ 2.141136] [<ffffffff810028d2>] ? system_call_fastpath+0x16/0x1b
[ 2.141139] handlers:
[ 2.141189] [<ffffffff8123b199>] (usb_hcd_irq+0x0/0x6c)
RSSがfetchされないのはエントリ内容が長すぎるせい?¶
昨日のエントリ「 libvirtのnwfilterについて調べてみた。 」のRSSがGoogle Readerでも、Planet Debian JPでも出力されていないようです。cURLで直接アクセスすると見られるので、長すぎるからダメなんでしょうか。あるいは含まれている文字列によってエラーになっているかもしれないですが。とりあえず、RSSを要約モードに変更してみました。過去のエントリはfetchされないようですけどね。
libvirtのnwfilterについて調べてみた。¶
もともとの経緯としては、 2月のDebian勉強会 でSqueezeリリース記念で、「ヤッター、リリースしたよ! Squeezeになってうれしいこと、Squeezeになって変わったことを教えてください。」という事前課題で 自分の回答してそれに対して自分自身に課した宿題 でした。が、手つかずで最近やるやる詐欺になっていました。あかんですな。まぁ連休を使ってようやくまとめて調べる機会ができたので、自分のために整理してみました。
環境¶
もちろんDebianです。環境としてはWheezy/Sidを使ったり、Squeezeだったりなのですが、このブログでの最終的な設定自体はSqueezeです。Squeezeな理由は後述します。Wheezy/Sidな環境ではnwfilterが含まれるlibvirt-binは0.9.0、Squeezeは0.8.3です。
なぜSqueeze?¶
なんでSidじゃなく、Squeezeなのか。普段はSidを使っている方が断然多いのですが、サーバ用途ではstableも使っています。今回のきっかけはハードウェア老朽化&機能拡張のためにシステムを刷新しようとしている労働組合のサーバがあるのですが、最終的には自分じゃなく組合の他の人でも管理できるようにするためにSqueezeで構築している、というわけです 1 。
どうしてnwfilterに気づいたのか?¶
現状、nwfilterに関する日本語ドキュメントは皆無です。 @ITの「libvirt探訪(基礎編)」 でその単語と一文概要のみ載っていますが、他には今のところ見かけません。それじゃあ後述のlibvirt本家のドキュメントを読んでいて、というわけでもありません。普段、ルータやファイアウォール用途の機器だけでなく、全ホストでiptablesでフィルタリングを自作のスクリプトで/etc/init.d/経由で実行しているのです 2 が、構築中の鯖にKVM/QEMU + Libvirtを使おうと思っていたら、フィルタリングの挙動が違っているのでテーブル見てみると知らないルールが設定されている、何じゃこりゃ?と思って調べてみたらnwfilterに行きついた、という経緯です。
とりあえず、これ読んどけ¶
日本語じゃなくてもいいよ、という方はlibvirtのドキュメントを読むべきです。
調べてみた、と言いながらいきなりなんだこの展開。(わら とは言うものの、ドキュメントをちゃんと読まないとハマるポイントもあったので、気になる方は続きを読んでみてください。
そもそもnwfilterって何よ¶
「VM向け仮想ネットワークのフィルタリング機能」、以上。 3
……。これではブログにわざわざ書く意味はないので、もう少し説明を加えておきます。VM向け仮想ネットワークに対するフィルタリング機能の実態は、ebtables, iptables, ip6tablesコマンドを利用します。ただし、ebtablesは仮想ネットワークがbridgeモードでないとき機能しません。libvirtdの起動時やVMの起動時に定義(された|した)ルールが設定されるのですが、libvirtの仮想ネットワークのフォワーディングの種類 4 によって、設定されるフィルタリングルールが変わります。
今回は、仮想ネットワークはnat, isolated, routedの3種類の設定の場合の挙動を見ます。iptablesのデフォルトは何も設定していない状態とします。これらが仮想ネットワークの設定でどう変わるか見てみます 5 。なお、それぞれの仮想ネットワークは下記のものとします。
ネットワーク名
デバイス名
モード
IPアドレス/マスク長
default
virbr0
nat
192.168.122.1/24
back
virbr1
isolate virtual network
192.168.123.1/24
dmz
virbr2
routed
192.168.2.1/24
どのようにルールが設定されるのかを確認するために、下記の手順で行いました。iptablesおよびlibvirt-binともに/etc/rc2.d/S02で起動した場合です。
$ virsh --connect qemu:///system net-destroy <ネットワーク>
$ virsh --connect qemu:///system net-autostart --disable <ネットワーク>
$ sudo iptables --flush; sudo iptables -t nat --flash
$ /etc/init.d/libvirt-bin restart
isolated virtual networkの場合¶
iptables filterテーブル
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 279 packets, 21132 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- virbr1 virbr1 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr1 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr1 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
(snip)
natモードの場合¶
iptables filterテーブル
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 3788 packets, 2274K bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 state RELATED,ESTABLISHED
0 0 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
(snip)
iptables natテーブル
$ sudo iptables -t nat -L -n -v
(snip)
Chain POSTROUTING (policy ACCEPT 2 packets, 144 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24
(snip)
routeモードの場合¶
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 18 packets, 1510 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * virbr2 0.0.0.0/0 192.168.2.0/24
0 0 ACCEPT all -- virbr2 * 192.168.2.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr2 virbr2 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr2 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr2 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
はい、以上libvirtのドキュメントどおりですね。ここからはドキュメントに無いところもちょっと見てみます。
全ての仮想ネットワークを有効にした場合¶
これらのネットワークをすべて有効にした状態の場合は、次のように全てのルールが設定されます。優先順序はインタフェース名の順ですね。
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 161 packets, 12452 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 state RELATED,ESTABLISHED
0 0 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 ACCEPT all -- virbr1 virbr1 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr1 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr1 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 ACCEPT all -- * virbr2 0.0.0.0/0 192.168.2.0/24
0 0 ACCEPT all -- virbr2 * 192.168.2.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr2 virbr2 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr2 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr2 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
(snip)
$ sudo iptables -L -n -v -t nat
(snip)
Chain POSTROUTING (policy ACCEPT 4 packets, 284 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24
(snip)
既存のルールとの関係¶
ところで、私が今回nwfilterを調べるきっかけとなったように、iptables-{save,restore}や、/etc/network/if-pre-up.d/、あるいは、/etc/init.d/以下でupdate-rc.dの起動スクリプトとしてiptablesやebtablesのルールをすでに設定している場合、nwfilterで設定されるルールはどのように設定されるのでしょうか。
insservのランレベルSで、ebtablesのデフォルトと同じ起動順序にしている場合 6 での、libvirtdの起動可否(/etc/default/libvirt-binの”start_libvirtd”を(yes|no)で制御)によってfilterテーブルとnatテーブルがどのように変わるかを見てみます。-dis-libvirtがついているのがlibvirtdが起動していない場合の状態です。
$ diff -u iptables-S-S13 iptables-S-S13-dis-libvirt
--- iptables-S-S13 2011-05-07 18:32:40.036111541 +0900
+++ iptables-S-S13-dis-libvirt 2011-05-07 18:38:53.392146385 +0900
@@ -1,17 +1,5 @@
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
- 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
- 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
- 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
- 0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
- 0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
- 0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
- 0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
- 0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
- 0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
- 0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
- 0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 255.0.0.0/8 0.0.0.0/0 LOG flags 0 level 4 prefix `Spoofed source IP!'
0 0 DROP all -- * * 255.0.0.0/8 0.0.0.0/0
@@ -19,7 +7,7 @@
0 0 DROP all -- * * 127.0.0.0/8 0.0.0.0/0
0 0 LOG tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02 state NEW LOG flags 0 level 4 prefix `Stealth scan attempt?'
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02 state NEW
- 676 54501 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
+ 147 13983 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
1 60 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67 state NEW
@@ -37,19 +25,6 @@
0 0 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 state RELATED,ESTABLISHED
0 0 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
- 0 0 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 ACCEPT all -- virbr1 virbr1 0.0.0.0/0 0.0.0.0/0
- 0 0 REJECT all -- * virbr1 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 REJECT all -- virbr1 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 ACCEPT all -- * virbr2 0.0.0.0/0 192.168.2.0/24
- 0 0 ACCEPT all -- virbr2 * 192.168.2.0/24 0.0.0.0/0
- 0 0 ACCEPT all -- virbr2 virbr2 0.0.0.0/0 0.0.0.0/0
- 0 0 REJECT all -- * virbr2 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 REJECT all -- virbr2 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
- 0 0 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 state RELATED,ESTABLISHED
- 0 0 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
- 0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- virbr1 virbr1 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 state ESTABLISHED
@@ -60,10 +35,10 @@
Chain OUTPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 350 42724 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
+ 90 10720 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8
- 7 502 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 state NEW
+ 1 74 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 state NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:123 state NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:123 state NEW
これを見るとlibvirtdによってルールが先頭に挿入されているのが分かります。自分で設定しているルールからすると、先頭でないほうが都合が良いのですけどね。
natテーブルも同様です。
$ diff -u iptables-S-S13-nat iptables-S-S13-nat-dis-libvirt
--- iptables-S-S13-nat 2011-05-07 18:32:46.920112642 +0900
+++ iptables-S-S13-nat-dis-libvirt 2011-05-07 18:39:19.716126862 +0900
@@ -3,10 +3,7 @@
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
- 0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
- 0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24
- 7 502 MASQUERADE all -- * eth0 0.0.0.0/0 0.0.0.0/0
+ 1 74 MASQUERADE all -- * eth0 0.0.0.0/0 0.0.0.0/0
-Chain OUTPUT (policy ACCEPT 7 packets, 502 bytes)
+Chain OUTPUT (policy ACCEPT 1 packets, 74 bytes)
pkts bytes target prot opt in out source destination
insservでのiptablesの起動順序を変えると、設定されるルールが変わることが分かります。ランレベル2でlibvirt-binの起動と同じ順番(S02)の場合は上記と設定されるルールは変わりません。しかし、libvirt-binよりも後にiptablesを実行すると(S04)、下記のようになります。
$ diff -u iptables-S-S13 iptables-S02-04
--- iptables-S-S13 2011-05-07 18:32:40.036111541 +0900
+++ iptables-S02-04 2011-05-07 17:48:09.553671267 +0900
@@ -13,13 +13,11 @@
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
- 0 0 LOG all -- * * 255.0.0.0/8 0.0.0.0/0 LOG flags 0 level 4 prefix `Spoofed source IP!'
0 0 DROP all -- * * 255.0.0.0/8 0.0.0.0/0
0 0 LOG all -- * * 127.0.0.0/8 0.0.0.0/0 LOG flags 0 level 4 prefix `Spoofed source IP!'
0 0 DROP all -- * * 127.0.0.0/8 0.0.0.0/0
- 0 0 LOG tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02 state NEW LOG flags 0 level 4 prefix `Stealth scan attempt?'
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02 state NEW
- 676 54501 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
+ 401 33673 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
1 60 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67 state NEW
@@ -60,7 +58,7 @@
Chain OUTPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 350 42724 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
+ 220 32892 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8
7 502 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 state NEW
期待値としては、nwfitlerによって設定されるルールがflushされ、スクリプトで設定されるルールだけが表示されることでした。スクリプトで設定する際に、全てのtableをflushしているからです。しかし、なぜかnwfilterで設定されるルールではなく、自分のスクリプトの一部ルールが勝手に削除されています。なんでや…。
とりあえず、スクリプトの起動順序によって、既存ルールを残したままnwfilterによってルールが追加される、ということが分かります。細かい挙動についてはもうちょっと確認が必要ですね…。
ルールの設定方法¶
さて、nwfilterでのルールの設定方法について見てみます。ざっくり、以下の流れです。
/etc/libvirt/nwfilter/ディレクトリの下にルールファイル作る
nwfilterとしてルールファイルを定義する
VMの定義ファイルの<interface>要素の子要素として<filterref filter=’フィルター名’/>を追記する
これはやはりlibvirtのドキュメントの Example custom filter とか Second example custom filter を見ればいいでしょう。
前者のExample custom filterを実際に設定してみます。このルールは最初にclean-trafficを実行し、その後VMに対するSSH, HTTPと、VMからのICMPとDNS Queryを許可し、それ以外はすべてinbound, outboundともにドロップする、という設定です。同じ設定をnwfilter-dumpxmlで出力したのが下記です。
<filter name='moge' chain='root'>
<uuid>cb1b6bd6-0c51-530c-a218-2534042ccadc</uuid>
<filterref filter='clean-traffic'/>
<rule action='accept' direction='in' priority='500'>
<tcp dstportstart='22'/>
</rule>
<rule action='accept' direction='in' priority='500'>
<tcp dstportstart='80'/>
</rule>
<rule action='accept' direction='out' priority='500'>
<icmp/>
</rule>
<rule action='accept' direction='out' priority='500'>
<udp dstportstart='53'/>
</rule>
<rule action='drop' direction='inout' priority='500'>
<all/>
</rule>
</filter>
これをVM hogeに設定してみます。 Concepts にあるようにVMの設定ファイルの<interface>要素の子要素として、
<filterref filter='moge'/>
<parameter name='IP' value='xxx.xxx.xxx.xxx'/>
</filterref>
を設定します。<paramter>要素でVMのIPアドレスを変数IPに設定しています。この設定を使うには、この仮想NICのIPアドレスは静的に設定する必要があります。
DHCPでIPアドレスを割り当てる場合¶
ノードPCを利用している場合などは、NATモードで、かつDHCPでVMの仮想NICにIPアドレスを割り当てていることも多いとおもいます。仮想NICのMACアドレスは同じMACアドレスを使わない限り、MACアドレスごとに一意のIPアドレスが割り当てられることが多いので、一見先ほどの<parameter>要素でIPアドレスを設定しても良さそうですが、実際にやってみたところ、この設定を行ってもQEMUのDHCPサーバとの通信ができず設定できません。
そこで、NATでDHCPで設定するには以下のように行うと設定はできます。まず、前述のnwfilterのルールにはDHCPを許可するルールがないので下記を追加します。
<rule action='accept' direction='out'>
<udp srcipaddr='0.0.0.0' dstipaddr='255.255.255.255' srcportstart='68' dstportstart='67'/>
</rule>
<rule action='accept' direction='in'>
<udp srcportstart='67' dstportstart='68'/>
</rule>
そして、非常にイケてないのですがclean-trafficのルールを削除します。最終的には下記にようになります。
<filter name='moge' chain='root'>
<uuid>cb1b6bd6-0c51-530c-a218-2534042ccadc</uuid>
<rule action='accept' direction='out'>
<udp srcipaddr='0.0.0.0' dstipaddr='255.255.255.255' srcportstart='68' dstportstart='67'/>
</rule>
<rule action='accept' direction='in'>
<udp srcportstart='67' dstportstart='68'/>
</rule>
<rule action='accept' direction='in' priority='500'>
<tcp dstportstart='22'/>
</rule>
<rule action='accept' direction='in' priority='500'>
<tcp dstportstart='80'/>
</rule>
<rule action='accept' direction='out' priority='500'>
<icmp/>
</rule>
<rule action='accept' direction='out' priority='500'>
<udp dstportstart='53'/>
</rule>
<rule action='drop' direction='inout' priority='500'>
<all/>
</rule>
</filter>
なぜDHCPのときにclean-trafficを追加するとDHCPの通信ができないのか、原因は現時点では分かってない 7 ので、追加検証してみる予定です。なお、clean-trafficは、 libvirtのドキュメント にあります。
なお、libvirtのドキュメントによると、<parameter>でIPアドレスを設定しない場合は、VMで使用しているeth0のMACアドレス、IPアドレスが自動的に割り当てられるようなのですが、実際に設定せずにVMを起動すると失敗します。
$ virsh --connect qemu:///system start hoge
error: Failed to start domain hoge
error: internal error IP parameter must be given since libvirt was not compiled with IP address learning support
VM用に設定されるフィルタリングルール¶
先ほどの設定を行ったあと、VMを起動すると以下のようなルールが設定されます。
Chain INPUT (policy ACCEPT 68 packets, 5024 bytes)
pkts bytes target prot opt in out source destination
68 5024 libvirt-host-in all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr2 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
0 0 ACCEPT udp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
0 0 ACCEPT tcp -- virbr1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 libvirt-in all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 libvirt-out all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 libvirt-in-post all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * virbr2 0.0.0.0/0 192.168.2.0/24
0 0 ACCEPT all -- virbr2 * 192.168.2.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr2 virbr2 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr2 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr2 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 state RELATED,ESTABLISHED
0 0 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 ACCEPT all -- virbr1 virbr1 0.0.0.0/0 0.0.0.0/0
0 0 REJECT all -- * virbr1 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr1 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
Chain OUTPUT (policy ACCEPT 36 packets, 10016 bytes)
pkts bytes target prot opt in out source destination
Chain FI-vnet2 (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:22 state ESTABLISHED
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:80 state ESTABLISHED
0 0 RETURN icmp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW,ESTABLISHED
0 0 RETURN udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 state NEW,ESTABLISHED
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FO-vnet2 (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW,ESTABLISHED
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 state NEW,ESTABLISHED
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 state ESTABLISHED
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp spt:53 state ESTABLISHED
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain HI-vnet2 (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:22
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:80
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain libvirt-host-in (1 references)
pkts bytes target prot opt in out source destination
0 0 HI-vnet2 all -- * * 0.0.0.0/0 0.0.0.0/0 [goto] PHYSDEV match --physdev-in vnet2
Chain libvirt-in (1 references)
pkts bytes target prot opt in out source destination
0 0 FI-vnet2 all -- * * 0.0.0.0/0 0.0.0.0/0 [goto] PHYSDEV match --physdev-in vnet2
Chain libvirt-in-post (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-in vnet2
Chain libvirt-out (1 references)
pkts bytes target prot opt in out source destination
0 0 FO-vnet2 all -- * * 0.0.0.0/0 0.0.0.0/0 [goto] PHYSDEV match --physdev-out vnet2
natテーブルはルール設定していないので当然変わりません。また、現状わかっているのはVMを起動したときに下記のエラーがでます。
[29044.580539] physdev match: using --physdev-out in the OUTPUT, FORWARD and POSTROUTING chains for non-bridged traffic is not supported anymore.
メッセージを見る限り、仮想ネットワークがbridgeモードになっていないからとも見えますが、これも後日確認するつもりです。
その他、後日確認するつもりの事¶
ドキュメント読む限り、下記の事はできない感じなのですが、ソースコードも確認してみますかね。
デフォルトポリシーがACCEPTになっているが、これいずれのチェインでもDROPにしたい。0.9.0のソースコード見る限り、まだ実装されてない?
基本的にはやはりログを取りたい。これもまだ実装されてない?
nwfilterのデフォルトのルールを変更する方法
あとは前述の後日確認事項。
既存のルールとnwfilterの設定順序の仕組み
clean-trafficを設定したときにDHCPが設定できない理由
bridgeモードの挙動
今回の結論¶
まだできないこともあるようなので、現時点ではスクリプトで–flush, –delete-chainして、上書きする、というやり方が、自分の期待通りのフィルタリングルールを設定できるかなと、個人的には思います。が、スクリプトで記述しているルールを全部nwfilterに移行する方が分かりやすいかなと。ただ、VMごとのルールではなく、nwfilterのデフォルトルールを変更する方法(優先度とか)も把握が必要かなと。
ということで、本日は以上。(そのうち)続きます。
備忘録¶
以下は単純に備忘録。
filterの書き方¶
変数で取りうる値¶
IP
MAC
DHCPSERVER
VM自身のIPアドレス
VM自身のMACアドレス
許可するDHCPサーバ
Footnotes
- 1
ちなみに今のサーバはSargeです。その上で動かしているアプリも古くて拡張するよりも新しく作った方が早いので。
- 2
この方法がベストだとは思いませんが、デフォルトで設定されるルールだと、OUTPUTのデフォルトポリシーが許可されていたり、ちょいと複雑なサービス(NFS, Sambaとか)だと使いそうなポートがざっくり許可されていたりというのが嫌で、基本的には必要なサービスをtcpdumpかけて自分で細かくルールを設定しています。なので、スクリプトで制御したほうが、私にとっては管理しやすいからです。/etc/network/if-pre-up.dで制御した方が良くないか、というのもあると思いますが、initにsysvinitを使っている場合は/etc/init.d/networkingよりも先に実行してやれば良いんじゃない?と思いますがどうでしょうかね。upstartだと話変わりますが。
- 3
- 4
/etc/libvirt/qemu/networks/以下のXMLファイルのforward要素の設定。
- 5
これは上記のlibvirtのドキュメントに載っているので、必要なければ読み飛ばしてくらはい。
- 6
ifupdown→iptables,ebtables→networkingの順。
- 7
普通に考えればclean-trafficで設定されるDROPルールの所為だと思いますが。
- 8
先に評価される
- 9
-m state –stateですな。