#!/usr/bin/python # -- coding: utf-8 -- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import base64 import logging import os import re import sys import urllib.request import urllib.parse import urllib.error import time import copy from collections import OrderedDict from fcntl import flock, LOCK_EX, LOCK_UN from cs.CsDatabag import CsDataBag from cs.CsNetfilter import CsNetfilters from cs.CsDhcp import CsDhcp from cs.CsRedundant import * from cs.CsFile import CsFile from cs.CsMonitor import CsMonitor from cs.CsLoadBalancer import CsLoadBalancer from cs.CsConfig import CsConfig from cs.CsProcess import CsProcess from cs.CsStaticRoutes import CsStaticRoutes from cs.CsVpcGuestNetwork import CsVpcGuestNetwork ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, \ echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, \ nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" TCP_UDP_PORT_ANY = "{ 0-65535 }" def removeUndesiredCidrs(cidrs, version): version_char = ":" if version == 4: version_char = "." if "," in cidrs: cidrList = cidrs.split(",") ipv4Cidrs = [] for cidr in cidrList: if version_char not in cidr: ipv4Cidrs.append(cidr) if len(ipv4Cidrs) > 0: return ",".join(ipv4Cidrs) else: if version_char not in cidrs: return cidrs return None def appendStringIfNotEmpty(s1, s2): if s2: if not isinstance(s2, str): s2 = str(s2) if s1: return s1 + " " + s2 return s2 return s1 class CsPassword(CsDataBag): TOKEN_FILE = "/tmp/passwdsrvrtoken" def process(self): for item in self.dbag: if item == "id": continue self.__update(item, self.dbag[item]) def __update(self, vm_ip, password): token = "" try: tokenFile = open(self.TOKEN_FILE) token = tokenFile.read() except IOError: logging.debug("File %s does not exist" % self.TOKEN_FILE) server_ip = None guest_ip = None for interface in self.config.address().get_interfaces(): if interface.ip_in_subnet(vm_ip) and interface.is_added(): if self.config.cl.is_redundant(): server_ip = interface.get_gateway() guest_ip = interface.get_ip() else: server_ip = interface.get_ip() break if server_ip is not None: if guest_ip is None: proc = CsProcess(['/opt/cloud/bin/passwd_server_ip.py', server_ip]) else: proc = CsProcess(['/opt/cloud/bin/passwd_server_ip.py', server_ip + "," + guest_ip]) if proc.find(): url = "http://%s:8080/" % server_ip payload = {"ip": vm_ip, "password": password, "token": token} data = urllib.parse.urlencode(payload).encode() request = urllib.request.Request(url, data=data, headers={"DomU_Request": "save_password"}) try: resp = urllib.request.urlopen(request, data) logging.debug("Update password server result: http:%s, content:%s" % (resp.code, resp.read())) except Exception as e: logging.error("Failed to update password server due to: %s" % e) class CsAcl(CsDataBag): """ Deal with Network acls """ class AclIP(): """ For type Virtual Router """ def __init__(self, obj, fw): self.fw = fw.get_fw() self.direction = 'egress' if obj['traffic_type'] == 'Ingress': self.direction = 'ingress' self.device = '' self.ip = obj['src_ip'] self.rule = obj self.rule['type'] = obj['protocol'] # src_port_range if 'src_port_range' in obj: self.rule['first_port'] = obj['src_port_range'][0] self.rule['last_port'] = obj['src_port_range'][1] self.rule['allowed'] = True self.rule['action'] = "ACCEPT" if self.rule['type'] == 'all' and not obj['source_cidr_list']: self.rule['cidr'] = [] else: self.rule['cidr'] = obj['source_cidr_list'] if self.direction == 'egress': try: if not obj['dest_cidr_list']: self.rule['dcidr'] = [] else: self.rule['dcidr'] = obj['dest_cidr_list'] except Exception: self.rule['dcidr'] = [] logging.debug("AclIP created for rule ==> %s", self.rule) def create(self): self.add_rule() def add_rule(self): CIDR_ALL = '0.0.0.0/0' icmp_type = '' rule = self.rule icmp_type = "any" if "icmp_type" in list(self.rule.keys()) and self.rule['icmp_type'] != -1: icmp_type = self.rule['icmp_type'] if "icmp_code" in list(self.rule.keys()) and rule['icmp_code'] != -1: icmp_type = "%s/%s" % (self.rule['icmp_type'], self.rule['icmp_code']) rnge = '' if "first_port" in list(self.rule.keys()) and \ self.rule['first_port'] == self.rule['last_port']: rnge = " --dport %s " % self.rule['first_port'] if "first_port" in list(self.rule.keys()) and \ self.rule['first_port'] != self.rule['last_port']: rnge = " --dport %s:%s" % (rule['first_port'], rule['last_port']) logging.debug("Current ACL IP direction is ==> %s", self.direction) if self.direction == 'ingress': for cidr in self.rule['cidr']: action = self.rule['action'] if action == "ACCEPT": action = "RETURN" if rule['protocol'] == "icmp": self.fw.append(["mangle", "front", " -A FIREWALL_%s" % self.ip + " -s %s " % cidr + " -p %s " % rule['protocol'] + " --icmp-type %s -j %s" % (icmp_type, action)]) else: self.fw.append(["mangle", "front", " -A FIREWALL_%s" % self.ip + " -s %s " % cidr + " -p %s " % rule['protocol'] + " -m %s " % rule['protocol'] + " %s -j %s" % (rnge, action)]) sflag = False dflag = False if self.direction == 'egress': ruleId = self.rule['id'] sourceIpsetName = 'sourceCidrIpset-%d' % ruleId destIpsetName = 'destCidrIpset-%d' % ruleId # Create source cidr ipset srcIpset = 'ipset create '+sourceIpsetName + ' hash:net ' dstIpset = 'ipset create '+destIpsetName + ' hash:net ' CsHelper.execute(srcIpset) CsHelper.execute(dstIpset) for cidr in self.rule['cidr']: ipsetAddCmd = 'ipset add ' + sourceIpsetName + ' ' + cidr CsHelper.execute(ipsetAddCmd) sflag = True logging.debug("egress rule ####==> %s", self.rule) for cidr in self.rule['dcidr']: if cidr == CIDR_ALL: continue ipsetAddCmd = 'ipset add ' + destIpsetName + ' ' + cidr CsHelper.execute(ipsetAddCmd) dflag = True self.fw.append(["filter", "", " -A FW_OUTBOUND -j FW_EGRESS_RULES"]) fwr = " -I FW_EGRESS_RULES" # In case we have a default rule (accept all or drop all), we have to evaluate the action again. if rule['type'] == 'all' and not rule['source_cidr_list']: fwr = " -A FW_EGRESS_RULES" # For default egress ALLOW or DENY, the logic is inverted. # Having default_egress_policy == True, means that the default rule should have ACCEPT, # otherwise DROP. The rule should be appended, not inserted. if self.rule['default_egress_policy']: self.rule['action'] = "ACCEPT" else: self.rule['action'] = "DROP" else: # For other rules added, if default_egress_policy == True, following rules should be DROP, # otherwise ACCEPT if self.rule['default_egress_policy']: self.rule['action'] = "DROP" else: self.rule['action'] = "ACCEPT" egressIpsetStr = '' if sflag and dflag: egressIpsetStr = " -m set --match-set %s src " % sourceIpsetName + \ " -m set --match-set %s dst " % destIpsetName elif sflag: egressIpsetStr = " -m set --match-set %s src " % sourceIpsetName elif dflag: egressIpsetStr = " -m set --match-set %s dst " % destIpsetName if rule['protocol'] == "icmp": fwr += egressIpsetStr + " -p %s " % rule['protocol'] + " -m %s " % rule['protocol'] + \ " --icmp-type %s" % icmp_type elif rule['protocol'] != "all": fwr += egressIpsetStr + " -p %s " % rule['protocol'] + " -m %s " % rule['protocol'] + \ " %s" % rnge elif rule['protocol'] == "all": fwr += egressIpsetStr self.fw.append(["filter", "", "%s -j %s" % (fwr, rule['action'])]) logging.debug("EGRESS rule configured for protocol ==> %s, action ==> %s", rule['protocol'], rule['action']) class AclDevice(): """ A little class for each list of acls per device """ FIXED_RULES_INGRESS = 3 FIXED_RULES_EGRESS = 3 def __init__(self, obj, config): self.ingess = [] self.egress = [] self.device = obj['device'] self.ip = obj['nic_ip'] self.ip6_cidr = None if "nic_ip6_cidr" in list(obj.keys()): self.ip6_cidr = obj['nic_ip6_cidr'] self.netmask = obj['nic_netmask'] self.config = config self.cidr = "%s/%s" % (self.ip, self.netmask) if "ingress_rules" in list(obj.keys()): self.ingress = obj['ingress_rules'] if "egress_rules" in list(obj.keys()): self.egress = obj['egress_rules'] self.fw = config.get_fw() self.ipv6_acl = config.get_ipv6_acl() def create(self): self.process("ingress", self.ingress, self.FIXED_RULES_INGRESS) self.process("egress", self.egress, self.FIXED_RULES_EGRESS) def __process_ip6(self, direction, rule_list): if not self.ip6_cidr: return tier_cidr = self.ip6_cidr chain = "%s_%s_policy" % (self.device, direction) parent_chain = "acl_forward" cidr_key = "saddr" if direction == "ingress": cidr_key = "daddr" parent_chain_rule = "ip6 %s %s jump %s" % (cidr_key, tier_cidr, chain) self.ipv6_acl.insert(0, {'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) self.ipv6_acl.insert(0, {'type': "chain", 'chain': chain}) for rule in rule_list: cidr = rule['cidr'] if cidr is not None and cidr != "": cidr = removeUndesiredCidrs(cidr, 4) if cidr is None or cidr == "": continue addr = "" if cidr: addr = "ip6 daddr " + cidr if direction == "ingress": addr = "ip6 saddr " + cidr proto = "" protocol = rule['type'] if protocol != "all": icmp_type = "" if protocol == "protocol": protocol = "ip6 nexthdr %d" % rule['protocol'] proto = protocol if proto == "icmp": proto = proto_str = "icmpv6" icmp_type = ICMPV6_TYPE_ANY if 'icmp_type' in rule and rule['icmp_type'] != -1: icmp_type = str(rule['icmp_type']) proto = "%s type %s" % (proto_str, icmp_type) if 'icmp_code' in rule and rule['icmp_code'] != -1: proto = "%s %s code %d" % (proto, proto_str, rule['icmp_code']) first_port = "" last_port = "" if 'first_port' in rule: first_port = rule['first_port'] if 'last_port' in rule: last_port = rule['last_port'] port = "" if first_port: port = first_port if last_port and port and \ last_port != first_port: port = "{%s-%s}" % (port, last_port) if (protocol == "tcp" or protocol == "udp") and not port: port = TCP_UDP_PORT_ANY if port: proto = "%s dport %s" % (proto, port) action = "drop" if 'allowed' in list(rule.keys()) and rule['allowed']: action = "accept" rstr = addr type = "" rstr = appendStringIfNotEmpty(rstr, proto) if rstr and action: rstr = rstr + " " + action else: type = "chain" rstr = action logging.debug("Process IPv6 ACL rule %s" % rstr) if type == "chain": self.ipv6_acl.insert(0, {'type': type, 'chain': chain, 'rule': rstr}) else: self.ipv6_acl.append({'type': type, 'chain': chain, 'rule': rstr}) rstr = "counter packets 0 bytes 0 drop" self.ipv6_acl.append({'type': "", 'chain': chain, 'rule': rstr}) def process(self, direction, rule_list, base): count = base for i in rule_list: ruleData = copy.copy(i) cidr = ruleData['cidr'] if cidr is not None and cidr != "": cidr = removeUndesiredCidrs(cidr, 6) if cidr is None or cidr == "": continue ruleData['cidr'] = cidr r = self.AclRule(direction, self, ruleData, self.config, count) r.create() count += 1 # Prepare IPv6 ACL rules self.__process_ip6(direction, rule_list) class AclRule(): def __init__(self, direction, acl, rule, config, count): self.count = count if config.is_vpc(): self.init_vpc(direction, acl, rule, config) def init_vpc(self, direction, acl, rule, config): self.table = "" self.device = acl.device self.direction = direction # acl is an object of the AclDevice type. So, its fw attribute is already a list. self.fw = acl.fw self.chain = config.get_ingress_chain(self.device, acl.ip) self.dest = "-s %s" % rule['cidr'] if direction == "egress": self.table = config.get_egress_table() self.chain = config.get_egress_chain(self.device, acl.ip) self.dest = "-d %s" % rule['cidr'] self.type = "" self.type = rule['type'] self.icmp_type = "any" self.protocol = self.type if "icmp_type" in list(rule.keys()) and rule['icmp_type'] != -1: self.icmp_type = rule['icmp_type'] if "icmp_code" in list(rule.keys()) and rule['icmp_code'] != -1: self.icmp_type = "%s/%s" % (self.icmp_type, rule['icmp_code']) if self.type == "protocol": if rule['protocol'] == 41: rule['protocol'] = "ipv6" self.protocol = rule['protocol'] self.action = "DROP" self.dport = "" if 'allowed' in list(rule.keys()) and rule['allowed']: self.action = "ACCEPT" if 'first_port' in list(rule.keys()): self.dport = "-m %s --dport %s" % (self.protocol, rule['first_port']) if 'last_port' in list(rule.keys()) and self.dport and \ rule['last_port'] != rule['first_port']: self.dport = "%s:%s" % (self.dport, rule['last_port']) def create(self): rstr = "" rstr = "%s -A %s -p %s %s" % (rstr, self.chain, self.protocol, self.dest) if self.type == "icmp": rstr = "%s -m icmp --icmp-type %s" % (rstr, self.icmp_type) rstr = "%s %s -j %s" % (rstr, self.dport, self.action) rstr = rstr.replace(" ", " ").lstrip() self.fw.append([self.table, self.count, rstr]) def flushAllowAllEgressRules(self): logging.debug("Flush allow 'all' egress firewall rule") # Ensure that FW_EGRESS_RULES chain exists CsHelper.execute("iptables-save | grep '^:FW_EGRESS_RULES' || iptables -t filter -N FW_EGRESS_RULES") CsHelper.execute("iptables-save | grep '^-A FW_EGRESS_RULES -j ACCEPT$' | sed 's/^-A/iptables -t filter -D/g' | bash") CsHelper.execute("iptables -F FW_EGRESS_RULES") CsHelper.execute("ipset -L | grep Name: | awk {'print $2'} | ipset flush") CsHelper.execute("ipset -L | grep Name: | awk {'print $2'} | ipset destroy") def flushAllIpv6Rules(self): logging.info("Flush all IPv6 ACL rules") address_family = 'ip6' table = 'ip6_acl' tables = CsHelper.execute("nft list tables %s | grep %s" % (address_family, table)) if any(table in t for t in tables): CsHelper.execute("nft delete table %s %s" % (address_family, table)) def process(self): for item in self.dbag: if item == "id": continue if self.config.is_vpc(): self.AclDevice(self.dbag[item], self.config).create() else: self.AclIP(self.dbag[item], self.config).create() class CsIpv6Firewall(CsDataBag): """ Deal with IPv6 Firewall """ def flushAllRules(self): logging.info("Flush all IPv6 firewall rules") address_family = 'ip6' table = 'ip6_firewall' tables = CsHelper.execute("nft list tables %s | grep %s" % (address_family, table)) if any(table in t for t in tables): CsHelper.execute("nft delete table %s %s" % (address_family, table)) def process(self): fw = self.config.get_ipv6_fw() logging.info("Processing IPv6 firewall rules %s; %s" % (self.dbag, fw)) chains_added = False egress_policy = None for item in self.dbag: if item == "id": continue rule = self.dbag[item] if chains_added is False: guest_cidr = rule['guest_ip6_cidr'] parent_chain = "fw_forward" chain = "fw_chain_egress" parent_chain_rule = "ip6 saddr %s jump %s" % (guest_cidr, chain) fw.append({'type': "chain", 'chain': chain}) fw.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) chain = "fw_chain_ingress" parent_chain_rule = "ip6 daddr %s jump %s" % (guest_cidr, chain) fw.append({'type': "chain", 'chain': chain}) fw.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) if rule['default_egress_policy']: egress_policy = "accept" else: egress_policy = "drop" chains_added = True rstr = "" chain = "fw_chain_ingress" if 'traffic_type' in rule and rule['traffic_type'].lower() == "egress": chain = "fw_chain_egress" saddr = "" if 'source_cidr_list' in rule and len(rule['source_cidr_list']) > 0: source_cidrs = rule['source_cidr_list'] if len(source_cidrs) == 1: source_cidrs = source_cidrs[0] else: source_cidrs = "{" + (",".join(source_cidrs)) + "}" saddr = "ip6 saddr " + source_cidrs daddr = "" if 'dest_cidr_list' in rule and len(rule['dest_cidr_list']) > 0: dest_cidrs = rule['dest_cidr_list'] if len(dest_cidrs) == 1: dest_cidrs = dest_cidrs[0] else: dest_cidrs = "{" + (",".join(dest_cidrs)) + "}" daddr = "ip6 daddr " + dest_cidrs proto = "" protocol = rule['protocol'] if protocol != "all": icmp_type = "" proto = protocol if proto == "icmp": proto = proto_str = "icmpv6" icmp_type = ICMPV6_TYPE_ANY if 'icmp_type' in rule and rule['icmp_type'] != -1: icmp_type = str(rule['icmp_type']) proto = "%s type %s" % (proto_str, icmp_type) if 'icmp_code' in rule and rule['icmp_code'] != -1: proto = "%s %s code %d" % (proto, proto_str, rule['icmp_code']) first_port = "" last_port = "" if 'src_port_range' in rule: first_port = rule['src_port_range'][0] last_port = rule['src_port_range'][1] port = "" if first_port: port = first_port if last_port and port and \ last_port != first_port: port = "{%s-%s}" % (port, last_port) if (protocol == "tcp" or protocol == "udp") and not port: port = TCP_UDP_PORT_ANY if port: proto = "%s dport %s" % (proto, port) action = "accept" if chain == "fw_chain_egress": # In case we have a default rule (accept all or drop all), we have to evaluate the action again. if protocol == 'all' and not rule['source_cidr_list']: # For default egress ALLOW or DENY, the logic is inverted. # Having default_egress_policy == True, means that the default rule should have ACCEPT, # otherwise DROP. The rule should be appended, not inserted. if rule['default_egress_policy']: action = "accept" else: action = "drop" else: # For other rules added, if default_egress_policy == True, following rules should be DROP, # otherwise ACCEPT if rule['default_egress_policy']: action = "drop" else: action = "accept" rstr = saddr type = "" rstr = appendStringIfNotEmpty(rstr, daddr) rstr = appendStringIfNotEmpty(rstr, proto) if rstr and action: rstr = rstr + " " + action logging.debug("Process IPv6 firewall rule %s" % rstr) fw.append({'type': type, 'chain': chain, 'rule': rstr}) if chains_added: base_rstr = "counter packets 0 bytes 0" rstr = "%s drop" % base_rstr fw.append({'type': "", 'chain': "fw_chain_ingress", 'rule': rstr}) rstr = "%s %s" % (base_rstr, egress_policy) fw.append({'type': "", 'chain': "fw_chain_egress", 'rule': rstr}) class CsVmMetadata(CsDataBag): def process(self): for ip in self.dbag: if ("id" == ip): continue logging.info("Processing metadata for %s" % ip) for item in self.dbag[ip]: folder = item[0] file = item[1] data = item[2] # process only valid data if folder != "userdata" and folder != "metadata": continue if file == "": continue self.__htaccess(ip, folder, file) if data == "": self.__deletefile(ip, folder, file) else: self.__createfile(ip, folder, file, data) def __deletefile(self, ip, folder, file): datafile = "/var/www/html/" + folder + "/" + ip + "/" + file if os.path.exists(datafile): os.remove(datafile) def __createfile(self, ip, folder, file, data): dest = "/var/www/html/" + folder + "/" + ip + "/" + file metamanifestdir = "/var/www/html/" + folder + "/" + ip metamanifest = metamanifestdir + "/meta-data" # base64 decode userdata if folder == "userdata" or folder == "user-data": if data is not None: # need to pad data if it is not valid base 64 if len(data) % 4 != 0: data += (4 - (len(data) % 4)) * "=" data = base64.b64decode(data) fh = open(dest, "w") self.__exflock(fh) if data is not None: if isinstance(data, str): fh.write(data) elif isinstance(data, bytes): fh.write(data.decode()) else: fh.write("") self.__unflock(fh) fh.close() os.chmod(dest, 0o644) if folder == "metadata" or folder == "meta-data": try: os.makedirs(metamanifestdir, 0o755) except OSError as e: # error 17 is already exists, we do it this way for concurrency if e.errno != 17: print("failed to make directories " + metamanifestdir + " due to :" + e.strerror) sys.exit(1) if os.path.exists(metamanifest): fh = open(metamanifest, "a+") self.__exflock(fh) if file not in fh.read(): fh.write(file + '\n') self.__unflock(fh) fh.close() else: fh = open(metamanifest, "w") self.__exflock(fh) fh.write(file + '\n') self.__unflock(fh) fh.close() if os.path.exists(metamanifest): os.chmod(metamanifest, 0o644) def __htaccess(self, ip, folder, file): entry = "RewriteRule ^" + file + "$ ../" + folder + "/%{REMOTE_ADDR}/" + file + " [L,NC,QSA]" htaccessFolder = "/var/www/html/latest" htaccessFile = htaccessFolder + "/.htaccess" CsHelper.mkdir(htaccessFolder, 0o755, True) if os.path.exists(htaccessFile): fh = open(htaccessFile, "a+") self.__exflock(fh) if entry not in fh.read(): fh.write(entry + '\n') self.__unflock(fh) fh.close() else: fh = open(htaccessFile, "w") self.__exflock(fh) fh.write("Options +FollowSymLinks\nRewriteEngine On\n\n") fh.write(entry + '\n') self.__unflock(fh) fh.close() entry = "Options -Indexes\nOrder Deny,Allow\nDeny from all\nAllow from " + ip htaccessFolder = "/var/www/html/" + folder + "/" + ip htaccessFile = htaccessFolder+"/.htaccess" try: os.makedirs(htaccessFolder, 0o755) except OSError as e: # error 17 is already exists, we do it this way for sake of concurrency if e.errno != 17: print("failed to make directories " + htaccessFolder + " due to :" + e.strerror) sys.exit(1) fh = open(htaccessFile, "w") self.__exflock(fh) fh.write(entry + '\n') self.__unflock(fh) fh.close() if folder == "metadata" or folder == "meta-data": entry = "RewriteRule ^meta-data/(.+)$ ../" + folder + "/%{REMOTE_ADDR}/$1 [L,NC,QSA]" htaccessFolder = "/var/www/html/latest" htaccessFile = htaccessFolder + "/.htaccess" fh = open(htaccessFile, "a+") self.__exflock(fh) if entry not in fh.read(): fh.write(entry + '\n') entry = "RewriteRule ^meta-data/$ ../" + folder + "/%{REMOTE_ADDR}/meta-data [L,NC,QSA]" fh.seek(0) if entry not in fh.read(): fh.write(entry + '\n') self.__unflock(fh) fh.close() def __exflock(self, file): try: flock(file, LOCK_EX) except IOError as e: print("failed to lock file" + file.name + " due to : " + e.strerror) sys.exit(1) # FIXME return True def __unflock(self, file): try: flock(file, LOCK_UN) except IOError as e: print("failed to unlock file" + file.name + " due to : " + e.strerror) sys.exit(1) # FIXME return True class CsSite2SiteVpn(CsDataBag): """ Setup any configured vpns (using swan) left is the local machine right is where the clients connect from """ VPNCONFDIR = "/etc/ipsec.d" def process(self): self.confips = [] # collect a list of configured vpns for file in os.listdir(self.VPNCONFDIR): m = re.search("^ipsec.vpn-(.*).conf", file) if m: self.confips.append(m.group(1)) for vpn in self.dbag: if vpn == "id": continue local_ip = self.dbag[vpn]['local_public_ip'] dev = CsHelper.get_device(local_ip) if dev == "": logging.error("Request for ipsec to %s not possible because ip is not configured", local_ip) continue CsHelper.start_if_stopped("ipsec") self.configure_iptables(dev, self.dbag[vpn]) self.configure_ipsec(self.dbag[vpn]) # Delete vpns that are no longer in the configuration for ip in self.confips: self.deletevpn(ip) def deletevpn(self, ip): logging.info("Removing VPN configuration for %s", ip) CsHelper.execute("ipsec down vpn-%s" % ip) CsHelper.execute("ipsec down vpn-%s" % ip) vpnconffile = "%s/ipsec.vpn-%s.conf" % (self.VPNCONFDIR, ip) vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, ip) os.remove(vpnconffile) os.remove(vpnsecretsfile) CsHelper.execute("ipsec reload") def configure_iptables(self, dev, obj): self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) self.fw.append(["", "front", "-A INPUT -i %s -p esp -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) self.fw.append(["nat", "front", "-A POSTROUTING -t nat -o %s -m mark --mark 0x525 -j ACCEPT" % dev]) for net in obj['peer_guest_cidr_list'].lstrip().rstrip().split(','): self.fw.append(["mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) self.fw.append(["mangle", "", "-A OUTPUT -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) self.fw.append(["mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr'])]) self.fw.append(["mangle", "", "-A INPUT -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr'])]) def configure_ipsec(self, obj): leftpeer = obj['local_public_ip'] rightpeer = obj['peer_gateway_ip'] peerlist = obj['peer_guest_cidr_list'].replace(' ', '') vpnconffile = "%s/ipsec.vpn-%s.conf" % (self.VPNCONFDIR, rightpeer) vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, rightpeer) ikepolicy = obj['ike_policy'].replace(';', '-') esppolicy = obj['esp_policy'].replace(';', '-') splitconnections = obj['split_connections'] if 'split_connections' in obj else False ikeversion = obj['ike_version'] if 'ike_version' in obj and obj['ike_version'].lower() in ('ike', 'ikev1', 'ikev2') else 'ike' peerlistarr = peerlist.split(',') if splitconnections: logging.debug('Splitting rightsubnets %s' % peerlistarr) peerlist = peerlistarr[0] if rightpeer in self.confips: self.confips.remove(rightpeer) file = CsFile(vpnconffile) file.repopulate() # This avoids issues when switching off split_connections or removing subnets with split_connections == true file.add("#conn for vpn-%s" % rightpeer, 0) file.search("conn ", "conn vpn-%s" % rightpeer) file.addeq(" left=%s" % leftpeer) file.addeq(" leftsubnet=%s" % obj['local_guest_cidr']) file.addeq(" right=%s" % rightpeer) file.addeq(" rightsubnet=%s" % peerlist) file.addeq(" type=tunnel") file.addeq(" authby=secret") file.addeq(" keyexchange=%s" % ikeversion) file.addeq(" ike=%s" % ikepolicy) file.addeq(" ikelifetime=%s" % self.convert_sec_to_min(obj['ike_lifetime'])) file.addeq(" esp=%s" % esppolicy) file.addeq(" lifetime=%s" % self.convert_sec_to_min(obj['esp_lifetime'])) file.addeq(" keyingtries=2") file.addeq(" auto=route") if 'encap' not in obj: obj['encap'] = False file.addeq(" forceencaps=%s" % CsHelper.bool_to_yn(obj['encap'])) if obj['dpd']: file.addeq(" dpddelay=30") file.addeq(" dpdtimeout=120") file.addeq(" dpdaction=restart") if splitconnections and peerlistarr.count > 1: logging.debug('Splitting connections for rightsubnets %s' % peerlistarr) for peeridx in range(1, len(peerlistarr)): logging.debug('Adding split connection -%d for subnet %s' % (peeridx + 1, peerlistarr[peeridx])) file.append('') file.search('conn vpn-.*-%d' % (peeridx + 1), "conn vpn-%s-%d" % (rightpeer, peeridx + 1)) file.append(' also=vpn-%s' % rightpeer) file.append(' rightsubnet=%s' % peerlistarr[peeridx]) secret = CsFile(vpnsecretsfile) secret.search("%s " % leftpeer, "%s %s : PSK \"%s\"" % (leftpeer, rightpeer, obj['ipsec_psk'])) if secret.is_changed() or file.is_changed(): secret.commit() file.commit() logging.info("Configured vpn %s %s", leftpeer, rightpeer) CsHelper.execute("ipsec rereadsecrets") # This will load the new config CsHelper.execute("ipsec reload") os.chmod(vpnsecretsfile, 0o400) for i in range(3): done = True for peeridx in range(0, len(peerlistarr)): # Check for the proper connection and subnet conn = rightpeer if not splitconnections else rightpeer if peeridx == 0 else '%s-%d' % (rightpeer, peeridx + 1) result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % (conn, peerlistarr[peeridx])) # If any of the peers hasn't yet finished, continue the outer loop if len(result) == 0: done = False if done: break time.sleep(1) # With 'auto=route', connections are established on an attempt to # communicate over the S2S VPN. This uses ping to initialize the connection. for peer in peerlistarr: octets = peer.split('/', 1)[0].split('.') octets[3] = str((int(octets[3]) + 1)) ipinsubnet = '.'.join(octets) CsHelper.execute("timeout 5 ping -c 3 %s" % ipinsubnet) def convert_sec_to_min(self, val): mins = int(val / 60) return "%sm" % mins class CsVpnUser(CsDataBag): PPP_CHAP = '/etc/ppp/chap-secrets' def process(self): for user in self.dbag: if user == 'id': continue userconfig = self.dbag[user] if userconfig['add']: self.add_l2tp_ipsec_user(user, userconfig) else: self.del_l2tp_ipsec_user(user, userconfig) def add_l2tp_ipsec_user(self, user, obj): userfound = False password = obj['password'] userAddEntry = "%s * %s *" % (user, password) logging.debug("Adding vpn user '%s'" % user) file = CsFile(self.PPP_CHAP) userfound = file.searchString(userAddEntry, '#') if not userfound: logging.debug("User is not there already, so adding user") self.del_l2tp_ipsec_user(user, obj) file.add(userAddEntry) file.commit() def del_l2tp_ipsec_user(self, user, obj): userfound = False password = obj['password'] userentry = "%s * %s *" % (user, password) logging.debug("Deleting the user '%s'" % user) file = CsFile(self.PPP_CHAP) file.deleteLine(userentry) file.commit() if not os.path.exists('/var/run/pppd2.tdb'): return logging.debug("killing the PPPD process for the user '%s'" % user) fileContents = CsHelper.execute("tdbdump /var/run/pppd2.tdb") for line in fileContents: if user in line: contentlist = line.split(';') for str in contentlist: pppd = str.split('=')[0] if pppd == 'PPPD_PID': pid = str.split('=')[1] if pid: logging.debug("killing process %s" % pid) CsHelper.execute('kill -9 %s' % pid) class CsRemoteAccessVpn(CsDataBag): VPNCONFDIR = "/etc/ipsec.d" def process(self): self.confips = [] logging.debug(self.dbag) for public_ip in self.dbag: if public_ip == "id": continue vpnconfig = self.dbag[public_ip] # Enable remote access vpn if vpnconfig['create']: logging.debug("Enabling remote access vpn on " + public_ip) CsHelper.start_if_stopped("ipsec") self.configure_l2tpIpsec(public_ip, self.dbag[public_ip]) logging.debug("Remote accessvpn data bag %s", self.dbag) self.remoteaccessvpn_iptables(public_ip, self.dbag[public_ip]) CsHelper.execute("ipsec update") CsHelper.execute("systemctl start xl2tpd") CsHelper.execute("ipsec rereadsecrets") else: logging.debug("Disabling remote access vpn .....") CsHelper.execute("ipsec down L2TP-PSK") CsHelper.execute("systemctl stop xl2tpd") def configure_l2tpIpsec(self, left, obj): l2tpconffile = "%s/l2tp.conf" % (self.VPNCONFDIR) vpnsecretfilte = "%s/ipsec.any.secrets" % (self.VPNCONFDIR) xl2tpdconffile = "/etc/xl2tpd/xl2tpd.conf" xl2tpoptionsfile = "/etc/ppp/options.xl2tpd" localip = obj['local_ip'] localcidr = obj['local_cidr'] publicIface = obj['public_interface'] iprange = obj['ip_range'] psk = obj['preshared_key'] # Left l2tpfile = CsFile(l2tpconffile) l2tpfile.addeq(" left=%s" % left) l2tpfile.commit() secret = CsFile(vpnsecretfilte) secret.empty() secret.addeq(": PSK \"%s\"" % (psk)) secret.commit() xl2tpdconf = CsFile(xl2tpdconffile) xl2tpdconf.addeq("ip range = %s" % iprange) xl2tpdconf.addeq("local ip = %s" % localip) xl2tpdconf.commit() xl2tpoptions = CsFile(xl2tpoptionsfile) xl2tpoptions.search("ms-dns ", "ms-dns %s" % localip) xl2tpoptions.commit() def remoteaccessvpn_iptables(self, publicip, obj): publicdev = obj['public_interface'] localcidr = obj['local_cidr'] local_ip = obj['local_ip'] self.fw.append(["", "", "-A INPUT -i %s --dst %s -p udp -m udp --dport 500 -j ACCEPT" % (publicdev, publicip)]) self.fw.append(["", "", "-A INPUT -i %s --dst %s -p udp -m udp --dport 4500 -j ACCEPT" % (publicdev, publicip)]) self.fw.append(["", "", "-A INPUT -i %s --dst %s -p udp -m udp --dport 1701 -j ACCEPT" % (publicdev, publicip)]) self.fw.append(["", "", "-A INPUT -i %s -p ah -j ACCEPT" % publicdev]) self.fw.append(["", "", "-A INPUT -i %s -p esp -j ACCEPT" % publicdev]) self.fw.append(["", "", "-A OUTPUT -p ah -j ACCEPT"]) self.fw.append(["", "", "-A OUTPUT -p esp -j ACCEPT"]) if self.config.is_vpc(): self.fw.append(["", "", " -N VPN_FORWARD"]) self.fw.append(["", "", "-I FORWARD -i ppp+ -j VPN_FORWARD"]) self.fw.append(["", "", "-I FORWARD -o ppp+ -j VPN_FORWARD"]) self.fw.append(["", "", "-I FORWARD -o ppp+ -j VPN_FORWARD"]) self.fw.append(["", "", "-A VPN_FORWARD -s %s -j RETURN" % localcidr]) self.fw.append(["", "", "-A VPN_FORWARD -i ppp+ -d %s -j RETURN" % localcidr]) self.fw.append(["", "", "-A VPN_FORWARD -i ppp+ -o ppp+ -j RETURN"]) else: self.fw.append(["", "", "-A FORWARD -i ppp+ -o ppp+ -j ACCEPT"]) self.fw.append(["", "", "-A FORWARD -s %s -o ppp+ -j ACCEPT" % localcidr]) self.fw.append(["", "", "-A FORWARD -i ppp+ -d %s -j ACCEPT" % localcidr]) self.fw.append(["", "", "-A INPUT -i ppp+ -m udp -p udp --dport 53 -j ACCEPT"]) self.fw.append(["", "", "-A INPUT -i ppp+ -m tcp -p tcp --dport 53 -j ACCEPT"]) self.fw.append(["nat", "", "-I PREROUTING -i ppp+ -p tcp -m tcp --dport 53 -j DNAT --to-destination %s" % local_ip]) if self.config.is_vpc(): return self.fw.append(["mangle", "", "-N VPN_%s " % publicip]) self.fw.append(["mangle", "", "-A VPN_%s -j RETURN " % publicip]) self.fw.append(["mangle", "", "-I VPN_%s -p ah -j ACCEPT " % publicip]) self.fw.append(["mangle", "", "-I VPN_%s -p esp -j ACCEPT " % publicip]) self.fw.append(["mangle", "", "-I PREROUTING -d %s -j VPN_%s " % (publicip, publicip)]) class CsForwardingRules(CsDataBag): def process(self): for public_ip in self.dbag: if public_ip == "id": continue for rule in self.dbag[public_ip]: if rule["type"] == "forward": self.processForwardRule(rule) elif rule["type"] == "staticnat": self.processStaticNatRule(rule) # Return the VR guest interface ip def getGuestIp(self): interfaces = [] ipAddr = None for interface in self.config.address().get_interfaces(): if interface.is_guest(): interfaces.append(interface) if len(interfaces) > 0: ipAddr = sorted(interfaces)[-1] if ipAddr: return ipAddr.get_ip() return None def getGuestIpByIp(self, ipa): for interface in self.config.address().get_interfaces(): if interface.ip_in_subnet(ipa): return interface.get_ip() return None def getDeviceByIp(self, ipa): for interface in self.config.address().get_interfaces(): if interface.ip_in_subnet(ipa): return interface.get_device() return None def getNetworkByIp(self, ipa): for interface in self.config.address().get_interfaces(): if interface.ip_in_subnet(ipa): return interface.get_network() return None def getGatewayByIp(self, ipa): for interface in self.config.address().get_interfaces(): if interface.ip_in_subnet(ipa): return interface.get_gateway() return None def getPrivateGatewayNetworks(self): interfaces = [] for interface in self.config.address().get_interfaces(): if interface.is_private_gateway(): interfaces.append(interface) return interfaces def getStaticRoutes(self): static_routes = CsStaticRoutes("staticroutes", self.config) routes = [] if not static_routes: return routes for item in static_routes.get_bag(): if item == "id": continue static_route = static_routes.get_bag()[item] if static_route['revoke']: continue routes.append(static_route) return routes def portsToString(self, ports, delimiter): ports_parts = ports.split(":", 2) if ports_parts[0] == ports_parts[1]: return str(ports_parts[0]) else: return "%s%s%s" % (ports_parts[0], delimiter, ports_parts[1]) def processForwardRule(self, rule): if self.config.is_vpc(): self.forward_vpc(rule) else: self.forward_vr(rule) def forward_vr(self, rule): # Prefetch iptables variables public_fwinterface = self.getDeviceByIp(rule['public_ip']) internal_fwinterface = self.getDeviceByIp(rule['internal_ip']) public_fwports = self.portsToString(rule['public_ports'], ':') internal_fwports = self.portsToString(rule['internal_ports'], '-') fw1 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ ( rule['public_ip'], public_fwinterface, rule['protocol'], rule['protocol'], public_fwports, rule['internal_ip'], internal_fwports ) fw2 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ ( rule['public_ip'], internal_fwinterface, rule['protocol'], rule['protocol'], public_fwports, rule['internal_ip'], internal_fwports ) fw3 = "-A OUTPUT -d %s/32 -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ ( rule['public_ip'], rule['protocol'], rule['protocol'], public_fwports, rule['internal_ip'], internal_fwports ) fw4 = "-j SNAT --to-source %s -A POSTROUTING -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ ( self.getGuestIp(), self.getNetworkByIp(rule['internal_ip']), rule['internal_ip'], internal_fwinterface, rule['protocol'], rule['protocol'], self.portsToString(rule['internal_ports'], ':') ) fw5 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j MARK --set-xmark %s/0xffffffff" % \ ( rule['public_ip'], public_fwinterface, rule['protocol'], rule['protocol'], public_fwports, hex(100 + int(public_fwinterface[3:])) ) fw6 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -m state --state NEW -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff" % \ ( rule['public_ip'], public_fwinterface, rule['protocol'], rule['protocol'], public_fwports, ) fw7 = "-A FORWARD -i %s -o %s -p %s -m %s --dport %s -m state --state NEW,ESTABLISHED -j ACCEPT" % \ ( public_fwinterface, internal_fwinterface, rule['protocol'], rule['protocol'], self.portsToString(rule['internal_ports'], ':') ) self.fw.append(["nat", "", fw1]) self.fw.append(["nat", "", fw2]) self.fw.append(["nat", "", fw3]) self.fw.append(["nat", "", fw4]) self.fw.append(["nat", "", fw5]) self.fw.append(["nat", "", fw6]) self.fw.append(["filter", "", fw7]) def forward_vpc(self, rule): fw_prerout_rule = "-A PREROUTING -d %s/32 " % (rule["public_ip"]) if not rule["protocol"] == "any": fw_prerout_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"]) if not rule["public_ports"] == "any": fw_prerout_rule += " --dport %s" % self.portsToString(rule["public_ports"], ":") fw_prerout_rule += " -j DNAT --to-destination %s" % rule["internal_ip"] if not rule["internal_ports"] == "any": fw_prerout_rule += ":" + self.portsToString(rule["internal_ports"], "-") fw_output_rule = "-A OUTPUT -d %s/32" % rule["public_ip"] if not rule["protocol"] == "any": fw_output_rule += " -m %s -p %s" % (rule["protocol"], rule["protocol"]) if not rule["public_ports"] == "any": fw_output_rule += " --dport %s" % self.portsToString(rule["public_ports"], ":") fw_output_rule += " -j DNAT --to-destination %s" % rule["internal_ip"] if not rule["internal_ports"] == "any": fw_output_rule += ":" + self.portsToString(rule["internal_ports"], "-") fw_postrout_rule2 = "-j SNAT --to-source %s -A POSTROUTING -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ ( self.getGuestIpByIp(rule['internal_ip']), self.getNetworkByIp(rule['internal_ip']), rule['internal_ip'], self.getDeviceByIp(rule['internal_ip']), rule['protocol'], rule['protocol'], self.portsToString(rule['internal_ports'], ':') ) self.fw.append(["nat", "", fw_prerout_rule]) self.fw.append(["nat", "", fw_postrout_rule2]) self.fw.append(["nat", "", fw_output_rule]) def processStaticNatRule(self, rule): # FIXME this needs ordering with the VPN no nat rule device = self.getDeviceByIp(rule["public_ip"]) if device is None: raise Exception("Ip address %s has no device in the ips databag" % rule["public_ip"]) chain_name = "PREROUTING-%s-def" % device self.fw.append(["mangle", "front", "-A PREROUTING -s %s/32 -m state --state NEW -j %s" % (rule["internal_ip"], chain_name)]) self.fw.append(["mangle", "", "-A %s -j MARK --set-xmark %s/0xffffffff" % (chain_name, hex(100 + int(device[len("eth"):])))]) self.fw.append(["mangle", "", "-A %s -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff" % chain_name]) private_gateways = self.getPrivateGatewayNetworks() for private_gw in private_gateways: self.fw.append(["mangle", "front", "-A %s -d %s -j RETURN" % (chain_name, private_gw.get_network())]) static_routes = self.getStaticRoutes() for static_route in static_routes: self.fw.append(["mangle", "front", "-A %s -d %s -j RETURN" % (chain_name, static_route['network'])]) self.fw.append(["nat", "front", "-A PREROUTING -d %s/32 -j DNAT --to-destination %s" % (rule["public_ip"], rule["internal_ip"])]) self.fw.append(["nat", "front", "-A POSTROUTING -o %s -s %s/32 -j SNAT --to-source %s" % (device, rule["internal_ip"], rule["public_ip"])]) self.fw.append(["nat", "front", "-A OUTPUT -d %s/32 -j DNAT --to-destination %s" % (rule["public_ip"], rule["internal_ip"])]) self.fw.append(["filter", "", "-A FORWARD -i %s -o eth0 -d %s -m state --state NEW -j ACCEPT " % (device, rule["internal_ip"])]) # Configure the hairpin snat self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" % (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))]) class IpTablesExecutor: config = None def __init__(self, config): self.config = config def process(self): acls = CsAcl('networkacl', self.config) acls.flushAllIpv6Rules() acls.process() acls = CsAcl('firewallrules', self.config) acls.flushAllowAllEgressRules() acls.process() ip6_fw = CsIpv6Firewall('ipv6firewallrules', self.config) ip6_fw.flushAllRules() ip6_fw.process() fwd = CsForwardingRules("forwardingrules", self.config) fwd.process() vpns = CsSite2SiteVpn("site2sitevpn", self.config) vpns.process() rvpn = CsRemoteAccessVpn("remoteaccessvpn", self.config) rvpn.process() lb = CsLoadBalancer("loadbalancer", self.config) lb.process() logging.debug("Configuring iptables rules") nf = CsNetfilters() nf.compare(self.config.get_fw()) logging.info("Configuring nftables ACL rules %s" % self.config.get_ipv6_acl()) nf = CsNetfilters() nf.apply_ip6_rules(self.config.get_ipv6_acl(), "acl") logging.info("Configuring nftables IPv6 rules %s" % self.config.get_ipv6_fw()) nf = CsNetfilters() nf.apply_ip6_rules(self.config.get_ipv6_fw(), "firewall") logging.debug("Configuring iptables rules done ...saving rules") # Save iptables configuration - will be loaded on reboot by the iptables-restore that is configured on /etc/rc.local CsHelper.save_iptables("iptables-save", "/etc/iptables/rules.v4") CsHelper.save_iptables("ip6tables-save", "/etc/iptables/rules.v6") def main(argv): # The file we are currently processing, if it is "cmd_line.json" everything will be processed. process_file = argv[1] if process_file is None: logging.debug("No file was received, do not go on processing the other actions. Just leave for now.") return json_type = os.path.basename(process_file).split('.json')[0] # The "GLOBAL" Configuration object config = CsConfig() # Load stored ip addresses from disk to CsConfig() config.set_address() logging.debug("Configuring ip addresses") config.address().compare() config.address().process() databag_map = OrderedDict([("guest_network", {"process_iptables": True, "executor": [CsVpcGuestNetwork("guestnetwork", config)]}), ("ip_aliases", {"process_iptables": True, "executor": []}), ("vm_password", {"process_iptables": False, "executor": [CsPassword("vmpassword", config)]}), ("vm_metadata", {"process_iptables": False, "executor": [CsVmMetadata('vmdata', config)]}), ("network_acl", {"process_iptables": True, "executor": []}), ("firewall_rules", {"process_iptables": True, "executor": []}), ("ipv6_firewall_rules", {"process_iptables": True, "executor": []}), ("forwarding_rules", {"process_iptables": True, "executor": []}), ("staticnat_rules", {"process_iptables": True, "executor": []}), ("site_2_site_vpn", {"process_iptables": True, "executor": []}), ("remote_access_vpn", {"process_iptables": True, "executor": []}), ("vpn_user_list", {"process_iptables": False, "executor": [CsVpnUser("vpnuserlist", config)]}), ("vm_dhcp_entry", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}), ("dhcp", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}), ("load_balancer", {"process_iptables": True, "executor": []}), ("monitor_service", {"process_iptables": False, "executor": [CsMonitor("monitorservice", config)]}), ("static_routes", {"process_iptables": False, "executor": [CsStaticRoutes("staticroutes", config)]}) ]) if not config.is_vpc(): databag_map.pop("guest_network") def execDatabag(key, db): if key not in list(db.keys()) or 'executor' not in db[key]: logging.warn("Unable to find config or executor(s) for the databag type %s" % key) return for executor in db[key]['executor']: logging.debug("Processing for databag type: %s" % key) executor.process() def execIptables(config): logging.debug("Processing iptables rules") iptables_executor = IpTablesExecutor(config) iptables_executor.process() if json_type == "cmd_line": logging.debug("cmd_line.json changed. All other files will be processed as well.") for key in list(databag_map.keys()): execDatabag(key, databag_map) execIptables(config) elif json_type in list(databag_map.keys()): execDatabag(json_type, databag_map) if databag_map[json_type]['process_iptables']: execIptables(config) else: logging.warn("Unable to find and process databag for file: %s, for json type=%s" % (process_file, json_type)) red = CsRedundant(config) red.set() return 0 if __name__ == "__main__": main(sys.argv)