Esempio n. 1
0
    def ConvertToDict(self, priority_index):
        """Converts term to dict representation of SecurityPolicy.Rule JSON format.

    Takes all of the attributes associated with a term (match, action, etc) and
    converts them into a dictionary which most closely represents
    the SecurityPolicy.Rule JSON format.

    Args:
      priority_index: An integer priority value assigned to the term.

    Returns:
      A dict term.
    """
        if self.skip:
            return {}

        rules = []

        # Identify if this is inet6 processing for a term under a mixed policy.
        mixed_policy_inet6_term = False
        if self.policy_inet_version == 'mixed' and self.address_family == 'inet6':
            mixed_policy_inet6_term = True

        term_dict = {
            'action': self.ACTION_MAP.get(self.term.action[0],
                                          self.term.action[0]),
            'direction': self.term.direction,
            'priority': priority_index
        }

        # Get the correct syntax for API versions.
        src_ip_range = ApiVersionSyntaxMap.SYNTAX_MAP[
            self.api_version]['src_ip_range']
        dest_ip_range = ApiVersionSyntaxMap.SYNTAX_MAP[
            self.api_version]['dest_ip_range']
        layer_4_config = ApiVersionSyntaxMap.SYNTAX_MAP[
            self.api_version]['layer_4_config']

        target_resources = []
        for proj, vpc in self.term.target_resources:
            target_resources.append(
                self._TARGET_RESOURCE_FORMAT.format(proj, vpc))

        if target_resources:  # Only set when non-empty.
            term_dict['targetResources'] = target_resources

        term_dict['enableLogging'] = self._GetLoggingSetting()

        # This combo provides ability to identify the rule.
        term_name = self.term.name
        if mixed_policy_inet6_term:
            term_name = gcp.GetIpv6TermName(term_name)
        raw_description = term_name + ': ' + ' '.join(self.term.comment)
        term_dict['description'] = gcp.TruncateString(
            raw_description, self._MAX_TERM_COMMENT_LENGTH)

        filtered_protocols = []
        for proto in self.term.protocol:
            # ICMP filtering by inet_version
            # Since each term has inet_version, 'mixed' is correctly processed here.
            if proto == 'icmp' and self.address_family == 'inet6':
                logging.warning(
                    'WARNING: Term %s is being rendered for inet6, ICMP '
                    'protocol will not be rendered.', self.term.name)
                continue
            if proto == 'icmpv6' and self.address_family == 'inet':
                logging.warning(
                    'WARNING: Term %s is being rendered for inet, ICMPv6 '
                    'protocol will not be rendered.', self.term.name)
                continue
            if proto == 'igmp' and self.address_family == 'inet6':
                logging.warning(
                    'WARNING: Term %s is being rendered for inet6, IGMP '
                    'protocol will not be rendered.', self.term.name)
                continue
            filtered_protocols.append(proto)
        # If there is no protocol left after ICMP/IGMP filtering, drop this term.
        # But only do this for terms that originally had protocols.
        # Otherwise you end up dropping the default-deny.
        if self.term.protocol and not filtered_protocols:
            return {}

        protocols_and_ports = []
        if not self.term.protocol:
            # Empty protocol list means any protocol, but any protocol in HF is
            # represented as "all"
            protocols_and_ports = [{'ipProtocol': 'all'}]
        else:
            for proto in filtered_protocols:
                # If the protocol name is not supported, use the protocol number.
                if proto not in self._ALLOW_PROTO_NAME:
                    proto = str(self.PROTO_MAP[proto])
                    logging.info(
                        'INFO: Term %s is being rendered using protocol number',
                        self.term.name)
                proto_ports = {'ipProtocol': proto}
                if self.term.destination_port:
                    ports = self._GetPorts()
                    if ports:  # Only set when non-empty.
                        proto_ports['ports'] = ports
                protocols_and_ports.append(proto_ports)

        if self.api_version == 'ga':
            term_dict['match'] = {layer_4_config: protocols_and_ports}
        else:
            term_dict['match'] = {
                'config': {
                    layer_4_config: protocols_and_ports
                }
            }

        # match needs a field called versionedExpr with value FIREWALL
        # See documentation:
        # https://cloud.google.com/compute/docs/reference/rest/beta/organizationSecurityPolicies/addRule
        term_dict['match']['versionedExpr'] = 'FIREWALL'

        ip_version = self.AF_MAP[self.address_family]
        if ip_version == 4:
            any_ip = [nacaddr.IP('0.0.0.0/0')]
        else:
            any_ip = [nacaddr.IPv6('::/0')]

        if self.term.direction == 'EGRESS':
            daddrs = self.term.GetAddressOfVersion('destination_address',
                                                   ip_version)

            # If the address got filtered out and is empty due to address family, we
            # don't render the term. At this point of term processing, the direction
            # has already been validated, so we can just log and return empty rule.
            if self.term.destination_address and not daddrs:
                logging.warning(
                    'WARNING: Term %s is not being rendered for %s, '
                    'because there are no addresses of that family.',
                    self.term.name, self.address_family)
                return []
            # This should only happen if there were no addresses set originally.
            if not daddrs:
                daddrs = any_ip

            destination_address_chunks = [
                daddrs[x:x + self._TERM_ADDRESS_LIMIT]
                for x in range(0, len(daddrs), self._TERM_ADDRESS_LIMIT)
            ]

            for daddr_chunk in destination_address_chunks:
                rule = copy.deepcopy(term_dict)
                if self.api_version == 'ga':
                    rule['match'][dest_ip_range] = [
                        daddr.with_prefixlen for daddr in daddr_chunk
                    ]
                else:
                    rule['match']['config'][dest_ip_range] = [
                        daddr.with_prefixlen for daddr in daddr_chunk
                    ]
                rule['priority'] = priority_index
                rules.append(rule)
                priority_index += 1
        else:
            saddrs = self.term.GetAddressOfVersion('source_address',
                                                   ip_version)

            # If the address got filtered out and is empty due to address family, we
            # don't render the term. At this point of term processing, the direction
            # has already been validated, so we can just log and return empty rule.
            if self.term.source_address and not saddrs:
                logging.warning(
                    'WARNING: Term %s is not being rendered for %s, '
                    'because there are no addresses of that family.',
                    self.term.name, self.address_family)
                return []
            # This should only happen if there were no addresses set originally.
            if not saddrs:
                saddrs = any_ip

            source_address_chunks = [
                saddrs[x:x + self._TERM_ADDRESS_LIMIT]
                for x in range(0, len(saddrs), self._TERM_ADDRESS_LIMIT)
            ]
            for saddr_chunk in source_address_chunks:
                rule = copy.deepcopy(term_dict)
                if self.api_version == 'ga':
                    rule['match'][src_ip_range] = [
                        saddr.with_prefixlen for saddr in saddr_chunk
                    ]
                else:
                    rule['match']['config'][src_ip_range] = [
                        saddr.with_prefixlen for saddr in saddr_chunk
                    ]
                rule['priority'] = priority_index
                rules.append(rule)
                priority_index += 1

        return rules
