First part of some serious refactoring

The problem is that teh convergence model does not works as it currently is
Firewall rules will get added but not deleted

This builds a table of firewall rules that need to be present
The new CsNetfilter class will then do:
1.  Add rules that are not present
2.  Delete any that were not explicitly in the add list

WIP
This commit is contained in:
Ian Southam 2014-08-14 16:32:17 +02:00 committed by wilderrodrigues
parent e323d63867
commit c6d99a45a2
2 changed files with 119 additions and 142 deletions

View File

@ -1,4 +1,77 @@
import CsHelper
from pprint import pprint
class Netfilter(object):
class CsChain(object):
def __init__(self):
self.chain = {}
def add(self, table, chain):
if not table in self.chain.keys():
self.chain.setdefault(table, []).append( chain )
else:
self.chain[table].append(chain)
def get(self, table):
return self.chain[table]
class CsTable(object):
def __init__(self):
self.table = []
self.last_added = ''
def add(self, name):
if not name in self.table:
self.table.append(name)
self.last_added = name
def get(self):
return self.table
def last(self):
return self.last_added
class CsNetfilters(object):
def __init__(self):
self.rules = []
self.table = CsTable()
self.chain = CsChain()
self.get_all_rules()
def get_all_rules(self):
for i in CsHelper.execute("iptables-save"):
if i.startswith('*'): # Table
self.table.add(i[1:])
if i.startswith(':'): # Chain
self.chain.add(self.table.last(), i[1:].split(' ')[0])
if i.startswith('-A'): # Rule
rule = CsNetfilter()
rule.parse(i)
self.save(rule)
def save(self,rule):
self.rules.append(rule)
def get(self):
return self.rules
def hasTable(self, table):
return table in self.table.get()
def hasChain(self, table, chain):
return chain in self.chain.get(table)
class CsNetfilter(object):
def parse(self, rule):
rule.replace('! -', '!_-')
bits = rule.split(' ')
self.rule = dict(zip(bits[0::2], bits[1::2])).iteritems()
if __name__ == "__main__":
t = CsNetfilters()
print t.hasTable('mangle');
print t.hasChain('mangle', 'PREROUTING');

View File

