def make_acl(self, acl_name, target_url, protocol, match_type, direction_initiated, local_ports=None, remote_ports=None): # (acl_name, protocol_direction, ip_version, target_url, protocol, match_type, # direction_initiated, local_ports=None, remote_ports=None): if "v4" in acl_name: ip_version = IPVersion.IPV4 elif "v6" in acl_name: ip_version = IPVersion.IPV6 else: raise InputException("Invalid IPVersion provided") acl_type_prefix = get_ipversion_string(ip_version) if acl_name.endswith("to"): protocol_direction = Direction.TO_DEVICE elif acl_name.endswith("fr"): protocol_direction = Direction.FROM_DEVICE else: raise InputException("Invalid Direction provided") return { 'name': acl_name, 'type': acl_type_prefix + '-acl-type', 'aces': { 'ace': self.make_ace(protocol_direction, target_url, protocol, match_type, direction_initiated, ip_version, local_ports, remote_ports) } }
def make_acldns_match(domain: str, direction: Direction): """Function to generate an ACL match for a domain. Args: domain (str): The domain for this ACL direction (Direction): The direction for which the TCP connection was initiated. `Direction.TO_DEVICE` for source domain, `Direction.FROM_DEVICE` for destination domain. Returns: dict: A dictionary representing the ACLDNS match. """ # TODO: (1/4) Readdress the regex override (mostly for testing) if not re.match(DOMAIN_NAME_REGEX, domain) and not REGEX_OVERRIDE: raise InputException(f"Not a domain name: {domain}") acldns_match = {} key = "ietf-acldns:src-dnsname" if direction is Direction.TO_DEVICE else \ "ietf-acldns:dst-dnsname" if direction is Direction.FROM_DEVICE else None if key: acldns_match[key] = domain else: raise InputException(f"direction is not valid: {direction}") return acldns_match
def add_rule(self, target_url: str, protocol: Protocol, match_type: str, direction_initiated: Direction = None, local_port: int = None, remote_port: int = None): if len(target_url) > 140: raise InputException('target url is too long: {}' % target_url) match = {} if match_type is MatchType.IS_LOCAL: match['ietf-mud:mud'] = make_local_match() self.rules_local.append(match) elif match_type is MatchType.IS_CLOUD: match['target_url'] = target_url self.rules_cloud.append(match) elif match_type is MatchType.IS_CONTROLLER: match['ietf-mud:mud'] = make_controller_match(target_url) self.rules_controller.append(match) elif match_type is MatchType.IS_MY_CONTROLLER: match['ietf-mud:mud'] = make_my_controller_match() self.rules_controller_my.append(match) elif match_type is MatchType.IS_MFG: match['ietf-mud:mud'] = make_manufacturer_match(target_url) self.rules_manufacturer.append(match) elif match_type is MatchType.IS_MYMFG: match['ietf-mud:mud'] = make_same_manufacturer_match() self.rules_manufacturer_my.append(match) if match.get('ietf-mud:mud') is None and match.get( 'target_url') is None: raise InputException(f"match_type is not valid: {match_type}") match['protocol'] = protocol if protocol is not Protocol.ANY: match['remote_port'] = remote_port match['local_port'] = local_port if protocol is Protocol.TCP: match["cloud_placeholder"] = {'protocol': 6} #match['tcp'] = True match['direction_initiated'] = direction_initiated elif protocol is Protocol.UDP: match["cloud_placeholder"] = {'protocol': 17} else: raise InputException(f'protocol is not valid: {protocol}') return
def make_acls(self, ip_version, target_url, protocol, match_types, direction_initiated, local_ports=None, remote_ports=None, acl_names=None, mud_name=None): acls = {} if acl_names is None and mud_name is None: raise InputException( 'acl_names and mud_name can\'t both by None at the same time') elif acl_names is None: acl_names = self.make_acl_names(mud_name, ip_version, direction_initiated) if ip_version == [IPVersion.BOTH]: ip_version = [IPVersion.IPV4, IPVersion.IPV6] for i in range(len(acl_names)): for protocol_direction in [ Direction.TO_DEVICE, Direction.FROM_DEVICE ]: acls.update( self.make_acl(protocol_direction, ip_version[i], target_url, protocol, match_types, direction_initiated, local_ports, remote_ports, acl_names[i])) return acls
def get_protocol_direction_suffix_string(direction): if direction is Direction.TO_DEVICE: return 'to' if direction is Direction.FROM_DEVICE: return 'fr' raise InputException(f'protocol_direction is not valid: {direction}')
def make_acl(protocol_direction, ip_version, target_url, protocol, match_types, direction_initiated, local_ports=None, remote_ports=None, acl_name=None, mud_name=None): acl_type_prefix = get_ipversion_string(ip_version) if acl_name is None and mud_name is None: raise InputException( 'acl_name and mud_name can\'t both by None at the same time') elif acl_name is None: acl_name = make_acl_name(mud_name, ip_version, direction_initiated) return { 'name': acl_name, 'type': acl_type_prefix + '-acl-type', 'aces': { 'ace': make_ace(protocol_direction, target_url, protocol, match_types, direction_initiated, ip_version, local_ports, remote_ports) } }
def get_ipversion_string(ip_version): if ip_version is IPVersion.IPV4: return 'ipv4' if ip_version is IPVersion.IPV6: return 'ipv6' raise InputException(f'ip_version is not valid: {ip_version}')
def get_policy_type_prefix_string(direction): if direction is Direction.TO_DEVICE: return 'to' elif direction is Direction.FROM_DEVICE: return 'from' raise InputException(f'direction is not valid: {direction}')
def get_protocol_object(protocol): if protocol == 'udp': return Protocol.UDP if protocol == 'tcp': return Protocol.TCP if protocol == 'any': return Protocol.ANY raise InputException(f'protocol is not valid: {protocol}')
def get_ipversion_object(ip_version): if ip_version == 'ipv4': return IPVersion.IPV4 if ip_version == 'ipv6': return IPVersion.IPV4 if ip_version == 'both': return IPVersion.BOTH raise InputException(f'ip_version is not valid: {ip_version}')
def get_match_type_object(match_type): if match_type == 'is_my_controller': return MatchType.IS_MY_CONTROLLER if match_type == 'is_controller': return MatchType.IS_CONTROLLER if match_type == 'is_mfg': return MatchType.IS_MFG if match_type == 'is_mymfg': return MatchType.IS_MYMFG if match_type == 'is_cloud': return MatchType.IS_CLOUD raise InputException(f'match_type is not valid: {match_type}')
def get_ace_name(match_type): if match_type is MatchType.IS_LOCAL: return 'loc' if match_type is MatchType.IS_CLOUD: return 'cl' if match_type is MatchType.IS_MYMFG: return 'myman' if match_type is MatchType.IS_MFG: return 'man' if match_type is MatchType.IS_MY_CONTROLLER: return 'myctl' if match_type is MatchType.IS_CONTROLLER: return 'ent' raise InputException(f'match_type is not valid: {match_type}')
def make_manufacturer_match(domain: str): """Function to generate an ACL match for access to named manufacturers of devices that are identified by the domain names in their MUD URLs Args: domain (str): domain name for manufacturer Returns: dict: A dictionary representing the manufacturer match. """ # TODO: (3/4) Readdress the regex override (mostly for testing) if not re.match(DOMAIN_NAME_REGEX, domain) and not REGEX_OVERRIDE: raise InputException("Not a domain name: {domain}") return {'manufacturer': domain}
def make_controller_match(url: str): """Function to generate an ACL match for classes of devices that are known to be controllers Args: url (str): URI for the device class Returns: dict: A dictionary representing the controller match. """ # TODO: (2/4) Readdress the regex override (mostly for testing) if not (re.match(HTTP_URL_REGEX, url) or re.match(URN_URL_REGEX, url)) and not REGEX_OVERRIDE: raise InputException('Not a valid URI: {}' % url) return {'controller': url}
def get_direction_object(direction): if direction == 'to_device': return Direction.TO_DEVICE if direction == 'from_device': return Direction.FROM_DEVICE raise InputException(f'direction is not valid: {direction}')
def get_sub_ace_name(ace_name, direction, sub_ace_number): if direction is Direction.TO_DEVICE: return f"{ace_name}{sub_ace_number}-todev" if direction is Direction.FROM_DEVICE: return f"{ace_name}{sub_ace_number}-frdev" raise InputException(f'direction is not valid: {direction}')
def make_mud_5(self): #mud = self.support_info #mud.update(self.policies) #self.mud_file = {'ietf-mud:mud': mud, 'ietf-access-control-list:acls': {'acl': self.acls}} #return self.mud_file #def assemble_mud(self): if self.ip_version == IPVersion.BOTH: ip_version = [IPVersion.IPV4, IPVersion.IPV6] self.acl = [ self.acl_v4_to, self.acl_v4_from, self.acl_v6_to, self.acl_v6_from ] else: ip_version = [self.ip_version] if self.ip_version == IPVersion.IPV4: #self.acl_v4_to = {} #self.acl_v4_from = {} self.acl = [self.acl_v4_to, self.acl_v4_from] elif self.ip_version == IPVersion.IPV6: #self.acl_v6_to = {} #self.acl_v6_from = {} self.acl = [self.acl_v6_to, self.acl_v6_from] rule_list = [(MatchType.IS_LOCAL, self.rules_local), (MatchType.IS_CLOUD, self.rules_cloud), (MatchType.IS_CONTROLLER, self.rules_controller), (MatchType.IS_MY_CONTROLLER, self.rules_controller_my), (MatchType.IS_MFG, self.rules_manufacturer), (MatchType.IS_MYMFG, self.rules_manufacturer_my)] # Not the most efficient way to do this, but it works for (i, (match_type, rules)) in enumerate(rule_list): for (j, rule) in enumerate(rules): for protocol_direction in [ Direction.TO_DEVICE, Direction.FROM_DEVICE ]: for ipv in ip_version: sub_ace_name = get_sub_ace_name( get_ace_name(match_type), protocol_direction, j) match = {} ip_version_string = get_ipversion_string(ipv) source_port = None destination_port = None direction_initiated = rule.get('direction_initiated') if rule.get('ietf-mud:mud') is not None: match['ietf-mud:mud'] = rule['ietf-mud:mud'] cloud_entry = None else: cloud_entry = make_acldns_match( rule['target_url'], protocol_direction) if rule['protocol'] is Protocol.ANY: if cloud_entry: match[ip_version_string] = cloud_entry else: if protocol_direction is Direction.FROM_DEVICE: source_port = rule.get('remote_port') destination_port = rule.get('local_port') elif protocol_direction is Direction.TO_DEVICE: source_port = rule.get('local_port') destination_port = rule.get('remote_port') if rule['protocol'] is Protocol.TCP: match[ip_version_string] = rule.get( "cloud_placeholder").copy( ) # {'protocol': 6} if source_port is not None or destination_port is not None or \ direction_initiated is not None: match['tcp'] = make_port_range( direction_initiated, source_port, destination_port) elif rule['protocol'] is Protocol.UDP: match[ip_version_string] = rule.get( "cloud_placeholder").copy( ) # {'protocol': 17} if rule.get( 'source_port') is not None or rule.get( 'destination_port') is not None: match['udp'] = make_port_range( dir_init=None, source_port=source_port, destination_port=destination_port) else: raise InputException( f'protocol is not valid: {rule["protocol"]}' ) if cloud_entry: match[ip_version_string].update(cloud_entry) ace = { 'name': sub_ace_name, 'matches': match, 'actions': { 'forwarding': 'accept' } } if ipv == IPVersion.IPV4: if protocol_direction == Direction.TO_DEVICE: self.acl_v4_to['aces']['ace'].append(ace) #self.acl.append(self.acl_v4_to.copy()) else: self.acl_v4_from['aces']['ace'].append(ace) #self.acl.append(self.acl_v4_from.copy()) elif ipv == IPVersion.IPV6: if protocol_direction == Direction.TO_DEVICE: self.acl_v6_to['aces']['ace'].append(ace) #self.acl.append(self.acl_v6_to.copy()) else: self.acl_v6_from['aces']['ace'].append(ace) #self.acl.append(self.acl_v6_from.copy()) mud = self.support_info mud.update(self.policies) self.mud_file = { 'ietf-mud:mud': mud, 'ietf-access-control-list:acls': { 'acl': self.acl } } return self.mud_file
def make_sub_ace(self, sub_ace_name, protocol_direction, target_url, protocol, match_type, direction_initiated, ip_version, local_port=None, remote_port=None): if len(target_url) > 140: raise InputException('target url is too long: {}' % target_url) match = {} ip_version = get_ipversion_string(ip_version) source_port = None destination_port = None cloud_ipv4_entry = None if match_type is MatchType.IS_LOCAL: match['ietf-mud:mud'] = make_local_match() elif match_type is MatchType.IS_CLOUD: cloud_ipv4_entry = make_acldns_match(target_url, protocol_direction) elif match_type is MatchType.IS_CONTROLLER: match['ietf-mud:mud'] = make_controller_match(target_url) elif match_type is MatchType.IS_MY_CONTROLLER: match['ietf-mud:mud'] = make_my_controller_match() elif match_type is MatchType.IS_MFG: match['ietf-mud:mud'] = make_manufacturer_match(target_url) elif match_type is MatchType.IS_MYMFG: match['ietf-mud:mud'] = make_same_manufacturer_match() if match.get('ietf-mud:mud') is None and cloud_ipv4_entry is None: raise InputException(f"match_type is not valid: {match_type}") if protocol is Protocol.ANY: if cloud_ipv4_entry: match[ip_version] = cloud_ipv4_entry else: if protocol_direction is Direction.FROM_DEVICE: source_port = remote_port destination_port = local_port elif protocol_direction is Direction.TO_DEVICE: source_port = local_port destination_port = remote_port if protocol is Protocol.TCP: match[ip_version] = {'protocol': 6} if source_port is not None or destination_port is not None or direction_initiated is not None: match['tcp'] = make_port_range(direction_initiated, source_port, destination_port) elif protocol is Protocol.UDP: match[ip_version] = {'protocol': 17} if source_port is not None or destination_port is not None: match['udp'] = make_port_range(source_port, destination_port) else: raise InputException(f'protocol is not valid: {protocol}') if cloud_ipv4_entry: match[ip_version].update(cloud_ipv4_entry) return { 'name': sub_ace_name, 'matches': match, 'actions': { 'forwarding': 'accept' } }