Esempio n. 2
0
    def ConvertToDict(self, priority_index):
        """Converts term to dict representation of SecurityPolicy.Rule JSON format.

    Takes all of the attributes associated with a term (match, action, etc) and
    converts them into a dictionary which most closely represents
    the SecurityPolicy.Rule JSON format.

    Args:
      priority_index: An integer priority value assigned to the term.

    Returns:
      A dict term.
    """
        term_dict = {
            'action': self.ACTION_MAP.get(self.term.action[0],
                                          self.term.action[0]),
            'direction': self.term.direction,
            'priority': priority_index
        }

        target_resources = []
        for proj, vpc in self.term.target_resources:
            target_resources.append('projects/{}/networks/{}'.format(
                proj, vpc))

        if target_resources:  # Only set when non-empty.
            term_dict['targetResources'] = target_resources

        term_dict['enableLogging'] = self._GetLoggingSetting()

        # This combo provides ability to identify the rule.
        raw_descirption = self.term.name + ': ' + ' '.join(self.term.comment)
        term_dict['description'] = gcp.TruncateString(
            raw_descirption, self._MAX_TERM_COMMENT_LENGTH)

        ip_version = self.AF_MAP[self.address_family]
        if self.term.direction == 'EGRESS':
            daddrs = self.term.GetAddressOfVersion('destination_address',
                                                   ip_version)
            term_dict['match'] = {
                'config': {
                    'destIpRanges': [daddr.with_prefixlen for daddr in daddrs]
                }
            }
        else:
            saddrs = self.term.GetAddressOfVersion('source_address',
                                                   ip_version)
            term_dict['match'] = {
                'config': {
                    'srcIpRanges': [saddr.with_prefixlen for saddr in saddrs]
                }
            }
        protocols_and_ports = []
        for proto in self.term.protocol:
            proto_ports = {'ipProtocol': proto}
            if self.term.destination_port:
                ports = self._GetPorts()
                if ports:  # Only set when non-empty.
                    proto_ports['ports'] = ports
            protocols_and_ports.append(proto_ports)

        if protocols_and_ports:  # Only set when non-empty.
            term_dict['match']['config']['layer4Configs'] = protocols_and_ports

        # match needs a field called versionedExpr with value FIREWALL
        # See documentation:
        # https://cloud.google.com/compute/docs/reference/rest/beta/organizationSecurityPolicies/addRule
        term_dict['match']['versionedExpr'] = 'FIREWALL'

        return term_dict
