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