| #!/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 | |
| import ipaddress | |
| 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 | |
| from cs.CsBgpPeers import CsBgpPeers | |
| ICMP_TYPE_ANY = "{ echo-reply, destination-unreachable, source-quench, redirect, echo-request, time-exceeded, \ | |
| parameter-problem, timestamp-request, timestamp-reply, info-request, info-reply, address-mask-request, \ | |
| address-mask-reply, router-advertisement, router-solicitation }" | |
| 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']) | |
| def add_routing_rules(self): | |
| fw = self.config.get_nft_ipv4_fw() | |
| logging.info("Processing routing 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] | |
| network = ipaddress.ip_network(self.config.cmdline().get_eth0_ip() + "/" + self.config.cmdline().get_cidr_size(), False) | |
| guest_cidr = network.with_prefixlen | |
| if chains_added is False: | |
| parent_chain = "FORWARD" | |
| chain = "fw_chain_egress" | |
| parent_chain_rule = "ip 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 = "ip 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 = "ip 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 = "ip daddr " + dest_cidrs | |
| proto = "" | |
| protocol = rule['protocol'] | |
| if protocol != "all": | |
| icmp_type = "" | |
| proto = protocol | |
| if proto == "icmp": | |
| proto = proto_str = "icmp" | |
| icmp_type = ICMP_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 routing 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 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() | |
| self.nft_ipv4_acl = config.get_nft_ipv4_acl() | |
| def create(self): | |
| self.process("ingress", self.ingress, self.FIXED_RULES_INGRESS, self.config.is_routed()) | |
| self.process("egress", self.egress, self.FIXED_RULES_EGRESS, self.config.is_routed()) | |
| def __process_routing_ip4(self, direction, rule_list): | |
| if not self.cidr: | |
| return | |
| tier_cidr = self.cidr | |
| chain = "%s_%s_policy" % (self.device, direction) | |
| parent_chain = "FORWARD" | |
| cidr_key = "saddr" | |
| if direction == "ingress": | |
| cidr_key = "daddr" | |
| parent_chain_rule = "ip %s %s jump %s" % (cidr_key, tier_cidr, chain) | |
| self.nft_ipv4_acl.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) | |
| self.nft_ipv4_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, 6) | |
| if cidr is None or cidr == "": | |
| continue | |
| addr = "" | |
| if cidr: | |
| addr = "ip daddr " + cidr | |
| if direction == "ingress": | |
| addr = "ip saddr " + cidr | |
| proto = "" | |
| protocol = rule['type'] | |
| if protocol != "all": | |
| icmp_type = "" | |
| if protocol == "protocol": | |
| protocol = "ip nexthdr %d" % rule['protocol'] | |
| proto = protocol | |
| if proto == "icmp": | |
| proto = proto_str = "icmp" | |
| icmp_type = ICMP_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 routing ACL rule %s" % rstr) | |
| if type == "chain": | |
| self.nft_ipv4_acl.insert(0, {'type': type, 'chain': chain, 'rule': rstr}) | |
| else: | |
| self.nft_ipv4_acl.append({'type': type, 'chain': chain, 'rule': rstr}) | |
| rstr = "counter packets 0 bytes 0 drop" | |
| self.nft_ipv4_acl.append({'type': "", 'chain': chain, 'rule': rstr}) | |
| 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, is_routed): | |
| if is_routed: | |
| self.__process_routing_ip4(direction, rule_list) | |
| self.__process_ip6(direction, rule_list) | |
| return | |
| 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() and not config.is_routed(): | |
| 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 flushAllIptablesRules(self): | |
| if not self.config.is_routed(): | |
| return | |
| # Flush all iptables rules for routing networks, which are replaced by nftables rules | |
| logging.info("Flush all iptables rules") | |
| CsHelper.execute("iptables -F filter") | |
| CsHelper.execute("iptables -F nat") | |
| CsHelper.execute("iptables -F mangle") | |
| CsHelper.execute("nft delete table ip filter") | |
| CsHelper.execute("nft delete table ip nat") | |
| CsHelper.execute("nft delete table ip mangle") | |
| def flushAllowAllEgressRules(self): | |
| if self.config.is_routed(): | |
| return | |
| 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 flushAllIpv4RoutingRules(self): | |
| if not self.config.is_routed(): | |
| return | |
| logging.info("Flush all Routing firewall rules") | |
| address_family = 'ip' | |
| table = 'ip4_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 flushAllIpv4RoutingACLRules(self): | |
| if not self.config.is_routed(): | |
| return | |
| logging.info("Flush all ACL rules for routing network") | |
| address_family = 'ip' | |
| table = 'ip4_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 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): | |
| if self.config.is_routed() and not self.config.is_vpc(): | |
| self.add_routing_rules() | |
| return | |
| 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 __writefile(self, dest, data, mode): | |
| fh = open(dest, mode) | |
| self.__exflock(fh) | |
| fh.write(data) | |
| self.__unflock(fh) | |
| fh.close() | |
| os.chmod(dest, 0o644) | |
| def __createfile(self, ip, folder, file, data): | |
| dest = "/var/www/html/" + folder + "/" + ip + "/" + file | |
| metamanifestdir = "/var/www/html/" + folder + "/" + ip | |
| metamanifest = metamanifestdir + "/meta-data" | |
| if data is not None: | |
| # base64 decode userdata | |
| if folder == "userdata" or folder == "user-data": | |
| # 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) | |
| if isinstance(data, str): | |
| self.__writefile(dest, data, "w") | |
| elif isinstance(data, bytes): | |
| self.__writefile(dest, data, "wb") | |
| else: | |
| self.__writefile(dest, "", "w") | |
| 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) | |
| fh.seek(0) | |
| 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) | |
| fh.seek(0) | |
| 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 len(peerlistarr) > 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" | |
| if "source_cidr_list" in rule and rule["source_cidr_list"]: | |
| fw_prerout_rule += " -s %s" % rule["source_cidr_list"] | |
| fw_prerout_rule += " -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" | |
| if "source_cidr_list" in rule and rule["source_cidr_list"]: | |
| fw_output_rule += " -s %s" % rule["source_cidr_list"] | |
| fw_output_rule += " -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 IPv4 firewall rules %s" % self.config.get_nft_ipv4_fw()) | |
| acls.flushAllIpv4RoutingRules() | |
| nf = CsNetfilters() | |
| nf.apply_nft_ipv4_rules(self.config.get_nft_ipv4_fw(), "firewall") | |
| acls.flushAllIptablesRules() | |
| logging.info("Configuring nftables IPv4 ACL rules %s" % self.config.get_nft_ipv4_acl()) | |
| acls.flushAllIpv4RoutingACLRules() | |
| nf = CsNetfilters() | |
| nf.apply_nft_ipv4_rules(self.config.get_nft_ipv4_acl(), "acl") | |
| logging.info("Configuring nftables IPv6 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 firewall 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") | |
| # Save nftables configuration | |
| CsHelper.save_iptables("nft list ruleset", "/etc/iptables/rules.nftables") | |
| 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)]}), | |
| ("bgp_peers", {"process_iptables": False, "executor": [CsBgpPeers("bgppeers", 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": True, "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) |