ldx / python-iptables

Python bindings for iptables
730 stars 183 forks source link

If you create "state" rules for IP v6, then you cannot create them for IP v4. #112

Closed plwhite closed 9 years ago

plwhite commented 9 years ago

I just spent a few hours figuring out how to filter on state using python-iptables when there are both IPv6 and IPv4 rules. Since this was painful, it seems worth raising as an issue in case anybody hits the same problem, perhaps so a warning can go in the docs.

Some trivial sample code below showing the problem. The underlying issue seems to be that, although I can use state matching for either IPv4 or IPv6, I cannot do both in the same program. The resolution was to use match type "conntrack" and "ctstate" for the IPv6 rules (only) (i.e. rule.create_match("conntrack") followed by match.ctstate=["INVALID"]).

import iptc

# Change this value from True to False to make the failure go away
name = "plw"

# Create both IP v4 and IP v6 chains, both empty
table = iptc.Table6(iptc.Table6.FILTER)
if not table.is_chain(name):
    chain = table.create_chain(name)
else:
    chain = iptc.Chain(table,name)
chain.flush()

table = iptc.Table(iptc.Table.FILTER)
if not table.is_chain(name):
    chain = table.create_chain(name)
else:
    chain = iptc.Chain(table,name)
chain.flush()

# Add a rule to both.
table = iptc.Table6(iptc.Table6.FILTER)
chain = iptc.Chain(table,name)
rule  = iptc.Rule6()
rule.create_target("DROP")
match = rule.create_match("state")
match.state = ["INVALID"]
# It turned out not to matter if I inserted the rule or not
#chain.insert_rule(rule,0)

table = iptc.Table(iptc.Table.FILTER)
chain = iptc.Chain(table,name)
rule  = iptc.Rule()
rule.create_target("DROP")
match = rule.create_match("state")
match.state = "INVALID"
#chain.insert_rule(rule,0)
ldx commented 9 years ago

Good recap!

One way to work around the problem is something like this:

iptc.Rule().create_match("conntrack")
iptc.Rule6().create_match("conntrack")

somewhere at the beginning of your script. This will make sure conntrack is loaded, and then python-iptables can find it via its state alias.

The real solution would be to do it like iptables does, loading all extensions at start-up time.

ldx commented 9 years ago

I created this:

https://github.com/ldx/python-iptables/issues/113

to track this issue. Thanks!

ApoXalvation commented 3 years ago

Hello, It looks like this issue still occurs for external kernel modules like jool/jool_siit

>>> import iptc
>>> iptc.Rule6().create_target("JOOL_SIIT")
<iptc.ip4tc.Target object at 0x7f106a25ab80>
>>> iptc.Rule().create_target("JOOL_SIIT")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/root/.local/share/virtualenvs/qualia-gdQNZkWv/lib/python3.8/site-packages/iptc/ip4tc.py", line 1002, in create_target
    target = Target(self, name=name, revision=revision, goto=goto)
  File "/root/.local/share/virtualenvs/qualia-gdQNZkWv/lib/python3.8/site-packages/iptc/ip4tc.py", line 728, in __init__
    raise XTablesError("can't find target %s" % (name))
iptc.errors.XTablesError: can't find target JOOL_SIIT

use-case:

import iptc

rule={'target': {'JOOL_SIIT': {'instance': 'test1'}}}

def _iptables_easy_insert(table: str, chain: str, rule: dict, pos: int = 0, ipv6: bool = False):
    if ipv6:
        iptc_table = iptc.easy.Table6(table, autocommit=False)
    else:
        iptc_table = iptc.easy.Table(table, autocommit=False)
    iptc_chain = iptc.Chain(iptc_table, chain)
    iptc_chain.insert_rule(iptc.easy.encode_iptc_rule(rule, ipv6), pos)
    iptc_table.commit()
    iptc_table.refresh()

_iptables_easy_insert("mangle", "PREROUTING", rule, 0, False)
_iptables_easy_insert("mangle", "PREROUTING", rule, 0, True)

jool/jool_siit modules: https://www.jool.mx/en/index.html Python 3.8.5 Ubuntu 20.04.1 LTS (5.4.0-1028-gcp) iptables v1.8.4 (legacy)

ldx commented 3 years ago

@ApoXalvation I tried to reproduce it after installing Jool from the deb packages, but the process gets stuck in kernel, and only reboot helps:

 8607 pts/10   S+     0:00  |   \_ sudo /home/vilmos/.virtualenvs/iptc/bin/python issue112.py
 8608 pts/10   D+     0:02  |       \_ /home/vilmos/.virtualenvs/iptc/bin/python issue112.py

