pyinfra-dev / pyinfra

pyinfra turns Python code into shell commands and runs them on your servers. Execute ad-hoc commands and write declarative operations. Target SSH servers, local machine and Docker containers. Fast and scales from one server to thousands.
https://pyinfra.com
MIT License
3.91k stars 382 forks source link

iptables.rule make duplicated rules #935

Closed julienfr112 closed 9 months ago

julienfr112 commented 1 year ago

Describe the bug

iptables.rule make duplicated rules

To Reproduce

from pyinfra import host, logger
from pyinfra.operations import iptables, server, python
from pyinfra.facts.iptables import IptablesRules

facts = host.get_fact(IptablesRules, table="filter")
logger.info(facts) # previously added rules are well collected in the fact

iptables.rule(
    chain="INPUT",
    jump="ACCEPT",
    destination_port=22,
    protocol="TCP",
)
ipt = server.shell(["iptables -S"])

def cb(*args):
    logger.info(ipt.stdout)

python.call(function=cb) # we see duplication of the rule, one more at every run

Expected behavior

do not duplicate rules

Meta

    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 14), opHash=4ea1358dc0c6a2aa3901d41cae7ef6ed51d46600
    [pyinfra.api.facts] Getting fact: iptables.IptablesRules (table=filter) (ensure_hosts: None)
    [pyinfra.connectors.ssh] Running command on XXXXXXXXX: (pty=None) sudo -H -n sh -c 'iptables-save -t filter'
    [pyinfra.connectors.ssh] Waiting for exit status...
    [pyinfra.connectors.ssh] Command exit status: 0
    [pyinfra.api.facts] [XXXXXXXXXXX] Loaded fact iptables.IptablesRules (table=filter)
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 21), opHash=b1d1da4808a9765a47e35b9cfb13e271e108f5f0
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 28), opHash=532ac95b4b20704066d28335127cf7675d448182
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 35), opHash=b45b184215602c1ec68ee76d16d7b172e817506f
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 42), opHash=dcacca10d0098e2654c41fa3e5ea6f51f7b07533
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 48), opHash=74a57e31e500f612b4c98833d40f1e0f14865498
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 54), opHash=d0e52635295de97eac812cad251b939c9dbcb415
    [pyinfra.api.operation] Adding operation, {'Iptables/Rule'}, opOrder=(0, 61), opHash=bd136e609ea7db4400a09d2586f0e08866e5e5be
    [pyinfra.api.operation] Adding operation, {'Iptables/Chain'}, opOrder=(0, 71), opHash=93526d6ccb412894b7e4f7e6924144d680c7b301
julienfr112 commented 1 year ago

very ugly (and dangerous) workaround:

iptables.chain(chain="INPUT", policy="ACCEPT")
iptables.chain(chain="OUTPUT", policy="ACCEPT")
iptables.chain(chain="FORWARD", policy="ACCEPT")
server.shell(["iptables -F"])
sysadmin75 commented 1 year ago

@julienfr112

The issue is your deployment file has the protocol as all caps - TCP, but iptables changes this to lower case. The code doesn't account for this so the rule is added again.

You can fix your deployment file by chaging tcp to all lowercase.

I've also created a PR to address this. https://github.com/Fizzadar/pyinfra/pull/937

Fizzadar commented 9 months ago

Fixed by the PR above 🙏