svn:keywords
[nodemanager.git] / iptables.py
1 #!/usr/bin/python -tt
2 #
3 # $Id$
4 # $URL$
5 #
6 # Author: Daniel Hokka Zakrisson <daniel@hozac.com>
7 # $Id$
8
9 import os
10 import logger
11 import subprocess
12
13 class IPTables:
14     """A class to encapsulate iptables operations"""
15     IPTABLES_RESTORE = "/sbin/iptables-restore"
16
17     def __init__(self):
18         self.extifs = []
19         self.intifs = []
20         self.pfs = []
21
22     def add_ext(self, interface):
23         """Adds an external interface. An external interface is one where
24            outgoing traffic will be NATed, and incoming traffic will go to
25            the port forward chain."""
26         self.extifs.append(interface)
27
28     def add_int(self, interface):
29         """Adds an internal interface. An internal interface is trusted,
30            and traffic coming in on it is allowed through."""
31         self.intifs.append(interface)
32
33     def add_pf(self, pf):
34         """Adds a port forward. The argument is a dict consisting of:
35            'protocol'       tcp/udp
36            'destination'    the new destination IP
37            'dport'          the destination port
38            'new_dport'      the new destination port
39            and optionally:
40            'interface'      the incoming interface
41            'source'         limit the redirect to these IPs"""
42         # XXX Should make sure the required fields are there
43         self.pfs.append(pf)
44
45     def commit(self):
46         """Call commit when all the rules are ready to be applied.
47            This is a no-op if no port forwards, external or internal
48            interfaces have been declared."""
49
50         # XXX This should check for errors
51         #     and make sure the new ruleset differs from the current one
52
53         if (len(self.extifs) + len(self.intifs) + len(self.pfs)) == 0:
54             return True
55
56         restore = subprocess.Popen([self.IPTABLES_RESTORE, "--noflush"], stdin=subprocess.PIPE)
57         restore.stdin.write("""*filter
58 :INPUT ACCEPT [0:0]
59 :FORWARD ACCEPT [0:0]
60 :OUTPUT ACCEPT [0:0]
61 :LOGDROP - [0:0]
62 :SLICESPRE - [0:0]
63 :SLICES - [0:0]
64 :PORTFW - [0:0]
65
66 -F INPUT
67 -F FORWARD
68 -F OUTPUT
69
70 -A LOGDROP -j LOG
71 -A LOGDROP -j DROP
72 -A OUTPUT -j BLACKLIST
73 -A OUTPUT -m mark ! --mark 0/65535 -j SLICESPRE
74 -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
75 """)
76
77         for int in self.intifs:
78             # Allow all traffic from internal to external
79             for ext in self.extifs:
80                 restore.stdin.write("-A FORWARD -i %s -o %s -j ACCEPT\n" % (int, ext))
81             # Traffic from slices to internal networks is scrutinized
82             restore.stdin.write("-A SLICESPRE -o %s -j SLICES\n" % int)
83
84         restore.stdin.write("-A FORWARD -m state --state NEW -j PORTFW\n")
85         for pf in self.pfs:
86             # Port forwards, redirect incoming external traffic to some internal address
87             rule = "-A PORTFW -p %s -d %s " % (pf['protocol'], pf['destination'])
88             if 'interface' in pf:
89                 rule += "-i %s " % pf['interface']
90             if 'source' in pf:
91                 rule += "-s %s " % pf['source']
92             rule += "--dport %s" % pf['new_dport']
93             restore.stdin.write(rule + "\n")
94
95         restore.stdin.write("-A FORWARD -j LOGDROP\n")
96
97         # This should have a way to add rules
98         restore.stdin.write("-A SLICES -j LOGDROP\n")
99         restore.stdin.write("""COMMIT
100 *nat
101 :PREROUTING ACCEPT [0:0]
102 :POSTROUTING ACCEPT [0:0]
103 :OUTPUT ACCEPT [0:0]
104 :PORTFW - [0:0]
105 :MASQ - [0:0]
106
107 -F PREROUTING
108 -F POSTROUTING
109 -F OUTPUT
110 """)
111
112         # Outgoing traffic on external interfaces needs to be NATed
113         for ext in self.extifs:
114             restore.stdin.write("-A MASQ -o %s -j MASQUERADE\n")
115
116         # Redirect port forwards to their real destination
117         for pf in self.pfs:
118             rule = "-A PORTFW -p %s " % pf['protocol']
119             if 'interface' in pf:
120                 rule += "-i %s " % pf['interface']
121             if 'source' in pf:
122                 rule += "-s %s " % pf['source']
123             rule += "--dport %s -j DNAT --to %s:%s" % (pf['dport'], pf['destination'],
124                     pf['new_dport'])
125             restore.stdin.write(rule + "\n")
126
127         restore.stdin.write("COMMIT\n")
128         restore.stdin.close()
129         return restore.wait() == 0