예제 #1
0
    def allowed_connections(self, from_peer, to_peer, is_ingress):
        """
        Evaluate the set of connections this policy allows/denies/passes between two peers
        :param Peer.Peer from_peer: The source peer
        :param Peer.Peer to_peer:  The target peer
        :param bool is_ingress: whether we evaluate ingress rules only or egress rules only
        :return: A PolicyConnections object containing sets of allowed/denied/pass connections
        :rtype: PolicyConnections
        """

        # TODO: currently not handling egress, istio authorization policies have no egress rules
        if not is_ingress:
            return PolicyConnections(False, ConnectionSet(True))

        captured = to_peer in self.selected_peers
        if not captured:
            return PolicyConnections(False)

        allowed_conns = ConnectionSet()
        denied_conns = ConnectionSet()

        collected_conns = allowed_conns if self.action == IstioNetworkPolicy.ActionType.Allow else denied_conns
        for rule in self.ingress_rules:
            if from_peer in rule.peer_set:
                collected_conns |= rule.connections

        return PolicyConnections(True, allowed_conns, denied_conns)
예제 #2
0
    def allowed_connections(self, from_peer, to_peer):
        """
        This is the core of the whole application - computes the set of allowed connections from one peer to another.
        In our connectivity model, this function computes the labels for the edges in our directed graph.
        :param Peer.Peer from_peer: The source peer
        :param Peer.Peer to_peer: The target peer
        :return: a 4-tuple with:
          - allowed_conns: all allowed connections (captured/non-captured)
          - captured_flag: flag to indicate if any of the policies captured one of the peers (src/dst)
          - allowed_captured_conns: allowed captured connections (can be used only if the captured flag is True)
          - denied_conns: connections denied by the policies (captured)
        :rtype: ConnectionSet, bool, ConnectionSet, ConnectionSet
        """
        if isinstance(to_peer, Peer.IpBlock):
            ingress_conns = PolicyConnections(
                captured=False, all_allowed_conns=ConnectionSet(True))
        else:
            ingress_conns = self._allowed_xgress_conns(from_peer, to_peer,
                                                       True)

        if isinstance(from_peer, Peer.IpBlock):
            egress_conns = PolicyConnections(
                captured=False, all_allowed_conns=ConnectionSet(True))
        else:
            egress_conns = self._allowed_xgress_conns(from_peer, to_peer,
                                                      False)

        captured_flag = ingress_conns.captured or egress_conns.captured
        denied_conns = ingress_conns.denied_conns | egress_conns.denied_conns
        allowed_conns = ingress_conns.all_allowed_conns & egress_conns.all_allowed_conns
        # captured connections are where at least one if ingress / egress is captured
        allowed_captured_conns = (ingress_conns.allowed_conns & egress_conns.all_allowed_conns) | \
            (egress_conns.allowed_conns & ingress_conns.all_allowed_conns)

        return allowed_conns, captured_flag, allowed_captured_conns, denied_conns
예제 #3
0
    def allowed_connections(self, from_peer, to_peer, is_ingress):
        """
        Evaluate the set of connections this policy allows between two peers
        (either the allowed ingress into to_peer or the allowed egress from from_peer).
        :param Peer.Peer from_peer: The source peer
        :param Peer.Peer to_peer:  The target peer
        :param bool is_ingress: whether we evaluate ingress rules only or egress rules only
        :return: A PolicyConnections object containing sets of allowed connections
        :rtype: PolicyConnections
        """
        captured = is_ingress and self.affects_ingress and to_peer in self.selected_peers or \
            not is_ingress and self.affects_egress and from_peer in self.selected_peers
        if not captured:
            return PolicyConnections(False)

        allowed_conns = ConnectionSet()
        rules = self.ingress_rules if is_ingress else self.egress_rules
        other_peer = from_peer if is_ingress else to_peer
        for rule in rules:
            if other_peer in rule.peer_set:
                rule_conns = rule.port_set.copy()  # we need a copy because convert_named_ports is destructive
                rule_conns.convert_named_ports(to_peer.get_named_ports())
                allowed_conns |= rule_conns

        return PolicyConnections(True, allowed_conns)
예제 #4
0
    def allowed_connections(self, from_peer, to_peer, is_ingress):
        """
        Evaluate the set of connections this ingress resource allows between two peers
        :param Peer.Peer from_peer: The source peer
        :param Peer.Peer to_peer:  The target peer
        :param bool is_ingress: For compatibility with other policies.
         Will return the set of allowed connections only for is_ingress being False.
        :return: A PolicyConnections object containing sets of allowed connections
        :rtype: PolicyConnections
        """

        captured = from_peer in self.selected_peers
        if not captured:
            return PolicyConnections(False)
        if is_ingress:
            return PolicyConnections(False)

        allowed_conns = ConnectionSet()
        denied_conns = ConnectionSet()
        conns = allowed_conns if self.action == IngressPolicy.ActionType.Allow else denied_conns
        for rule in self.egress_rules:
            if to_peer in rule.peer_set:
                assert not rule.connections.has_named_ports()
                conns |= rule.connections

        return PolicyConnections(True, allowed_conns, denied_conns)
예제 #5
0
 def _get_profile_conns(self, from_peer, to_peer, is_ingress):
     peer = to_peer if is_ingress else from_peer
     assert isinstance(peer, Peer.ClusterEP)
     profile_name = peer.get_first_profile_name()
     if not profile_name:
         return PolicyConnections(False)
     profile = self.profiles.get(profile_name)
     if not profile:
         raise Exception(peer.full_name() +
                         ' refers to a non-existing profile ' +
                         profile_name)
     return profile.allowed_connections(from_peer, to_peer, is_ingress)
