root / snf-router / router.py @ 8c850fc2
History | View | Annotate | Download (7 kB)
1 | 7134b24d | Dionysis Grigoropoulos | # Copyright 2011-2013 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | 7134b24d | Dionysis Grigoropoulos | #
|
3 | 7134b24d | Dionysis Grigoropoulos | # Redistribution and use in source and binary forms, with or
|
4 | 7134b24d | Dionysis Grigoropoulos | # without modification, are permitted provided that the following
|
5 | 7134b24d | Dionysis Grigoropoulos | # conditions are met:
|
6 | 7134b24d | Dionysis Grigoropoulos | #
|
7 | 7134b24d | Dionysis Grigoropoulos | # 1. Redistributions of source code must retain the above
|
8 | 7134b24d | Dionysis Grigoropoulos | # copyright notice, this list of conditions and the following
|
9 | 7134b24d | Dionysis Grigoropoulos | # disclaimer.
|
10 | 7134b24d | Dionysis Grigoropoulos | #
|
11 | 7134b24d | Dionysis Grigoropoulos | # 2. Redistributions in binary form must reproduce the above
|
12 | 7134b24d | Dionysis Grigoropoulos | # copyright notice, this list of conditions and the following
|
13 | 7134b24d | Dionysis Grigoropoulos | # disclaimer in the documentation and/or other materials
|
14 | 7134b24d | Dionysis Grigoropoulos | # provided with the distribution.
|
15 | 7134b24d | Dionysis Grigoropoulos | #
|
16 | 7134b24d | Dionysis Grigoropoulos | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | 7134b24d | Dionysis Grigoropoulos | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | 7134b24d | Dionysis Grigoropoulos | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | 7134b24d | Dionysis Grigoropoulos | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | 7134b24d | Dionysis Grigoropoulos | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | 7134b24d | Dionysis Grigoropoulos | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | 7134b24d | Dionysis Grigoropoulos | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | 7134b24d | Dionysis Grigoropoulos | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | 7134b24d | Dionysis Grigoropoulos | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | 7134b24d | Dionysis Grigoropoulos | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | 7134b24d | Dionysis Grigoropoulos | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | 7134b24d | Dionysis Grigoropoulos | # POSSIBILITY OF SUCH DAMAGE.
|
28 | 7134b24d | Dionysis Grigoropoulos | #
|
29 | 7134b24d | Dionysis Grigoropoulos | # The views and conclusions contained in the software and
|
30 | 7134b24d | Dionysis Grigoropoulos | # documentation are those of the authors and should not be
|
31 | 7134b24d | Dionysis Grigoropoulos | # interpreted as representing official policies, either expressed
|
32 | 7134b24d | Dionysis Grigoropoulos | # or implied, of GRNET S.A.
|
33 | 7134b24d | Dionysis Grigoropoulos | |
34 | 7134b24d | Dionysis Grigoropoulos | |
35 | 7134b24d | Dionysis Grigoropoulos | '''
|
36 | 7134b24d | Dionysis Grigoropoulos | Script is called when an interface changes. For the time being we only support
|
37 | 7134b24d | Dionysis Grigoropoulos | one subnet and one public IP.
|
38 | 7134b24d | Dionysis Grigoropoulos |
|
39 | 7134b24d | Dionysis Grigoropoulos | Logic:
|
40 | 7134b24d | Dionysis Grigoropoulos | Whenever this script is called, one of our nics changed, so flush everything.
|
41 | 8c850fc2 | Marios Kogias | If the nic is public create the rules for the private net given in the config
|
42 | 8c850fc2 | Marios Kogias | file
|
43 | 8c850fc2 | Marios Kogias | Else do nothing
|
44 | 7134b24d | Dionysis Grigoropoulos | '''
|
45 | 7134b24d | Dionysis Grigoropoulos | |
46 | 7134b24d | Dionysis Grigoropoulos | import iptc |
47 | 7134b24d | Dionysis Grigoropoulos | import netifaces |
48 | 7134b24d | Dionysis Grigoropoulos | import sys |
49 | 7134b24d | Dionysis Grigoropoulos | import IPy |
50 | 8c850fc2 | Marios Kogias | import ConfigParser |
51 | 8c850fc2 | Marios Kogias | import logging |
52 | 8c850fc2 | Marios Kogias | import logging.handlers |
53 | 8c850fc2 | Marios Kogias | |
54 | 8c850fc2 | Marios Kogias | CONFIG_FILE = 'file.conf'
|
55 | 7134b24d | Dionysis Grigoropoulos | |
56 | 7134b24d | Dionysis Grigoropoulos | |
57 | 7134b24d | Dionysis Grigoropoulos | def flush(table_name='', chain_name=''): |
58 | 7134b24d | Dionysis Grigoropoulos | '''
|
59 | 7134b24d | Dionysis Grigoropoulos | Flushes the chain chain_name of table table_name
|
60 | 7134b24d | Dionysis Grigoropoulos | 'iptables -F chain_name -t table_name'
|
61 | 7134b24d | Dionysis Grigoropoulos | '''
|
62 | 7134b24d | Dionysis Grigoropoulos | table = iptc.Table(getattr(iptc.Table, table_name))
|
63 | 7134b24d | Dionysis Grigoropoulos | table.flush_entries(chain_name) |
64 | 7134b24d | Dionysis Grigoropoulos | |
65 | 7134b24d | Dionysis Grigoropoulos | |
66 | 8c850fc2 | Marios Kogias | def create_dnat(cidr, ports, start_port, step, pub_ip): |
67 | 8c850fc2 | Marios Kogias | i = 0
|
68 | 8c850fc2 | Marios Kogias | for ip in IPy.IP(cidr): |
69 | 8c850fc2 | Marios Kogias | j = 1
|
70 | 8c850fc2 | Marios Kogias | for sport in ports: |
71 | 8c850fc2 | Marios Kogias | rule = iptc.Rule() |
72 | 8c850fc2 | Marios Kogias | rule.dst = pub_ip |
73 | 8c850fc2 | Marios Kogias | rule.protocol = "tcp"
|
74 | 8c850fc2 | Marios Kogias | match = iptc.Match(rule, "tcp")
|
75 | 8c850fc2 | Marios Kogias | port = int(start_port) + j*int(step) + i |
76 | 8c850fc2 | Marios Kogias | match.dport = str(port)
|
77 | 8c850fc2 | Marios Kogias | rule.add_match(match) |
78 | 8c850fc2 | Marios Kogias | target = iptc.Target(rule, "DNAT")
|
79 | 8c850fc2 | Marios Kogias | rule.target = target |
80 | 8c850fc2 | Marios Kogias | rule.target.to_destination = "%s:%s" % (ip, sport)
|
81 | 8c850fc2 | Marios Kogias | chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "PREROUTING")
|
82 | 8c850fc2 | Marios Kogias | chain.insert_rule(rule) |
83 | 8c850fc2 | Marios Kogias | j = j + 1
|
84 | 8c850fc2 | Marios Kogias | |
85 | 8c850fc2 | Marios Kogias | i = i + 1
|
86 | 8c850fc2 | Marios Kogias | |
87 | 8c850fc2 | Marios Kogias | |
88 | 8c850fc2 | Marios Kogias | def create_snat(public_ip, private_net, public_iface): |
89 | 7134b24d | Dionysis Grigoropoulos | chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "POSTROUTING")
|
90 | 7134b24d | Dionysis Grigoropoulos | rule = iptc.Rule() |
91 | 8c850fc2 | Marios Kogias | private_net = "%s/%s" % (str(IPy.IP(private_net).net()), str(IPy.IP(private_net).netmask())) |
92 | 7134b24d | Dionysis Grigoropoulos | rule.src = private_net |
93 | 7134b24d | Dionysis Grigoropoulos | rule.dst = "!%s" % private_net
|
94 | 7134b24d | Dionysis Grigoropoulos | rule.out_interface = public_iface |
95 | 7134b24d | Dionysis Grigoropoulos | target = iptc.Target(rule, "SNAT")
|
96 | 7134b24d | Dionysis Grigoropoulos | target.to_source = public_ip |
97 | 7134b24d | Dionysis Grigoropoulos | rule.target = target |
98 | 7134b24d | Dionysis Grigoropoulos | chain.insert_rule(rule) |
99 | 7134b24d | Dionysis Grigoropoulos | |
100 | 7134b24d | Dionysis Grigoropoulos | |
101 | 7134b24d | Dionysis Grigoropoulos | def create_states(private_net, public_iface, private_iface): |
102 | 7134b24d | Dionysis Grigoropoulos | chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "FORWARD")
|
103 | 7134b24d | Dionysis Grigoropoulos | rule = iptc.Rule() |
104 | 8c850fc2 | Marios Kogias | private_net = "%s/%s" % (str(IPy.IP(private_net).net()), str(IPy.IP(private_net).netmask())) |
105 | 7134b24d | Dionysis Grigoropoulos | rule.src = "!%s" % private_net
|
106 | 7134b24d | Dionysis Grigoropoulos | rule.dst = private_net |
107 | 7134b24d | Dionysis Grigoropoulos | rule.out_interface = private_iface |
108 | 7134b24d | Dionysis Grigoropoulos | rule.in_interface = public_iface |
109 | 7134b24d | Dionysis Grigoropoulos | match = iptc.Match(rule, 'state')
|
110 | 7134b24d | Dionysis Grigoropoulos | match.state = "ESTABLISHED,RELATED"
|
111 | 7134b24d | Dionysis Grigoropoulos | rule.add_match(match) |
112 | 7134b24d | Dionysis Grigoropoulos | target = iptc.Target(rule, "ACCEPT")
|
113 | 7134b24d | Dionysis Grigoropoulos | rule.target = target |
114 | 7134b24d | Dionysis Grigoropoulos | chain.insert_rule(rule) |
115 | 7134b24d | Dionysis Grigoropoulos | |
116 | 7134b24d | Dionysis Grigoropoulos | |
117 | 7134b24d | Dionysis Grigoropoulos | def nic_info(interface): |
118 | 7134b24d | Dionysis Grigoropoulos | '''
|
119 | 7134b24d | Dionysis Grigoropoulos | Returns the IP and ip type (PUBLIC/PRIVATE) of interface
|
120 | 7134b24d | Dionysis Grigoropoulos | '''
|
121 | 8c850fc2 | Marios Kogias | nic = dict()
|
122 | 7134b24d | Dionysis Grigoropoulos | nic['ip'] = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr'] |
123 | 7134b24d | Dionysis Grigoropoulos | nic['ip_type'] = IPy.IP(nic['ip']).iptype() |
124 | 7134b24d | Dionysis Grigoropoulos | return nic
|
125 | 7134b24d | Dionysis Grigoropoulos | |
126 | 7134b24d | Dionysis Grigoropoulos | |
127 | 8c850fc2 | Marios Kogias | def parse_config(filename): |
128 | 8c850fc2 | Marios Kogias | config = ConfigParser.ConfigParser() |
129 | 8c850fc2 | Marios Kogias | config.read(filename) |
130 | 8c850fc2 | Marios Kogias | return config
|
131 | 8c850fc2 | Marios Kogias | |
132 | 8c850fc2 | Marios Kogias | |
133 | 8c850fc2 | Marios Kogias | def create_remote_logger(host, url, method): |
134 | 8c850fc2 | Marios Kogias | logger = logging.getLogger('snf-router')
|
135 | 8c850fc2 | Marios Kogias | logger.setLevel(logging.INFO) |
136 | 8c850fc2 | Marios Kogias | #http_handler = logging.handlers.HTTPHandler(host, url, method)
|
137 | 8c850fc2 | Marios Kogias | http_handler = logging.handlers.WatchedFileHandler("test.log")
|
138 | 8c850fc2 | Marios Kogias | formatter = logging.Formatter("%(asctime)s - %(message)s")
|
139 | 8c850fc2 | Marios Kogias | http_handler.setFormatter(formatter) |
140 | 8c850fc2 | Marios Kogias | logger.addHandler(http_handler) |
141 | 8c850fc2 | Marios Kogias | return logger
|
142 | 7134b24d | Dionysis Grigoropoulos | |
143 | 7134b24d | Dionysis Grigoropoulos | |
144 | 7134b24d | Dionysis Grigoropoulos | def allow_ssh(): |
145 | 8c850fc2 | Marios Kogias | chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
|
146 | 8c850fc2 | Marios Kogias | rule = iptc.Rule() |
147 | 8c850fc2 | Marios Kogias | rule.protocol = "tcp"
|
148 | 8c850fc2 | Marios Kogias | match = iptc.Match(rule, "tcp")
|
149 | 8c850fc2 | Marios Kogias | match.dport = "22"
|
150 | 8c850fc2 | Marios Kogias | rule.add_match(match) |
151 | 8c850fc2 | Marios Kogias | target = iptc.Target(rule, "ACCEPT")
|
152 | 8c850fc2 | Marios Kogias | rule.target = target |
153 | 8c850fc2 | Marios Kogias | chain.insert_rule(rule) |
154 | 8c850fc2 | Marios Kogias | |
155 | 8c850fc2 | Marios Kogias | chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "OUTPUT")
|
156 | 8c850fc2 | Marios Kogias | rule = iptc.Rule() |
157 | 8c850fc2 | Marios Kogias | rule.protocol = "tcp"
|
158 | 8c850fc2 | Marios Kogias | match = iptc.Match(rule, "tcp")
|
159 | 8c850fc2 | Marios Kogias | match.sport = "22"
|
160 | 8c850fc2 | Marios Kogias | rule.add_match(match) |
161 | 8c850fc2 | Marios Kogias | target = iptc.Target(rule, "ACCEPT")
|
162 | 8c850fc2 | Marios Kogias | rule.target = target |
163 | 8c850fc2 | Marios Kogias | chain.insert_rule(rule) |
164 | 8c850fc2 | Marios Kogias | |
165 | 8c850fc2 | Marios Kogias | def set_policy(table, chain, policy): |
166 | 8c850fc2 | Marios Kogias | cur_table = iptc.Table(getattr(iptc.Table, table))
|
167 | 8c850fc2 | Marios Kogias | cur_chain = iptc.Chain(cur_table, chain) |
168 | 8c850fc2 | Marios Kogias | cur_chain.set_policy(policy) |
169 | 7134b24d | Dionysis Grigoropoulos | |
170 | 7134b24d | Dionysis Grigoropoulos | |
171 | 8c850fc2 | Marios Kogias | def get_public_iface(): |
172 | 8c850fc2 | Marios Kogias | public_iface = ''
|
173 | 8c850fc2 | Marios Kogias | for iface in netifaces.interfaces(): |
174 | 8c850fc2 | Marios Kogias | nic = nic_info(iface) |
175 | 8c850fc2 | Marios Kogias | if nic['ip_type'] == 'PUBLIC': |
176 | 8c850fc2 | Marios Kogias | public_iface = iface |
177 | 8c850fc2 | Marios Kogias | break
|
178 | 8c850fc2 | Marios Kogias | return public_iface
|
179 | 7134b24d | Dionysis Grigoropoulos | |
180 | 7134b24d | Dionysis Grigoropoulos | |
181 | 7134b24d | Dionysis Grigoropoulos | def main(): |
182 | 8c850fc2 | Marios Kogias | #read config file
|
183 | 8c850fc2 | Marios Kogias | config = parse_config(CONFIG_FILE) |
184 | 8c850fc2 | Marios Kogias | |
185 | 8c850fc2 | Marios Kogias | #create logger
|
186 | 8c850fc2 | Marios Kogias | logger = create_remote_logger(config.get('log', 'host'), |
187 | 8c850fc2 | Marios Kogias | config.get('log', 'url'), |
188 | 8c850fc2 | Marios Kogias | config.get('log', 'method')) |
189 | 8c850fc2 | Marios Kogias | |
190 | 8c850fc2 | Marios Kogias | nic = nic_info(sys.argv[1])
|
191 | 8c850fc2 | Marios Kogias | logger.info(nic['ip_type'] + " " + sys.argv[1] + " " + sys.argv[2]) |
192 | 8c850fc2 | Marios Kogias | |
193 | 8c850fc2 | Marios Kogias | if sys.argv[2] == 'up' and nic['ip_type'] == "PUBLIC": |
194 | 8c850fc2 | Marios Kogias | #flush the old rules
|
195 | 8c850fc2 | Marios Kogias | flush("NAT", "PREROUTING") |
196 | 8c850fc2 | Marios Kogias | flush("NAT", "POSTROUTING") |
197 | 8c850fc2 | Marios Kogias | flush("FILTER", "FORWARD") |
198 | 8c850fc2 | Marios Kogias | |
199 | 8c850fc2 | Marios Kogias | #set policy
|
200 | 8c850fc2 | Marios Kogias | #set_policy("FILTER", "FORWARD", config.get('policy', 'FORWARD'))
|
201 | 8c850fc2 | Marios Kogias | #set_policy("FILTER", "OUTPUT", config.get('policy', 'OUTPUT'))
|
202 | 8c850fc2 | Marios Kogias | #set_policy("FILTER", "INPUT", config.get('policy', 'INPUT'))
|
203 | 8c850fc2 | Marios Kogias | |
204 | 8c850fc2 | Marios Kogias | # create dnat
|
205 | 8c850fc2 | Marios Kogias | create_dnat(config.get("private-network", "cidr"), |
206 | 8c850fc2 | Marios Kogias | config.get("private-network", "ports").split(","), |
207 | 8c850fc2 | Marios Kogias | config.get("private-network", "start_port"), |
208 | 8c850fc2 | Marios Kogias | config.get("private-network", "step"), |
209 | 8c850fc2 | Marios Kogias | nic["ip"])
|
210 | 8c850fc2 | Marios Kogias | |
211 | 8c850fc2 | Marios Kogias | # create snat
|
212 | 8c850fc2 | Marios Kogias | create_snat(nic['ip'], config.get("private-network","cidr"), |
213 | 8c850fc2 | Marios Kogias | sys.argv[1])
|
214 | 8c850fc2 | Marios Kogias | |
215 | 8c850fc2 | Marios Kogias | create_states(config.get("private-network","cidr"), sys.argv[1], |
216 | 8c850fc2 | Marios Kogias | config.get("private-network","iface")) |
217 | 8c850fc2 | Marios Kogias | |
218 | 8c850fc2 | Marios Kogias | if __name__ == "__main__": |
219 | 8c850fc2 | Marios Kogias | main() |