Esempio n. 3
0
    def ConvertToDict(self, priority_index):
        """Converts term to dict representation of SecurityPolicy.Rule JSON format.

    Takes all of the attributes associated with a term (match, action, etc) and
    converts them into a dictionary which most closely represents
    the SecurityPolicy.Rule JSON format.

    Args:
      priority_index: An integer priority value assigned to the term.

    Returns:
      A dict term.
    """
        if self.skip:
            return {}

        rules = []

        term_dict = {
            'action': self.ACTION_MAP.get(self.term.action[0],
                                          self.term.action[0]),
            'direction': self.term.direction,
            'priority': priority_index
        }

        target_resources = []
        for proj, vpc in self.term.target_resources:
            target_resources.append(
                self._TARGET_RESOURCE_FORMAT.format(proj, vpc))

        if target_resources:  # Only set when non-empty.
            term_dict['targetResources'] = target_resources

        term_dict['enableLogging'] = self._GetLoggingSetting()

        # This combo provides ability to identify the rule.
        raw_descirption = self.term.name + ': ' + ' '.join(self.term.comment)
        term_dict['description'] = gcp.TruncateString(
            raw_descirption, self._MAX_TERM_COMMENT_LENGTH)

        protocols_and_ports = []
        if not self.term.protocol:
            # Empty protocol list means any protocol, but any protocol in HF is
            # represented as "all"
            protocols_and_ports = [{'ipProtocol': 'all'}]
        else:
            for proto in self.term.protocol:
                proto_ports = {'ipProtocol': proto}
                if self.term.destination_port:
                    ports = self._GetPorts()
                    if ports:  # Only set when non-empty.
                        proto_ports['ports'] = ports
                protocols_and_ports.append(proto_ports)

        term_dict['match'] = {'config': {'layer4Configs': protocols_and_ports}}

        # match needs a field called versionedExpr with value FIREWALL
        # See documentation:
        # https://cloud.google.com/compute/docs/reference/rest/beta/organizationSecurityPolicies/addRule
        term_dict['match']['versionedExpr'] = 'FIREWALL'

        ip_version = self.AF_MAP[self.address_family]
        if ip_version == 4:
            any_ip = [nacaddr.IP('0.0.0.0/0')]
        else:
            any_ip = [nacaddr.IPv6('::/0')]

        if self.term.direction == 'EGRESS':
            daddrs = self.term.GetAddressOfVersion('destination_address',
                                                   ip_version)
            if not daddrs:
                daddrs = any_ip

            destination_address_chunks = [
                daddrs[x:x + self._TERM_ADDRESS_LIMIT]
                for x in range(0, len(daddrs), self._TERM_ADDRESS_LIMIT)
            ]

            for daddr_chunk in destination_address_chunks:
                rule = copy.deepcopy(term_dict)
                rule['match']['config']['destIpRanges'] = [
                    daddr.with_prefixlen for daddr in daddr_chunk
                ]
                rule['priority'] = priority_index
                rules.append(rule)
                priority_index += 1
        else:
            saddrs = self.term.GetAddressOfVersion('source_address',
                                                   ip_version)

            if not saddrs:
                saddrs = any_ip

            source_address_chunks = [
                saddrs[x:x + self._TERM_ADDRESS_LIMIT]
                for x in range(0, len(saddrs), self._TERM_ADDRESS_LIMIT)
            ]
            for saddr_chunk in source_address_chunks:
                rule = copy.deepcopy(term_dict)
                rule['match']['config']['srcIpRanges'] = [
                    saddr.with_prefixlen for saddr in saddr_chunk
                ]
                rule['priority'] = priority_index
                rules.append(rule)
                priority_index += 1

        return rules
