Open vSwitchのconntrack連携を試してみた(その2)

# これ・・全くプログラミングに関係ないネタなんですが、

Qiitaに投稿していいんでしたっけ・・・？

と不安になりつつも、強い心で第2回です。 ＠Qiita初心者

前回はnf_conntrackインテグレーションに対応したOpen vSwitchのインストールから起動までを紹介しました。

[Open vSwitchのconntrack連携を試してみた(その1)](http://qiita.com/jh1vxw/items/0d0e370fb1b374ab0c93)

今回は、最も簡単な構成で**ステートフルインスペクション**の設定をして通信確認をしてみます。

# テスト構成

下図のように、物理ホスト上にVMを2個起動し、Open vSwitchにて接続します。ゲストOSとしてはCentOS6をインストールしました。

![a.png](https://qiita-image-store.s3.amazonaws.com/0/108990/6d9eaa2e-3dd6-d2d6-d277-764424436d5b.png)

ここで、

- VM1からVM2向けにイニシエートされた通信の戻りパケットは許可し、それ以外は拒否する

というポリシーをOpen vSwitchのフローエントリで実装してみます。

# Step1. VMの起動

CentOSをインストールしたディスクイメージを2個用意し、普通にqemuを2個起動します。

■ VM1

```
/usr/libexec/qemu-kvm -smp 1 -m 1024 -vnc :1 -k ja -enable-kvm -daemonize \
-drive file=centos6-1.qcow2,if=virtio,index=0,media=disk,cache=none,aio=native \
-netdev type=tap,vhost=on,vnet_hdr=off,id=tap1,ifname=tap1,script=no,downscript=no \
-device virtio-net-pci,netdev=tap1,mac=52:54:00:60:4d:6e \
-netdev type=tap,vhost=on,vnet_hdr=off,id=tap2,ifname=tap2,script=no,downscript=no \
-device virtio-net-pci,netdev=tap2,mac=52:54:00:60:84:ff \
```

■ VM2

```
/usr/libexec/qemu-kvm -smp 1 -m 1024 -vnc :2 -k ja -enable-kvm -daemonize \
-drive file=centos6-2.qcow2,if=virtio,index=0,media=disk,cache=none,aio=native \
-netdev type=tap,vhost=on,vnet_hdr=off,id=tap3,ifname=tap3,script=no,downscript=no \
-device virtio-net-pci,netdev=tap3,mac=52:54:00:ba:61:f3 \
-netdev type=tap,vhost=on,vnet_hdr=off,id=tap4,ifname=tap4,script=no,downscript=no \
-device virtio-net-pci,netdev=tap4,mac=52:54:00:99:90:78 \
```

それぞれ、2個目のNIC(tap2とtap4)をOpen vSwitch通信テスト用に利用します。
先頭のNIC(tap1とtap3)はsshログイン用です。

# Step2. 仮想NICをOpen vSwitchに接続

Open vSwitch(br0)にtap2とtap4を普通に接続します。

```
ovs-vsctl add-port br0 tap2
ovs-vsctl add-port br0 tap4
```

正常に接続できると、以下のようになると思います。今回物理NIC(eth2)は使いません。

```
[root@sl67-r3 ~]# ovs-ofctl show br0
OFPT_FEATURES_REPLY (xid=0x2): dpid:000090e2ba230f70
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
 1(eth2): addr:90:e2:ba:23:0f:70
     config:     0
     state:      0
     current:    10GB-FD
     advertised: 10GB-FD FIBER
     supported:  10GB-FD FIBER
     speed: 10000 Mbps now, 10000 Mbps max
 2(tap2): addr:ae:b4:ef:2b:24:6b
     config:     0
     state:      0
     current:    10MB-FD COPPER
     speed: 10 Mbps now, 0 Mbps max
 3(tap4): addr:02:c6:fe:6c:2f:73
     config:     0
     state:      0
     current:    10MB-FD COPPER
     speed: 10 Mbps now, 0 Mbps max
 LOCAL(br0): addr:90:e2:ba:23:0f:70
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
```

tap2(VM1)とtap4(VM2)は、それぞれofport=2とofport=3が割り当てられました。次節のフローエントリ設定では、このofport番号を使用します。

この状態で、VM1とVM2間で普通に通信できることを確認しておきます。

```
[root@centos6-1 ~]# ip addr show dev eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:60:84:ff brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global eth1
    inet6 fe80::5054:ff:fe60:84ff/64 scope link
       valid_lft forever preferred_lft forever

[root@centos6-1 ~]# ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.956 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.518 ms
```

ここまではいつも通り、問題なっしですね。

# Step3. conntrack連携するフローエントリを設定

以下のようなフローエントリをovs-ofctlコマンドで設定します。

```
# cat a.flow
priority=100,ip,actions=ct(table=1)
priority=10,arp,actions=normal
priority=1,actions=drop

table=1,in_port=2,ct_state=+new,actions=ct(commit),normal
table=1,in_port=2,ct_state=+est,actions=normal
table=1,in_port=3,ct_state=+new,actions=drop
table=1,in_port=3,ct_state=+est,actions=normal

# ovs-ofctl add-flows br0 a.flow
```

ct_stateやactions=ct(xxxx)あたりがconntrackテーブルに登録したり、参照したりする構文です。

正しく設定できると、以下のようになると思います。

```
[root@sl67-r3 ~]# ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=46.236s, table=0, n_packets=0, n_bytes=0, idle_age=46, priority=100,ip actions=ct(table=1)
 cookie=0x0, duration=46.236s, table=0, n_packets=0, n_bytes=0, idle_age=46, priority=10,arp actions=NORMAL
 cookie=0x0, duration=46.236s, table=0, n_packets=0, n_bytes=0, idle_age=46, priority=1 actions=drop
 cookie=0x0, duration=46.236s, table=1, n_packets=0, n_bytes=0, idle_age=46, ct_state=+new,in_port=2 actions=ct(commit),NORMAL
 cookie=0x0, duration=46.235s, table=1, n_packets=0, n_bytes=0, idle_age=46, ct_state=+new,in_port=3 actions=drop
 cookie=0x0, duration=46.236s, table=1, n_packets=0, n_bytes=0, idle_age=46, ct_state=+est,in_port=2 actions=NORMAL
 cookie=0x0, duration=46.235s, table=1, n_packets=0, n_bytes=0, idle_age=46, ct_state=+est,in_port=3 actions=NORMAL
```

# Step4. 通信テスト

まずVM2側からVM1に対して通信**できない**ことを確認します。icmpやtcpやudpパケットを飛ばしてみます。

```
[root@centos6-2 ~]# ping 192.168.0.1
[root@centos6-2 ~]# telnet 192.168.0.1
[root@centos6-2 ~]# dig 192.168.0.1
```

カウンタを確認すると、以下のように**dropエントリ**にマッチしていることがわかります。

```
[root@sl67-r3 ~]# ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=282.937s, table=0, n_packets=8, n_bytes=736, idle_age=9, priority=100,ip actions=ct(table=1)
 cookie=0x0, duration=282.937s, table=0, n_packets=2, n_bytes=84, idle_age=13, priority=10,arp actions=NORMAL
 cookie=0x0, duration=282.937s, table=0, n_packets=0, n_bytes=0, idle_age=282, priority=1 actions=drop
 cookie=0x0, duration=282.937s, table=1, n_packets=0, n_bytes=0, idle_age=282, ct_state=+new,in_port=2 actions=ct(commit),NORMAL
 cookie=0x0, duration=282.936s, table=1, n_packets=8, n_bytes=736, idle_age=9, ct_state=+new,in_port=3 actions=drop
 cookie=0x0, duration=282.937s, table=1, n_packets=0, n_bytes=0, idle_age=282, ct_state=+est,in_port=2 actions=NORMAL
 cookie=0x0, duration=282.936s, table=1, n_packets=0, n_bytes=0, idle_age=282, ct_state=+est,in_port=3 actions=NORMAL
```

続いて、VM1からVM2に対して通信を行い、**正常に接続できる**ことを確認します。

```
[root@centos6-1 ~]# ping 192.168.0.2
[root@centos6-1 ~]# telnet 192.168.0.2 22
[root@centos6-1 ~]# dig @192.168.0.2
```

カウンタを確認すると、以下のようにconnection trackingのエントリにマッチしていることがわかります。

```
[root@sl67-r3 ~]# ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=221.654s, table=0, n_packets=40, n_bytes=3154, idle_age=63, priority=100,ip actions=ct(table=1)
 cookie=0x0, duration=221.654s, table=0, n_packets=10, n_bytes=420, idle_age=63, priority=10,arp actions=NORMAL
 cookie=0x0, duration=221.653s, table=0, n_packets=0, n_bytes=0, idle_age=221, priority=1 actions=drop
 cookie=0x0, duration=221.653s, table=1, n_packets=14, n_bytes=1058, idle_age=63, ct_state=+new,in_port=2 actions=ct(commit),NORMAL
 cookie=0x0, duration=221.653s, table=1, n_packets=0, n_bytes=0, idle_age=221, ct_state=+new,in_port=3 actions=drop
 cookie=0x0, duration=221.653s, table=1, n_packets=8, n_bytes=629, idle_age=85, ct_state=+est,in_port=2 actions=NORMAL
 cookie=0x0, duration=221.653s, table=1, n_packets=12, n_bytes=945, idle_age=85, ct_state=+est,in_port=3 actions=NORMAL
```

nf_conntrackのテーブルにも登録されています。

```
[root@sl67-r3 ~]# conntrack -L -s 192.168.0.1
udp      17 28 src=192.168.0.1 dst=192.168.0.2 sport=38118 dport=53 [UNREPLIED] src=192.168.0.2 dst=192.168.0.1 sport=53 dport=38118 mark=0 use=1
tcp      6 105 TIME_WAIT src=192.168.0.1 dst=192.168.0.2 sport=48265 dport=22 src=192.168.0.2 dst=192.168.0.1 sport=22 dport=48265 [ASSURED] mark=0 use=1
icmp     1 4 src=192.168.0.1 dst=192.168.0.2 type=8 code=0 id=52997 [UNREPLIED] src=192.168.0.2 dst=192.168.0.1 type=0 code=0 id=52997 mark=0 use=1
conntrack v0.9.13 (conntrack-tools): 3 flow entries have been shown.
```

無事に動いてますね！

# 設定の意味するところ

今回以下の設定を行いましたが、実はこれ、[ovs-ofctlコマンドのマニュアル](http://openvswitch.org/support/dist-docs/ovs-ofctl.8.txt)に載っているサンプルを簡素化したものです。

```
     1  priority=100,ip,actions=ct(table=1)
     2  priority=10,arp,actions=normal
     3  priority=1,actions=drop
     4
     5  table=1,in_port=2,ct_state=+new,actions=ct(commit),normal
     6  table=1,in_port=2,ct_state=+est,actions=normal
     7  table=1,in_port=3,ct_state=+new,actions=drop
     8  table=1,in_port=3,ct_state=+est,actions=normal
```

- 1行目: IPv4パケットをconntrackインテグレーション機能の処理に回します。実際、5行目以降のtable1のルールが評価されます。
- 2行目: 1行目にマッチしなかったもののうち、ARPパケットはそもそもconntrackできませんので、ここで無条件に許可しています。
- 3行目: その他のパケット(IPv6とか)は破棄します。
- 5行目: VM1が発した新規セッションをconntrackに登録し、パケット転送します。
- 6行目: VM1が発したconntrackに載っているセッションにマッチするパケットを許可します。
- 7行目: VM2が発した新規セッションは破棄します。
- 8行目: VM2が発したconntrackに載っているセッション(5行目で登録された)にマッチするパケットを許可します。

# 次回へ続く

今回は最も簡単な構成で、nf_conntrack連携を利用したステートフルファイアウォールを実装してみました。

次回は[弊社クラウドサービス](http://cloud.sakura.ad.jp/)における、仮想スイッチのお客様サーバ収容構成を想定したポリシーの実装方法を検討してみます。

→ [Open vSwitchのconntrack連携を試してみた(その3)](http://qiita.com/jh1vxw/items/f5881978242aaca3ca35)