Statistics
| Branch: | Tag: | Revision:

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()