Jason Gagnon
Jason Gagnon

Reputation: 189

call IPtables command within python script

I am trying to call the following command in my python script. I am trying to insert the rule into IP tables. I am using sub process to call it and inserting variables into it where needed, but am getting a large error. Any suggestions?

iptables = subprocess.call('iptables -I FORWARD -eth 0 -m '+protocol+' -t'+protocol+'--dport '+port+'-j DNAT --to-destination'+ipAddress)

Error:

Traceback (most recent call last):
  File "./port_forward.py", line 42, in <module>
    iptables = subprocess.call('iptables -I FORWARD -i eth0 -m '+protocol+' -t'+protocol+'--dport '+port+'-j DNAT --to-destination'+ipAddress)
  File "/usr/lib/python2.7/subprocess.py", line 493, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1259, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

Upvotes: 0

Views: 4922

Answers (4)

Prathamesh Shiralkar
Prathamesh Shiralkar

Reputation: 81

Just pass the argument shell=True along with the command.

iptables = subprocess.call('iptables -I FORWARD -eth 0 -m '+protocol+' -t'+protocol+'--dport '+port+'-j DNAT --to-destination'+ipAddress, shell=True)

Upvotes: 0

h33th3n
h33th3n

Reputation: 267

I wrote a simple firewall the same way and realized, "why not just write it in bash?". Anyway I discovered the python-iptables library and rewrote my code using that. I recommend checking it out. I think it will give you a more robust and structured way of writing iptables rules.

Upvotes: 1

oz123
oz123

Reputation: 28858

Your problem is very common for Python beginners. Instead of formatting the string command, you are trying to build a complex string by concatenating many strings and variables. Instead, use a a string format, it will allow you to test your command and make your code more readable and flexible.

Your original string lacks spaces between the options and the arguments, e.g. --to-destination1.2.3.4.

Hence, you should format your string (This works also for python 2.7):

opts = {'iptables': '/sbin/iptables', 'protocol': 'tcp', 'port': 80, 'ipAddress': '0.0.0.0'}
ipcmd = '{iptables} -I FORWARD -eth 0 -m {protocol} -t {protocol} \
--dport {port} -j DNAT --to-destination  {ipAddress}'.format(**opts)

if DEBUG:
   print ipcmd
iptables = subprocess.call(ipcmd)

This is much easier to modify later, and also, when you do more Python programming, you will see that it is more readable.

Also, to call IPTables, you should be root, as stated in the comments: In the beginning of your script add:

   import sys
   import os
   if not os.getuid() == 0:
        print "You must be root to change IPTables."
        sys.exit(2)

update after seeing your error trace:

You are trying to call a command iptables but it is not in your path. You should call the full path of iptables , e.g. /sbin/iptables

Upvotes: 3

Marc B
Marc B

Reputation: 360652

Your command is full of syntax errors due to missing spaces, as shown below:

iptables = subprocess.call(
     'iptables -I FORWARD -eth 0 -m '
   + protocol
   + ' -t'+protocol
         ^---here
   + '--dport '
      ^-- here
   + port
   + '-j DNAT --to-destination'
      ^--here
   + ipAddress)
     ^---here

As generated, your iptables line will look like

-ttcp--dport 80-j DNAT  --to-destination1.2.3.4

-ttcp--dport is parsed as a SINGLE argument, ditto for 80-j and --to-destination1.2.3.4

Upvotes: 0

Related Questions