Skip to content

Commit 36661e4

Browse files
authored
Equivalence based queries optimized - vacuity, redundancy and strongEquivalence queries (#536)
* Optimized implementation of EquivalenceQuery. Signed-off-by: Tanya <[email protected]> * Added VacuityQuery and RedundancyQuery optimized implementation. Keeping optimized properties separated per rule (instead of the union of all policy rules) Fixed handling HostEPs in optimized implementation. Signed-off-by: Tanya <[email protected]> * Added VacuityQuery and RedundancyQuery optimized implementation. Keeping optimized properties separated per rule (instead of the union of all policy rules) Fixed handling HostEPs in optimized implementation. Signed-off-by: Tanya <[email protected]> * Ignoring 'complex function' lint error. Returning 'passed' code for skipped queries. Signed-off-by: Tanya <[email protected]> * Added VacuityQuery and RedundancyQuery optimized implementation. Keeping optimized properties separated per rule (instead of the union of all policy rules) Fixed handling HostEPs in optimized implementation. Signed-off-by: Tanya <[email protected]> * Removed redundant method. Signed-off-by: Tanya <[email protected]> * Added VacuityQuery and RedundancyQuery optimized implementation. Keeping optimized properties separated per rule (instead of the union of all policy rules) Fixed handling HostEPs in optimized implementation. Signed-off-by: Tanya <[email protected]> * Fixed domain updating mechanism per rule (to avoid activating multiple times for the same rule, for example when a rule appears twice in a config). Signed-off-by: Tanya <[email protected]> * Fixed lint errors Signed-off-by: Tanya <[email protected]> * Enabled strongEquivalence optimized implementation. Signed-off-by: Tanya <[email protected]> * Fixed small inaccuracy in handling host endpoints in optimized solution. Adding docs Signed-off-by: Tanya <[email protected]> --------- Signed-off-by: Tanya <[email protected]>
1 parent 8ac19e8 commit 36661e4

16 files changed

+212
-181
lines changed

nca/CoreDS/ConnectivityProperties.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,17 @@ def get_all_conns_props_per_config_peers(peer_container):
445445
return ConnectivityProperties.make_conn_props_from_dict({"src_peers": all_peers_and_ips_and_dns,
446446
"dst_peers": all_peers_and_ips_and_dns})
447447

448+
@staticmethod
449+
def get_all_conns_props_per_domain_peers():
450+
"""
451+
Return all possible between-peers connections.
452+
This is a compact way to represent all peers connections, but it is an over-approximation also containing
453+
IpBlock->IpBlock connections. Those redundant connections will be eventually filtered out.
454+
"""
455+
src_peers = BasePeerSet().get_peer_set_by_indices(DimensionsManager().get_dimension_domain_by_name("src_peers"))
456+
dst_peers = BasePeerSet().get_peer_set_by_indices(DimensionsManager().get_dimension_domain_by_name("dst_peers"))
457+
return ConnectivityProperties.make_conn_props_from_dict({"src_peers": src_peers, "dst_peers": dst_peers})
458+
448459
@staticmethod
449460
def make_empty_props():
450461
"""

nca/NetworkConfig/NetworkConfig.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,17 @@ def allowed_connections_optimized(self, layer_name=None):
286286
# all possible connections involving hostEndpoints
287287
conn_hep = ConnectivityProperties.make_conn_props_from_dict({"src_peers": host_eps}) | \
288288
ConnectivityProperties.make_conn_props_from_dict({"dst_peers": host_eps})
289-
conns_res = OptimizedPolicyConnections()
290-
conns_res.all_allowed_conns = ConnectivityProperties.get_all_conns_props_per_config_peers(self.peer_container)
289+
if host_eps and NetworkLayerName.K8s_Calico not in self.policies_container.layers:
290+
# maintain K8s_Calico layer as active if peer container has hostEndpoint
291+
conns_res = \
292+
self.policies_container.layers.empty_layer_allowed_connections_optimized(self.peer_container,
293+
NetworkLayerName.K8s_Calico)
294+
conns_res.allowed_conns &= conn_hep
295+
conns_res.denied_conns &= conn_hep
296+
conns_res.pass_conns &= conn_hep
297+
else:
298+
conns_res = OptimizedPolicyConnections()
299+
conns_res.all_allowed_conns = ConnectivityProperties.get_all_conns_props_per_config_peers(self.peer_container)
291300
for layer, layer_obj in self.policies_container.layers.items():
292301
conns_per_layer = layer_obj.allowed_connections_optimized(self.peer_container)
293302
# only K8s_Calico layer handles host_eps

nca/Parsers/CalicoPolicyYamlParser.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ def _parse_xgress_rule(self, rule, is_ingress, policy_selected_eps, is_profile):
558558
if not dst_res_pods and policy_selected_eps and (not is_ingress or not is_profile):
559559
self.warning('Rule selects no destination endpoints', rule)
560560

561-
return CalicoPolicyRule(src_res_pods, dst_res_pods, connections, action), conn_props
561+
return CalicoPolicyRule(src_res_pods, dst_res_pods, connections, action, conn_props)
562562

563563
def _verify_named_ports(self, rule, rule_eps, rule_conns):
564564
"""
@@ -698,16 +698,12 @@ def parse_policy(self):
698698
self.syntax_error('order is not allowed in the spec of a Profile', policy_spec)
699699

700700
for ingress_rule in policy_spec.get('ingress', []):
701-
rule, optimized_props = self._parse_xgress_rule(ingress_rule, True, res_policy.selected_peers, is_profile)
701+
rule = self._parse_xgress_rule(ingress_rule, True, res_policy.selected_peers, is_profile)
702702
res_policy.add_ingress_rule(rule)
703-
if self.optimized_run != 'false':
704-
res_policy.update_and_add_optimized_props(optimized_props, rule.action, True)
705703

706704
for egress_rule in policy_spec.get('egress', []):
707-
rule, optimized_props = self._parse_xgress_rule(egress_rule, False, res_policy.selected_peers, is_profile)
705+
rule = self._parse_xgress_rule(egress_rule, False, res_policy.selected_peers, is_profile)
708706
res_policy.add_egress_rule(rule)
709-
if self.optimized_run != 'false':
710-
res_policy.update_and_add_optimized_props(optimized_props, rule.action, False)
711707

712708
self._apply_extra_labels(policy_spec, is_profile, res_policy.name)
713709
res_policy.findings = self.warning_msgs

nca/Parsers/GenericIngressLikeYamlParser.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from nca.CoreDS.DimensionsManager import DimensionsManager
99
from nca.CoreDS.Peer import PeerSet
1010
from nca.CoreDS.PortSet import PortSet
11+
from nca.CoreDS.ProtocolSet import ProtocolSet
1112
from nca.CoreDS.ConnectivityProperties import ConnectivityProperties
1213
from nca.CoreDS.ConnectionSet import ConnectionSet
1314
from nca.Resources.IngressPolicy import IngressPolicyRule
@@ -56,40 +57,31 @@ def parse_regex_host_value(self, regex_value, rule):
5657
regex_value = regex_value.replace("*", allowed_chars + '*')
5758
return MinDFA.dfa_from_regex(regex_value)
5859

59-
def _make_allow_rules(self, allowed_conns):
60-
"""
61-
Make deny rules from the given connections
62-
:param ConnectivityProperties allowed_conns: the given allowed connections
63-
:return: the list of deny IngressPolicyRules
64-
"""
65-
return self._make_rules_from_conns(allowed_conns)
66-
6760
@staticmethod
68-
def _make_rules_from_conns(conn_props):
61+
def _make_allow_rules(conn_props, src_peers):
6962
"""
7063
Make IngressPolicyRules from the given connections
7164
:param ConnectivityProperties conn_props: the given connections
65+
:param PeerSet src_peers: the source peers to add to optimized props
7266
:return: the list of IngressPolicyRules
7367
"""
7468
assert not conn_props.named_ports
7569
assert not conn_props.excluded_named_ports
76-
peers_to_conns = {}
7770
res = []
78-
# extract peers dimension from cubes
7971
assert not conn_props.is_active_dimension("src_peers")
72+
# extract dst_peers dimension from cubes
73+
tcp_protocol = ProtocolSet.get_protocol_set_with_single_protocol('TCP')
8074
for cube in conn_props:
8175
conn_cube = conn_props.get_connectivity_cube(cube)
82-
dst_peer_set = conn_cube["dst_peers"]
83-
conn_cube.unset_dim("dst_peers")
84-
new_props = ConnectivityProperties.make_conn_props(conn_cube)
76+
new_conn_cube = conn_cube.copy()
77+
conn_cube.update({"src_peers": src_peers, "protocols": tcp_protocol})
78+
rule_opt_props = ConnectivityProperties.make_conn_props(conn_cube)
79+
dst_peer_set = new_conn_cube["dst_peers"]
80+
new_conn_cube.unset_dim("dst_peers")
81+
new_props = ConnectivityProperties.make_conn_props(new_conn_cube)
8582
new_conns = ConnectionSet()
8683
new_conns.add_connections('TCP', new_props)
87-
if dst_peer_set in peers_to_conns:
88-
peers_to_conns[dst_peer_set] |= new_conns # optimize conns for the same peers
89-
else:
90-
peers_to_conns[dst_peer_set] = new_conns
91-
for peer_set, conns in peers_to_conns.items():
92-
res.append(IngressPolicyRule(peer_set, conns))
84+
res.append(IngressPolicyRule(dst_peer_set, new_conns, rule_opt_props))
9385
return res
9486

9587
@staticmethod

nca/Parsers/IngressPolicyYamlParser.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from nca.CoreDS.PortSet import PortSet
1111
from nca.CoreDS.ConnectivityCube import ConnectivityCube
1212
from nca.CoreDS.ConnectivityProperties import ConnectivityProperties
13-
from nca.CoreDS.ProtocolSet import ProtocolSet
1413
from nca.Resources.IngressPolicy import IngressPolicy
1514
from nca.Resources.NetworkPolicy import NetworkPolicy
1615
from .GenericIngressLikeYamlParser import GenericIngressLikeYamlParser
@@ -287,10 +286,6 @@ def parse_policy(self):
287286
# allowed_conns = none means that services referenced by this Ingress policy are not found,
288287
# then no connections rules to add (Ingress policy has no effect)
289288
if allowed_conns:
290-
res_policy.add_rules(self._make_allow_rules(allowed_conns))
291-
protocols = ProtocolSet.get_protocol_set_with_single_protocol('TCP')
292-
allowed_conns &= ConnectivityProperties.make_conn_props_from_dict({"protocols": protocols,
293-
"src_peers": res_policy.selected_peers})
294-
res_policy.add_optimized_allow_props(allowed_conns, False)
289+
res_policy.add_rules(self._make_allow_rules(allowed_conns, res_policy.selected_peers))
295290
res_policy.findings = self.warning_msgs
296291
return res_policy

nca/Parsers/IstioPolicyYamlParser.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ def parse_ingress_rule(self, rule, selected_peers):
523523
"dst_peers": selected_peers})
524524
connections &= condition_conns
525525
conn_props &= condition_props
526-
return IstioPolicyRule(res_peers, connections), conn_props
526+
return IstioPolicyRule(res_peers, connections, conn_props)
527527

