0

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
gre_gor
  • 6,669
  • 9
  • 47
  • 52
Jason Gagnon
  • 189
  • 3
  • 5
  • 11
  • General tip: if you say you get an error, TELL US what the error is. Are you running this script with root privileges? A standard user cannot run iptables... – Marc B Jul 23 '13 at 19:43
  • 1
    You need to be root to call iptables. Is your python script running with root privileges? – mti2935 Jul 23 '13 at 19:44
  • Not enough information, I'm afraid. We need the error too. – Kudzu Jul 23 '13 at 19:44
  • the errorTraceback (most recent call last): File "./port_forward.py", line 42, in 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 – Jason Gagnon Jul 23 '13 at 19:55

4 Answers4

3

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

Community
  • 1
  • 1
oz123
  • 27,559
  • 27
  • 125
  • 187
1

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.

h33th3n
  • 257
  • 1
  • 3
  • 11
0

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

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • i am trying to get it to be iptables -I FORWARD -i eth0 -m TCP -t TCP --dport port -j DNAT -- to-destination destination address – Jason Gagnon Jul 23 '13 at 19:53
  • yes, and I've shown you EXACTLY where you're making the syntax errors. – Marc B Jul 23 '13 at 19:54
  • yes and i spaced it out so now the output would be: iptables -IFORWARD -i eth0 -m tcp -p tcp --dport 80 -j DNAT --to-destination 192.168.1.1 and still errors out – Jason Gagnon Jul 23 '13 at 20:07
  • The error you pasted above suggests that iptables isn't in the path of whatever shell python's invoking to run the command. – Marc B Jul 23 '13 at 20:11
0

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)