예제 #6
0
    def allowed_connections(self, from_peer, to_peer, is_ingress):
        """
        Evaluate the set of connections this policy allows/denies/passes between two peers
        :param Peer.Peer from_peer: The source peer
        :param Peer.Peer to_peer:  The target peer
        :param bool is_ingress: whether we evaluate ingress rules only or egress rules only
        :return: A PolicyConnections object containing sets of allowed/denied/pass connections
        :rtype: PolicyConnections
        """
        captured = is_ingress and self.affects_ingress and to_peer in self.selected_peers or \
            not is_ingress and self.affects_egress and from_peer in self.selected_peers
        if not captured:
            return PolicyConnections(False)

        allowed_conns = ConnectionSet()
        denied_conns = ConnectionSet()
        pass_conns = ConnectionSet()
        rules = self.ingress_rules if is_ingress else self.egress_rules
        for rule in rules:
            if from_peer in rule.src_peers and to_peer in rule.dst_peers:
                rule_conns = rule.connections.copy(
                )  # we need a copy because convert_named_ports is destructive
                rule_conns.convert_named_ports(to_peer.get_named_ports())

                if rule.action == CalicoPolicyRule.ActionType.Allow:
                    rule_conns -= denied_conns
                    rule_conns -= pass_conns
                    allowed_conns |= rule_conns
                elif rule.action == CalicoPolicyRule.ActionType.Deny:
                    rule_conns -= allowed_conns
                    rule_conns -= pass_conns
                    denied_conns |= rule_conns
                elif rule.action == CalicoPolicyRule.ActionType.Pass:
                    rule_conns -= allowed_conns
                    rule_conns -= denied_conns
                    pass_conns |= rule_conns
                else:
                    pass  # Nothing to do for Log action - does not affect connectivity

        return PolicyConnections(True, allowed_conns, denied_conns, pass_conns)
예제 #7
0
    def _allowed_xgress_conns(self, from_peer, to_peer, is_ingress):
        """
        get allowed and denied ingress/egress connections between from_peer and to_peer,
        considering all config's policies (and defaults)
        :param from_peer: the source peer
        :param to_peer: the dest peer
        :param is_ingress: flag to indicate if should return ingress connections or egress connections
        :return: PolicyConnections object with:
          - captured: flag to indicate if any of the policies captured one of the peers (src/dst)
          - allowed_conns: allowed captured connections (can be used only if the captured flag is True)
          - denied_conns: connections denied by the policies (captured)
          - pass_conns: irrelevant , always empty
          - all_allowed_conns: all allowed connections (captured/non-captured)
        :rtype: PolicyConnections
        """
        allowed_conns = ConnectionSet()
        denied_conns = ConnectionSet()
        pass_conns = ConnectionSet()

        policy_captured = False
        has_allow_policies_for_target = False
        for policy in self.sorted_policies:
            policy_conns = policy.allowed_connections(from_peer, to_peer,
                                                      is_ingress)
            assert isinstance(policy_conns, PolicyConnections)
            if policy_conns.captured:
                policy_captured = True
                if isinstance(
                        policy, IstioNetworkPolicy
                ) and policy.action == IstioNetworkPolicy.ActionType.Allow:
                    has_allow_policies_for_target = True
                policy_conns.denied_conns -= allowed_conns
                policy_conns.denied_conns -= pass_conns
                denied_conns |= policy_conns.denied_conns
                policy_conns.allowed_conns -= denied_conns
                policy_conns.allowed_conns -= pass_conns
                allowed_conns |= policy_conns.allowed_conns
                policy_conns.pass_conns -= denied_conns
                policy_conns.pass_conns -= allowed_conns
                pass_conns |= policy_conns.pass_conns

        if self.type == NetworkConfig.ConfigType.Istio:
            # for istio initialize non-captured conns with non-TCP connections
            allowed_non_captured_conns = ConnectionSet.get_non_tcp_connections(
            )
            if not is_ingress:
                allowed_non_captured_conns = ConnectionSet(
                    True)  # egress currently always allowed and not captured
            elif not has_allow_policies_for_target:
                # add connections allowed by default that are not captured
                allowed_non_captured_conns |= (ConnectionSet(True) -
                                               denied_conns)

            return PolicyConnections(has_allow_policies_for_target,
                                     allowed_conns,
                                     denied_conns,
                                     all_allowed_conns=allowed_conns
                                     | allowed_non_captured_conns)

        allowed_non_captured_conns = ConnectionSet()
        if not policy_captured:
            if self.type in [
                    NetworkConfig.ConfigType.K8s,
                    NetworkConfig.ConfigType.Unknown
            ]:
                allowed_non_captured_conns = ConnectionSet(
                    True
                )  # default Allow-all ingress in k8s or in case of no policy
            else:
                if self.profiles:
                    allowed_non_captured_conns = self._get_profile_conns(
                        from_peer, to_peer, is_ingress).allowed_conns
        elif pass_conns:
            allowed_conns |= pass_conns & self._get_profile_conns(
                from_peer, to_peer, is_ingress).allowed_conns
        return PolicyConnections(policy_captured,
                                 allowed_conns,
                                 denied_conns,
                                 all_allowed_conns=allowed_conns
                                 | allowed_non_captured_conns)