528528
@staticmethod
529529
def parse_policy_action(action):
@@ -571,14 +571,8 @@ def parse_policy(self):
571571
pod_selector = policy_spec.get('selector')
572572
res_policy.selected_peers = self.update_policy_peers(pod_selector, 'matchLabels')
573573
for ingress_rule in policy_spec.get('rules', []):
574-
rule, optimized_props = self.parse_ingress_rule(ingress_rule, res_policy.selected_peers)
574+
rule = self.parse_ingress_rule(ingress_rule, res_policy.selected_peers)
575575
res_policy.add_ingress_rule(rule)
576-
if res_policy.action == IstioNetworkPolicy.ActionType.Allow:
577-
res_policy.add_optimized_allow_props(optimized_props, True)
578-
else: # Deny
579-
res_policy.add_optimized_deny_props(optimized_props, True)
580-
all_props = ConnectivityProperties.get_all_conns_props_per_config_peers(self.peer_container)
581-
res_policy.add_optimized_allow_props(all_props, False)
582576
if not res_policy.ingress_rules and res_policy.action == IstioNetworkPolicy.ActionType.Deny:
583577
self.syntax_error("DENY action without rules is meaningless as it will never be triggered")
584578

nca/Parsers/IstioSidecarYamlParser.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#
55
import re
66
from nca.CoreDS.Peer import PeerSet
7-
from nca.CoreDS.ConnectivityProperties import ConnectivityProperties
87
from nca.Resources.NetworkPolicy import NetworkPolicy
98
from nca.Resources.IstioSidecar import IstioSidecar, IstioSidecarRule
109
from nca.Resources.IstioTrafficResources import istio_root_namespace
@@ -215,8 +214,6 @@ def parse_policy(self):
215214
self.namespace = self.peer_container.get_namespace(policy_ns, warn_if_missing)
216215
res_policy = IstioSidecar(policy_name, self.namespace)
217216
res_policy.policy_kind = NetworkPolicy.PolicyType.IstioSidecar
218-
all_props = ConnectivityProperties.get_all_conns_props_per_config_peers(self.peer_container)
219-
res_policy.add_optimized_allow_props(all_props, True)
220217