@ -27,6 +27,9 @@ import shutil
import os.path
from cs_ip import merge
import CsHelper
import CsNetfilter
fw = []
class CsFile:
""" File editors """
@ -230,11 +233,7 @@ class CsApp:
if self.type == "guest":
gn = CsGuestNetwork(self.dev)
self.domain = gn.get_domain()
class CsAcl(CsApp):
"""
Manage network acls
"""
global fw
class CsPasswdSvc(CsApp):
"""
@ -242,11 +241,9 @@ class CsPasswdSvc(CsApp):
"""
def setup(self):
cmds = "-A INPUT -i %s -d %s -p %s -m %s --state %s --dport %s -j %s"
slist = [ self.dev, self.ip, "tcp", "state", "NEW", "8080", "ACCEPT" ]
firewall = CsIpTables(self.dev)
firewall.change_rule("", slist, cmds)
fw.append(["", "front",
"-A INPUT -i %s -d %s -p tcp -m tcp --state NEW --dport 8080 -j ACCEPT" % (self.dev, self.ip)
])
proc = CsProcess(['/opt/cloud/bin/vpc_passwd_server', self.ip])
if not proc.find():
@ -277,36 +274,27 @@ class CsApache(CsApp):
if file.is_changed():
CsHelper.service("apache2", "restart")
cmds = "-A INPUT -i %s -d %s -p %s -m %s --state %s --dport %s -j %s"
slist = [ self.dev, self.ip, "tcp", "state", "NEW", "80", "ACCEPT" ]
firewall = CsIpTables(self.dev)
firewall.change_rule("", slist, cmds)
fw.append(["", "front",
"-A INPUT -i %s -d %s -p tcp -m state --state NEW --dport 80 -j ACCEPT" % (self.dev, self.ip)
])
class CsDnsmasq(CsApp):
""" Set up dnsmasq """
def add_firewall_rules(self, method):
def add_firewall_rules(self):
""" Add the necessary firewall rules
This is problamatic because the current logic cannot delete them
(In a convergence model)
We will need to store some state about what "used" to be there
"""
firewall = CsIpTables(self.dev)
fw.append(["", "front"
"-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev
])
cmds = "-A INPUT -i %s -p %s -m %s --dport %s -j %s"
slist = [ self.dev, "udp", "udp", "67", "ACCEPT" ]
firewall.change_rule("", slist, cmds)
fw.append(["", "front"
"-A INPUT -i %s -d %s -p udp -m udp --dport 53 -j ACCEPT" % (self.dev, self.ip)
])
cmds = "-A INPUT -i %s -d %s -p %s -m %s --dport %s -j %s"
slist = [ self.dev, self.ip, "udp", "udp", "53", "ACCEPT" ]
firewall.change_rule("", slist, cmds)
cmds = "-A INPUT -i %s -d %s -p %s -m %s --dport %s -j %s"
slist = [ self.dev, self.ip, "tcp", "tcp", "53", "ACCEPT" ]
firewall.change_rule("", slist, cmds)
fw.append(["", "front"
"-A INPUT -i %s -d %s -p tcp -m tcp --dport 53 -j ACCEPT" % ( self.dev, self.ip )
])
def configure_server(self, method = "add"):
file = CsFile("/etc/dnsmasq.d/cloud.conf")
@ -352,6 +340,7 @@ class CsDevice:
if dev != '':
self.tableNo = dev[3]
self.table = "Table_%s" % dev
global fw
def configure_rp(self):
"""
@ -394,108 +383,11 @@ class CsDevice:
if " DOWN " in i:
cmd2 = "ip link set %s up" % self.dev
CsHelper.execute(cmd2)
CsIpTables(self.dev).set_connmark()
cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-mark 0x%s" % \
(self.dev, "Table_%s" % self.tableNo)
fw.append(["mangle", "", cmd])
class CsIpTables:
""" Utility class
All the bits and pieces needed for iptables operations
"""
def __init__(self, dev):
self.dev = dev
self.tableNo = dev[3]
self.table = "Table_%s" % (dev)
self.devChain = "ACL_INBOUND_%s" % (dev)
def set_connmark(self, method = "add"):
""" Set connmark for device """
slist = ["PREROUTING", self.dev, "state", "NEW", "CONNMARK", self.tableNo]
if method == "add":
cmds="-A %s -i %s -m %s --state %s -j %s --set-mark 0x%s"
else:
cmds="-D %s -i %s -m %s --state %s -j %s --set-mark 0x%s"
self.change_rule("mangle", slist, cmds)
def set_accept(self, table, method = "add"):
""" Add an accept rule
First version - very simple - once I find out what the patterns
are this will be refactored
"""
slist = [ self.devChain, "ACCEPT" ]
if method == "add":
cmds = "-A %s -j %s"
else:
cmds = "-D %s -j %s"
self.change_rule( table, slist, cmds)
def set_forward(self, ip, method = "add"):
""" set a forward to the device chain
takes a CsIP object """
slist = [ "FORWARD", self.dev, ip['network'], self.devChain ]
if method == "add":
cmds = "-A %s -o %s -d %s -j %s"
else:
cmds = "-D %s -o %s -d %s -j %s"
self.change_rule('', slist, cmds)
def set_drop(self, method = "add"):
""" Ensure the last rule is drop """
slist = [ self.devChain, "DROP" ]
if method == "add":
cmds="-A %s -j %s"
else:
cmds="-D %s -j %s"
self.change_rule('', slist, cmds)
def set_static_nat(self, ip, method = "add"):
""" Add static nat to a device/ip combination
Takes a CsIp object as its parameter
"""
slist = ["POSTROUTING", ip['network'], self.dev, "SNAT", ip['public_ip']]
if method == "add":
cmds ="-A %s -s %s -o %s -j %s --to-source %s"
else:
cmds ="-D %s -s %s -o %s -j %s --to-source %s"
self.change_rule('nat', slist, cmds)
def set_preroute(self, ip, method):
slist = [ "PREROUTING", "NEW", self.dev, ip['network'], ip['public_ip'], self.devChain ]
cmds = "-A %s -m state --state %s -i %s -s %s ! -d %s -j %s"
if method == "add":
self.change_rule('mangle', slist, cmds)
def change_rule(self, table, slist, cmds):
cmd = ''
if not self.has_rule(table, slist):
if table != '':
cmd = "-t %s " % table
cmd += cmds % tuple(slist)
CsHelper.execute("iptables %s" % (cmd))
logging.info("iptables %s", cmd)
def set_chain(self, table, method):
""" Create a chain if it does not already exist """
slist = [ self.devChain ]
cmd = ''
if not self.has_rule(table, slist):
if table != '':
cmd = "-t %s " % table
cmd += "-N %s" % tuple(slist)
CsHelper.execute("iptables %s" % (cmd))
logging.info("iptables %s", cmd)
def has_rule(self, table, list):
""" Check if a particular rule exists """
cmd = "iptables-save "
if table != "":
cmd += "-t %s" % table
for line in CsHelper.execute(cmd):
matches = len([i for i in list if i in line])
if matches == len(list):
return True
return False
class CsIP:
def __init__(self, dev):
@ -552,16 +444,27 @@ class CsIP:
route.add(self.address, method)
# On deletion nw_type will no longer be known
if self.get_type() in [ "guest" ]:
devChain = "ACL_INBOUND_%s" % (self.dev)
CsDevice(self.dev).configure_rp()
CsIpTables(self.dev).set_static_nat(self.address, method)
CsIpTables(self.dev).set_chain('', method)
CsIpTables(self.dev).set_chain('mangle', method)
CsIpTables(self.dev).set_accept('mangle', method)
CsIpTables(self.dev).set_forward(self.address, method)
CsIpTables(self.dev).set_drop(method)
CsIpTables(self.dev).set_preroute(self.address, method)
fw.append(["nat", "",
"-A POSTROUTING -s %s -o %s -j SNAT --to-source %s" % \
(self.address['network'], self.dev, self.address['public_ip'])
])
fw.append(["", "", "-N %s" % devChain ])
fw.append(["mangle", "", "-N %s" % devChain ])
fw.append(["mangle", "", "-A %s -j ACCEPT" % devChain])
fw.append(["", "",
"-A FORWARD -o %s -d %s -j %s" % (self.dev, self.address['network'], devChain)
])
fw.append(["", "", "-A DROP -j %s" % devChain])
fw.append(["mangle", "",
"-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s -j %s" % \
(self.dev, self.address['network'], self.address['public_ip'], devChain)
])
dns = CsDnsmasq(self)
dns.add_firewall_rules("add")
dns.add_firewall_rules()
dns.configure_server()
app = CsApache(self)
app.setup()
@ -802,7 +705,7 @@ def main(argv):
logging.basicConfig(filename='/var/log/cloud.log',
level=logging.DEBUG,
format='%(asctime)s %(message)s')
db = dataBag()
db.setKey("ips")
db.load()
@ -828,6 +731,7 @@ def main(argv):
if CsDevice(dev).waitfordevice():
ip.configure()
CsPassword()
pprint(fw)
metadata = CsVmMetadata()
metadata.process()