Esempio n. 4
0
    def _TranslatePolicy(self, pol, exp_info):
        """Translates a Capirca policy into a HF-specific data structure.

    Takes in a POL file, parses each term and populates the policy
    dict. Each term in this list is a dictionary formatted according to
    HF's rule API specification.  Additionally, checks for its quota.

    Args:
      pol: A Policy() object representing a given POL file.
      exp_info: An int that specifies number of weeks until policy expiry.

    Raises:
      ExceededCostError: Raised when the cost of a policy exceeds the default
          maximum cost.
      HeaderError: Raised when the header cannot be parsed or a header option is
          invalid.
    """
        self.policies = []
        policy = {'rules': [], 'type': 'FIREWALL'}
        is_policy_modified = False
        counter = 1
        total_cost = 0
        for header, terms in pol.filters:

            if self._PLATFORM not in header.platforms:
                continue

            filter_options = header.FilterOptions(self._PLATFORM)

            # Get the policy name.
            filter_name = header.FilterName(self._PLATFORM)
            filter_options.remove(filter_name)
            if 'displayName' in policy:
                policy['displayName'] += '_' + filter_name
            else:
                policy['displayName'] = filter_name
            # displayName cannot be more than 63 characters long.
            policy['displayName'] = gcp.TruncateString(policy['displayName'],
                                                       63)
            is_policy_modified = True

            # Get term direction if set.
            direction = 'INGRESS'
            for i in self._GOOD_DIRECTION:
                if i in filter_options:
                    direction = i
                    filter_options.remove(i)

            # Get the address family if set.
            address_family = 'inet'
            for i in self._SUPPORTED_AF:
                if i in filter_options:
                    address_family = i
                    filter_options.remove(i)

            # Find the default maximum cost of a policy, an integer, if specified.
            max_cost = self._DEFAULT_MAXIMUM_COST
            for opt in filter_options:
                try:
                    max_cost = int(opt)
                    filter_options.remove(opt)
                    break
                except ValueError:
                    continue

            if max_cost > 65536:
                raise gcp.HeaderError(
                    'Default maximum cost cannot be higher than 65536')

            # If there are remaining options, they are unknown/unsupported options.
            if filter_options:
                raise gcp.HeaderError(
                    'Unsupported or unknown filter options %s in policy %s ' %
                    (str(filter_options), filter_name))

            for term in terms:
                if term.stateless_reply:
                    continue
                total_cost += GetCost(term)
                if total_cost > max_cost:
                    raise ExceededCostError(
                        'Policy cost (%d) reached the maximum (%d)' %
                        (total_cost, max_cost))
                if gcp.IsDefaultDeny(term):
                    if direction == 'EGRESS':
                        term.destination_address = self._ANY
                    else:
                        term.source_address = self._ANY
                term.name = self.FixTermLength(term.name)
                term.direction = direction
                dict_term = Term(term,
                                 address_family=address_family).ConvertToDict(
                                     priority_index=counter)
                counter += 1
                policy['rules'].append(dict_term)

        self.policies.append(policy)
        # Do not render an empty rules if no policies have been evaluated.
        if not is_policy_modified:
            self.policies = []