[firewalld]/etc/firewalld/direct.xmlにおける同一priorityの挙動について

# 前提
* Vagrant 1.7.4
  * CentOS Linux release 7.1.1503 (Core)
  * getenforce => Disabled
  * sshd は 10.10.10.10/24 にてLISTEN
  * firewalld は有効
    * 以下が有効な状態(いわゆる「デフォルト」)
      * zone は public (firewall-cmd --get-active-zones)
      * /usr/lib/firewalld/services/ssh.xml 
      * /usr/lib/firewalld/services/dhcpv6-client.xml
      * 上記設定は以下検証内容で上書きされる認識

# 諸注意
* 以下の手順に従い検証を進めますと **vagrant destroy を余儀なくされます** (それ以外の回避方法を知らない)。
* もちろん実機検証の場合には端末接続がないと立ちいかなくなります。
* KVMゲスト機で検証するとホスト機からの virsh console ${geust} があるので気楽かもしれません。

# 検証内容
* Direct Optionsのpriority値が一致する場合の評価順の確認

# /etc/firewalld/direct.xml を手動で作成した
* priority値はすべて 1 である

```xml:/etc/firewalld/direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -i lo -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -o lo -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -s 10.10.10.0/24 -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -d 10.10.10.0/24 -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'> -m limit --limit 1/sec -j LOG --log-prefix  '[IPTABLES INPUT]  : '</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'> -j DROP</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES OUTPUT] : '</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -j DROP</rule>
</direct>
```

# firewalld を再起動する
* この場合は firewall-cmd --reload でもよい

```bash:command(root)
# systemctl restart firewalld.service
```

# 設定状態を確認する
* この状態ではSSH接続は切断されない

## firewall-cmd
```bash:command(root)
# firewall-cmd --direct --get-all-rules
ipv4 filter INPUT 1 -i lo -j ACCEPT
ipv4 filter INPUT 1 -s 10.10.10.0/24 -j ACCEPT
ipv4 filter INPUT 1 -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES INPUT]  : '
ipv4 filter INPUT 1 -j DROP
ipv4 filter OUTPUT 1 -o lo -j ACCEPT
ipv4 filter OUTPUT 1 -d 10.10.10.0/24 -j ACCEPT
ipv4 filter OUTPUT 1 -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES OUTPUT] : '
ipv4 filter OUTPUT 1 -j DROP
```
## iptables
```bash:command(root)
# iptables -nL INPUT_direct
Chain INPUT_direct (1 references)
target     prot opt source               destination
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  10.10.10.0/24        0.0.0.0/0
LOG        all  --  0.0.0.0/0            0.0.0.0/0            limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[IPTABLES INPUT]  : "
DROP       all  --  0.0.0.0/0            0.0.0.0/0
```

```bash:command(root)
# iptables -nL OUTPUT_direct
Chain OUTPUT_direct (1 references)
target     prot opt source               destination
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  0.0.0.0/0            10.10.10.0/24
LOG        all  --  0.0.0.0/0            0.0.0.0/0            limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[IPTABLES OUTPUT] : "
DROP       all  --  0.0.0.0/0            0.0.0.0/0
```

# /etc/firewalld/direct.xml を手動修正しDROP行とACCEPT行を入れ替える

```xml:/etc/firewalld/direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -i lo -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -o lo -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'> -m limit --limit 1/sec -j LOG --log-prefix  '[IPTABLES INPUT]  : '</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'> -j DROP</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES OUTPUT] : '</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -j DROP</rule>  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -s 10.10.10.0/24 -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -d 10.10.10.0/24 -j ACCEPT</rule>
</direct>
```

# firewalld を再起動する
* 以下を実行するとSSH接続が途絶える
* <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'> -j DROP</rule> が効いている
* この場合も firewall-cmd --reload でも問題ない
  * が direct.xml の手動書き換え/特に行削除や入れ替えに関しては再起動がよいと思う(不要な設定が残り続ける)
