[Python] python-iptablesに入門してみた

python-iptablesとはiptablesをPythonで操作するためのモジュールです。

# 環境
- Ubuntu 18.04
- Python 3.6
- python-iptables 0.13.0

# ソースコード
ソースコードは[Manipulate iptables using python-iptables](https://gist.github.com/homoluctus/60da8b945029ee67401c75ccd76e8cc4)にあります。

```python:demo.py
     1	import iptc
     2	import ipaddress
     3	
     4	
     5	def format_address(address):
     6	    """
     7	    Support IPv4 only yet.
     8	    Return IPv4 with prefix length (e.g. 192.168.0.1/32)
     9	    """
    10	
    11	    if isinstance(address, (str, ipaddress.IPv4Address)):
    12	        try:
    13	            address = ipaddress.ip_network(address)
    14	        except ValueError:
    15	            raise
    16	    elif isinstance(address, ipaddress.IPv4Network):
    17	        pass
    18	    else:
    19	        raise TypeError
    20	
    21	    return address.with_prefixlen
    22	
    23	
    24	def display_matches(matches=[]):
    25	    print("Match:", end=' ')
    26	    for match in matches:
    27	        for key, value in match.get_all_parameters().items():
    28	            if isinstance(value, list):
    29	                value = ', '.join(value)
    30	
    31	            print("[{}] {}".format(key, value))
    32	
    33	
    34	def display_rule(rule):
    35	    print('+'*40)
    36	    print("Target:", rule.target.name)
    37	    print("Protocol:", rule.protocol)
    38	    print("Source address:", format_address(rule.src))
    39	    print("Destination address:", format_address(rule.dst))
    40	    display_matches(rule.matches)
    41	    print('+'*40)
    42	
    43	
    44	def delete_rule(chain, rule):
    45	    chain.delete_rule(rule)
    46	
    47	if __name__ == '__main__':
    48	    # Create rule
    49	    rule1 = iptc.Rule()
    50	    rule1.out_interface = 'eth0'
    51	    rule1.dst = "192.168.0.1"
    52	    rule1.protocol = 'icmp'
    53	    match_for_rule1 = rule1.create_match('icmp')
    54	    match_for_rule1.icmp_type = 'echo-request'
    55	    rule1.create_target('DROP')
    56	
    57	    rule2 = iptc.Rule()
    58	    rule2.out_interface = 'eth0'
    59	    rule2.dst = "192.168.0.2"
    60	    rule2.protocol = 'icmp'
    61	    match_for_rule2 = rule2.create_match('icmp')
    62	    match_for_rule2.icmp_type = 'echo-reply'
    63	    rule2.create_target('DROP')
    64	
    65	    # Create chain
    66	    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'OUTPUT')
    67	    # Apply rule to chain
    68	    chain.insert_rule(rule1)
    69	    chain.insert_rule(rule2, position=1)
    70	
    71	    for rule in chain.rules:
    72	        try:
    73	            display_rule(rule)
    74	        except:
    75	            from traceback import print_exc
    76	            print_exc()
```

## 5~21行目
モジュールから返される送信元/宛先アドレスは192.168.0.0/255.255.255.0のようなフォーマットなので、これを192.168.0.0/24のようにネットマスクをプレフィックス長に変換するための関数

## 24~31行目
iptablesのmatch extensionsを表示するための関数
match.get_all_parameters()で設定されている全ての値を取得

## 34~41行目
設定したルールを表示するための関数

## 44~45行目
特定のchainに設定されているルールを削除するための関数

## 49~63行目
ルールの作成

## 66行目
FilterテーブルのOUTPUT Chainの作成

## 68~69行目
作成したルールをchainに適用
69行目の第2引数の`position`はchainの何番目にルールを挿入するか指定

## 71行目
chain.rulesによって、chainに設定されている全てのルールを取得
返り値はRuleオブジェクトであり、Ruleオブジェクトには`__str__`や`__repr__`が書かれていないので素のオブジェクト情報が返されます
つまり、詳細なルール情報を表示したい場合は34~41行目にあるように自作する必要があります

# 検証

```
$ python3 demo.py
++++++++++++++++++++++++++++++++++++++++
Target: DROP
Protocol: icmp
Source address: 0.0.0.0/0
Destination address: 192.168.0.1/32
Match: [icmp-type] 8
++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++
Target: DROP
Protocol: icmp
Source address: 0.0.0.0/0
Destination address: 192.168.0.2/32
Match: [icmp-type] 0
++++++++++++++++++++++++++++++++++++++++
```

```
$ iptables -L OUTPUT
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       icmp --  anywhere             192.168.0.1          icmp echo-request
DROP       icmp --  anywhere             192.168.0.2          icmp echo-reply
```

ちゃんとiptablesにルールがコミットされました。

# 参考
- [ldx/python-iptables: Python bindings for iptables](https://github.com/ldx/python-iptables)
- [python-iptables/ip4tc.py at master · ldx/python-iptables](https://github.com/ldx/python-iptables/blob/master/iptc/ip4tc.py)