Code:

import iptc
t6 = iptc.Rule6().create_target("JOOL_SIIT")
t = iptc.Rule().create_target("JOOL_SIIT")
ApoXalvation commented 3 years ago

@ldx After package installation sudo apt install jool-dkms jool-tools ensure that You have enabled kernel module /sbin/modprobe jool_siit

To check if jool_siit is installed properly You can issue few commands: jool_siit instance display should give You empty table

+--------------------+-----------------+-----------+
|          Namespace |            Name | Framework |
+--------------------+-----------------+-----------+
+--------------------+-----------------+-----------+

jool_siit instance add "vlan3400" --iptables --pool6 fd8f:bda5:3400::/96 This create new siit instance (this should not break anything, and disappear after reboot), use above command to check if new jool_siit instance created successfully.

ip6tables -t mangle -A PREROUTING -j JOOL_SIIT --instance "vlan3400" and iptables -t mangle -A PREROUTING -j JOOL_SIIT --instance "vlan3400" should create related iptables rules which We are trying to create using iptc module.

ldx commented 3 years ago

Thanks, that helped!

This code works for me:

import iptc

rule6 = iptc.Rule6()
target6 = rule6.create_target("JOOL_SIIT")
print(rule6.nfproto, target6.name)

rule = iptc.Rule()
target = rule.create_target("JOOL_SIIT")
print(rule.nfproto, target.name)

Output:

10 JOOL_SIIT
2 JOOL_SIIT

Using:

$ iptables --version
iptables v1.8.2 (nf_tables)
ApoXalvation commented 3 years ago

So it might be a problem with iptables version. I'll check this tommorow but this is good news for me. Thank You for Your time. @ldx

ldx commented 3 years ago

No worries, let me know how it goes. Another thing to check is your python-iptables version.

ApoXalvation commented 3 years ago

with: python-iptables==1.0.0 and iptables v1.8.4 (nf_tables) I still get the same error... Which os do You use?

ApoXalvation commented 3 years ago

It works fine on centos 8 but not on ubuntu 20.04 (tested with "v1.8.4 (nf_tables)" and "v1.8.4 (legacy)"

ldx commented 3 years ago

I use Debian 10. I also booted up an Ubuntu 20.04 VM, and the same, the sample code above works fine.

ApoXalvation commented 3 years ago

@ldx The last attempt with python-iptables==1.0.0 on centos 8 (the same thing on Ubuntu 20.04) and honestly this error looks different than previous, but I am trying to achieve the same thing and faced every time different issues on various OSes:

[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import iptc
>>> iptc.easy.add_rule("nat", "PREROUTING", {"protocol": "udp","udp": {"dport": "51821"},"target": {"DNAT": {"to-destination": "10.34.07.2:51821"}}}, 0, False)
>>> iptc.easy.add_rule("mangle", "PREROUTING", {"target": {"JOOL_SIIT": {"instance": "3407"}}}, 0, True)
>>> iptc.easy.add_rule("mangle", "PREROUTING", {"target": {"JOOL_SIIT": {"instance": "3407"}}}, 0, False)
python-iptables: target "JOOL_SIIT" already registered
ldx commented 3 years ago

@ApoXalvation I tried both Ubuntu 20.04 and CentOS8, same result:

Python 3.8.3 (default, Aug 31 2020, 16:03:14)
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import iptc
>>> iptc.easy.add_rule("nat", "PREROUTING", {"protocol": "udp","udp": {"dport": "51821"},"target": {"DNAT": {"to-destination": "10.34.07.2:51821"}}}, 0, False)
>>> iptc.easy.add_rule("mangle", "PREROUTING", {"target": {"JOOL_SIIT": {"instance": "3407"}}}, 0, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/root/python-iptables/iptc/easy.py", line 74, in add_rule
    iptc_chain.append_rule(iptc_rule)
  File "/root/python-iptables/iptc/ip4tc.py", line 1476, in append_rule
    raise ValueError("invalid rule")
ValueError: invalid rule
>>> iptc.easy.add_rule("mangle", "PREROUTING", {"target": {"JOOL_SIIT": {"instance": "3407"}}}, 0, False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/root/python-iptables/iptc/easy.py", line 74, in add_rule
    iptc_chain.append_rule(iptc_rule)
  File "/root/python-iptables/iptc/ip4tc.py", line 1476, in append_rule
    raise ValueError("invalid rule")
ValueError: invalid rule
>>>

(I guess invalid rule is what is expected?)