python-iptablesを使ってみた

pythonでiptablesを操作する便利モジュール無いかなと探していたら`python-iptables`を見つけたので触ってみました。

# python-iptables

[https://github.com/ldx/python-iptables](https://github.com/ldx/python-iptables)

## ドキュメント

[http://ldx.github.io/python-iptables/index.html](http://ldx.github.io/python-iptables/index.html)

# 環境

| 項目   | 内容 |
|:-------|:-----|
| CentOS | 7.4  |
| python | 3.4  |

# インストール

## 必要なパッケージをインストール

```bash
[root@localhost ~]# yum -y install epel-release
[root@localhost ~]# yum -y install python34 python34-devel python34-pip gcc
```

## python-iptablesインストール

```bash
[root@localhost ~]# pip3 install python-iptables
```

# つかってみる

例としてSSHの許可設定を入れてみます。

## モジュールを読み込む

```
In [1]: import iptc
```

## INルールを作る

```
In [2]: in_rule = iptc.Rule()
In [3]: in_rule.src = '192.168.0.0/24'
In [4]: in_rule.dst = '192.168.0.231'
In [6]: in_rule.protocol = 'tcp'
In [24]: in_rule.target = iptc.Target(in_rule, 'ACCEPT')
```

## OUTルールを作る

```
In [7]: out_rule = iptc.Rule()
In [8]: out_rule.src = '192.168.0.231'
In [9]: out_rule.dst = '192.168.0.0/24'
In [10]: out_rule.protocol = 'tcp'
In [25]: out_rule.target = iptc.Target(out_rule, 'ACCEPT')
```

## ルールに詳細な条件を追加する

```
In [11]: in_rule_match = iptc.Match(in_rule, 'tcp')
In [15]: in_rule_match.dport = '22'
In [16]: in_rule.add_match(in_rule_match)
In [17]: out_rule_match = iptc.Match(out_rule, 'tcp')
In [19]: out_rule_match.sport = '22'
In [20]: out_rule.add_match(out_rule_match)
```

または`iptc.Rule()`のメソッドに`create_match`があるので、そちらでも設定が可能です。

```
In [4]: in_rule.create_match('tcp').dport = '22'
In [7]: out_rule.create_match('tcp').sport = '22'
```

## チェインへ挿入する

```
In [21]: input_chain = iptc.Chain(iptc.Table('filter'), 'INPUT')
In [26]: input_chain.insert_rule(in_rule)
In [29]: output_chain = iptc.Chain(iptc.Table('filter'), 'OUTPUT')
In [30]: output_chain.insert_rule(out_rule)
```

## チェインポリシーを設定する

`INPUT`と`OUTPUT`のルールは入ったことが確認できます。

```bash
[root@localhost ~]# iptables -nL -v
Chain INPUT (policy ACCEPT 103 packets, 6640 bytes)
 pkts bytes target     prot opt in     out     source               destination
 1035 73905 ACCEPT     tcp  --  *      *       192.168.0.0/24       192.168.0.231        tcp dpt:22

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 289 packets, 41652 bytes)
 pkts bytes target     prot opt in     out     source               destination
    5   832 ACCEPT     tcp  --  *      *       192.168.0.231        192.168.0.0/24       tcp spt:22
```

ただし、チェインのポリシーが`ACCEPT`のままなので`DROP`に変更します。

```
In [39]: input_chain.set_policy('DROP')
In [40]: output_chain.set_policy('DROP')
```

こすると`INPUT` `OUTPUT`共に`DROP`に設定されます。

```
[root@localhost ~]# iptables -nL -v
Chain INPUT (policy DROP 42 packets, 2646 bytes)
 pkts bytes target     prot opt in     out     source               destination
 1293 91625 ACCEPT     tcp  --  *      *       192.168.0.0/24       192.168.0.231        tcp dpt:22

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy DROP 1 packets, 76 bytes)
 pkts bytes target     prot opt in     out     source               destination
  145 23312 ACCEPT     tcp  --  *      *       192.168.0.231        192.168.0.0/24       tcp spt:22
```

# YAMLでiptablesを設定してみる

YAMLでルールが管理できると便利かなと思ってpython-iptablesの使い方お勉強のために簡易ツールを作ってみました。

[https://github.com/sky-joker/iptables-yml](https://github.com/sky-joker/iptables-yml)

## 使い方

### YAMLファイル

```yaml:example.yaml
Loopback Interface Rule:
  INPUT:
    in_interface: lo
    target: ACCEPT
  OUTPUT:
    out_interface: lo
    target: ACCEPT
SSH Rule:
  INPUT:
    dst: 192.168.0.231
    src: 192.168.0.0/24
    target: ACCEPT
    protocol_options:
      protocol: tcp
      dport: 22
  OUTPUT:
    dst: 192.168.0.0/24
    src: 192.168.0.231
    target: ACCEPT
    protocol_options:
      protocol: tcp
      sport: 22
```

### 設定を入れてみる

```bash
[root@localhost ~]# ./iptables-yml.py init -f example.yaml
```

### 結果

```bash
[root@localhost ~]# iptables -nL -v
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0            /* Loopback Interface Rule */
   25  1720 ACCEPT     tcp  --  *      *       192.168.0.0/24       192.168.0.231        tcp dpt:22 /* SSH Rule */

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0            /* Loopback Interface Rule */
   14  2304 ACCEPT     tcp  --  *      *       192.168.0.231        192.168.0.0/24       tcp spt:22 /* SSH Rule */
```

# YAMLで設定を標準化

よくよく調べてみれば、Ansibleのiptablesモジュールを使えば使えばYAMLで標準化ができそうです。
[iptablesモジュール](http://docs.ansible.com/ansible/latest/iptables_module.html)
こっも触ってみようと思います:grinning:

python-iptablesは、既存の設定を抜き出してまとめるのに使えそうかも:exclamation:
ちなみに既存の設定を抜き出す場合は以下のようにすればできます。

```
In [4]: table = iptc.Table('filter')
In [46]: for chain in table.chains:
    ...:     print(chain.name)
    ...:     for rule in chain.rules:
    ...:         print('------')
    ...:         print('comment: ' + str(list(filter(lambda x: hasattr(x, 'comment') and x.comment , rule.matches))[0].comment))
    ...:         print('dst: ' + rule.dst)
    ...:         print('src: ' + rule.src)
    ...:     print("")
    ...:
INPUT
------
comment: Loopback Interface Rule
dst: 0.0.0.0/0.0.0.0
src: 0.0.0.0/0.0.0.0
------
comment: SSH Rule
dst: 192.168.0.231/255.255.255.255
src: 192.168.0.0/255.255.255.0

FORWARD

OUTPUT
------
comment: Loopback Interface Rule
dst: 0.0.0.0/0.0.0.0
src: 0.0.0.0/0.0.0.0
------
comment: SSH Rule
dst: 192.168.0.0/255.255.255.0
src: 192.168.0.231/255.255.255.255
```