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