221218
sidecar_spec = self.policy['spec']
222219
# currently, supported fields in spec are workloadSelector and egress

nca/Parsers/IstioTrafficResourcesYamlParser.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from nca.CoreDS.MinDFA import MinDFA
88
from nca.CoreDS.Peer import PeerSet
99
from nca.CoreDS.MethodSet import MethodSet
10-
from nca.CoreDS.ProtocolSet import ProtocolSet
1110
from nca.CoreDS.ConnectivityCube import ConnectivityCube
1211
from nca.CoreDS.ConnectivityProperties import ConnectivityProperties
1312
from nca.Resources.IstioTrafficResources import Gateway, VirtualService
@@ -396,12 +395,7 @@ def create_istio_traffic_policies(self):
396395
res_policy.selected_peers = peer_set
397396
allowed_conns = self.make_allowed_connections(vs, host_dfa)
398397
if allowed_conns:
399-
res_policy.add_rules(self._make_allow_rules(allowed_conns))
400-
protocols = ProtocolSet.get_protocol_set_with_single_protocol('TCP')
401-
allowed_conns &= \
402-
ConnectivityProperties.make_conn_props_from_dict({"protocols": protocols,
403-
"src_peers": res_policy.selected_peers})
404-
res_policy.add_optimized_allow_props(allowed_conns, False)
398+
res_policy.add_rules(self._make_allow_rules(allowed_conns, res_policy.selected_peers))
405399
res_policy.findings = self.warning_msgs
406400
vs_policies.append(res_policy)
407401
if not vs_policies:

nca/Parsers/K8sPolicyYamlParser.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,8 @@ def parse_ingress_egress_rule(self, rule, peer_array_key, policy_selected_pods):
311311
:param dict rule: The rule to parse
312312
:param str peer_array_key: The key which defined the peer set ('from' for ingress, 'to' for egress)
313313
:param Peer.PeerSet policy_selected_pods: The set of pods the policy applies to
314-
:return: A tuple (K8sPolicyRule, ConnectivityProperties) with the proper PeerSet and attributes, where
315-
ConnectivityProperties is an optimized rule format with protocols, src_peers and dst_peers in a HyperCubeSet
316-
:rtype: tuple(K8sPolicyRule, ConnectivityProperties)
314+
:return: K8sPolicyRule with the proper PeerSet and attributes
315+
:rtype: K8sPolicyRule
317316
"""
318317
self.check_fields_validity(rule, 'ingress/egress rule', {peer_array_key: [0, list], 'ports': [0, list]})
319318
peer_array = rule.get(peer_array_key, [])
@@ -355,7 +354,7 @@ def parse_ingress_egress_rule(self, rule, peer_array_key, policy_selected_pods):
355354
if not res_pods:
356355
self.warning('Rule selects no pods', rule)
357356

358-
return K8sPolicyRule(res_pods, res_conns), res_opt_props
357+
return K8sPolicyRule(res_pods, res_conns, res_opt_props)
359358

360359
def verify_named_ports(self, rule, rule_pods, rule_conns):
361360
"""
@@ -393,9 +392,9 @@ def parse_ingress_rule(self, rule, policy_selected_pods):
393392
ConnectivityProperties is an optimized rule format with protocols, src_peers and dst_peers in a HyperCubeSet
394393
:rtype: tuple(K8sPolicyRule, ConnectivityProperties)
395394
"""
396-
res_rule, res_opt_props = self.parse_ingress_egress_rule(rule, 'from', policy_selected_pods)
395+
res_rule = self.parse_ingress_egress_rule(rule, 'from', policy_selected_pods)
397396
self.verify_named_ports(rule, policy_selected_pods, res_rule.port_set)
398-
return res_rule, res_opt_props
397+
return res_rule
399398

400399
def parse_egress_rule(self, rule, policy_selected_pods):
401400
"""
@@ -407,9 +406,9 @@ def parse_egress_rule(self, rule, policy_selected_pods):
407406
ConnectivityProperties is an optimized rule format with protocols, src_peers and dst_peers in a HyperCubeSet
408407
:rtype: tuple(K8sPolicyRule, ConnectivityProperties)
409408
"""
410-
res_rule, res_opt_props = self.parse_ingress_egress_rule(rule, 'to', policy_selected_pods)
409+
res_rule = self.parse_ingress_egress_rule(rule, 'to', policy_selected_pods)
411410
self.verify_named_ports(rule, res_rule.peer_set, res_rule.port_set)
412-
return res_rule, res_opt_props
411+
return res_rule
413412

414413
def parse_policy(self):
415414
"""
@@ -458,16 +457,14 @@ def parse_policy(self):
458457
ingress_rules = policy_spec.get('ingress', [])
459458
if ingress_rules:
460459
for ingress_rule in ingress_rules:
461-
rule, optimized_props = self.parse_ingress_rule(ingress_rule, res_policy.selected_peers)
460+
rule = self.parse_ingress_rule(ingress_rule, res_policy.selected_peers)
462461
res_policy.add_ingress_rule(rule)
463-
res_policy.add_optimized_allow_props(optimized_props, True)
464462

465463
egress_rules = policy_spec.get('egress', [])
466464
if egress_rules:
467465
for egress_rule in egress_rules:
468-
rule, optimized_props = self.parse_egress_rule(egress_rule, res_policy.selected_peers)
466+
rule = self.parse_egress_rule(egress_rule, res_policy.selected_peers)
469467
res_policy.add_egress_rule(rule)
470-
res_policy.add_optimized_allow_props(optimized_props, False)
471468

472469
res_policy.findings = self.warning_msgs
473470
res_policy.referenced_labels = self.referenced_labels

0 commit comments

Comments
 (0)