* 実機確認/virsh console ${guest}が可能な方は以下も行うとしっくりくるやもしれません(vagrantで確認できませんでした)
  * /var/log/messagesのDROPログを確認する
  * firewall-cmd --direct --get-all-rules を確認する
  * iptables -nL {IN,OUT}PUT_direct を確認する
  

```bash:command(root)
# systemctl restart firewalld.service
```

# 結論1
* /etc/firewalld/direct.xml は同一priorityの場合、先頭行から順に評価されることがわかった

# Vagrantを再構築する
* してください

```c:command(client-{PC,Mac})
vagrant destroy
vagrant up
```

# /etc/firewalld/direct.xml 手動作成した(ただし **DROP行のpriority値を2** とした)

```xml:/etc/firewalld/direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -i lo -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -o lo -j ACCEPT</rule>
  <rule priority='2' table='filter' ipv='ipv4' chain='INPUT'> -m limit --limit 1/sec -j LOG --log-prefix  '[IPTABLES INPUT]  : '</rule>
  <rule priority='2' table='filter' ipv='ipv4' chain='INPUT'> -j DROP</rule>
  <rule priority='2' table='filter' ipv='ipv4' chain='OUTPUT'> -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES OUTPUT] : '</rule>
  <rule priority='2' table='filter' ipv='ipv4' chain='OUTPUT'> -j DROP</rule>  <rule priority='1' table='filter' ipv='ipv4' chain='INPUT'>  -s 10.10.10.0/24 -j ACCEPT</rule>
  <rule priority='1' table='filter' ipv='ipv4' chain='OUTPUT'> -d 10.10.10.0/24 -j ACCEPT</rule>
</direct>
```

# firewalld を再起動する
* この場合も firewall-cmd --reload でもよい
* priority値の書き換えによりSSH接続が途絶えない

```bash:command(root)
# systemctl restart firewalld.service
```

# 設定状態を確認する

## firewall-cmd
```bash:command(root)
# firewall-cmd --direct --get-all-rules
ipv4 filter INPUT 1 -i lo -j ACCEPT
ipv4 filter INPUT 2 -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES INPUT]  : '
ipv4 filter INPUT 2 -j DROP
ipv4 filter INPUT 1 -s 10.10.10.0/24 -j ACCEPT
ipv4 filter OUTPUT 1 -o lo -j ACCEPT
ipv4 filter OUTPUT 2 -m limit --limit 1/sec -j LOG --log-prefix '[IPTABLES OUTPUT] : '
ipv4 filter OUTPUT 2 -j DROP
ipv4 filter OUTPUT 1 -d 10.10.10.0/24 -j ACCEPT
```
## iptables
```bash:command(root)
# iptables -nL INPUT_direct
Chain INPUT_direct (1 references)
target     prot opt source               destination
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  10.10.10.0/24        0.0.0.0/0
LOG        all  --  0.0.0.0/0            0.0.0.0/0            limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[IPTABLES INPUT]  : "
DROP       all  --  0.0.0.0/0            0.0.0.0/0
```

```bash:command(root)
# iptables -nL OUTPUT_direct
Chain OUTPUT_direct (1 references)
target     prot opt source               destination
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  0.0.0.0/0            10.10.10.0/24
LOG        all  --  0.0.0.0/0            0.0.0.0/0            limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[IPTABLES OUTPUT] : "
DROP       all  --  0.0.0.0/0            0.0.0.0/0
```

# 結論2
* /etc/firewalld/direct.xml はpriority値どおりに挙動することがわかった

# まとめ
* Direct Options利用時にはpriority値は重要
* 稼働当初は実績のあるiptables書き換えで同一priority値でも問題はなく稼働しそうだが、後の修正で障害を引き起こす可能性がある

# 個人的な今後の課題
* firewalld を使っていきたい
* direct.xml をpriority値別に別ファイルで管理する方法がないか模索したい
  * ただしバッドノウハウなのではないかという懸念がある
  * 実験的/有用な情報があれば @ngsw までお知らせいただけると幸いに存じます