Skip to content

Commit 32ad6fd

Browse files
authored
cases to exclude ipv6 addresses from connectivity reports (#412)
1 parent bd87e4a commit 32ad6fd

File tree

237 files changed

+253
-2531
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

237 files changed

+253
-2531
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ The arguments to `--resource_list` and to `--base_resource_list` should be one o
9797
- `--output_endpoints`\
9898
Choose endpoints type in output (pods/deployments).\
9999
*default:* deployments
100+
- `--print_ipv6`\
101+
include IPv6 range in the query results even when the policies of the config do not contain any IPv6 addresses.
102+
100103

101104
For more information on command-line switches combinations, see [Common Query Patterns](docs/CommonQueryPatterns.md#cmdline-queries)
102105

docs/SchemeFileFormat.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,15 @@ For example: `my_set/prod_ns/deny_all_policy`. If there are multiple policies na
7878
#### <a name="outputconfig"></a>Output Configuration object
7979
The supported entries in the outputConfiguration object are as follows:
8080

81-
| Field | Description | Value |
82-
|-----------------|--------------------------------------------------------------------------------|----------------------------------------|
83-
| outputFormat | Output format specification. | string [ txt / yaml / csv / md / dot ] |
84-
| outputPath | A file path to redirect output into. | string |
85-
| outputEndpoints | Choose endpoints type in output. | string [ pods / deployments ] |
86-
| subset | A dict object with the defined subset elements to display in the output | [subset](#subset) object |
87-
| fullExplanation | Choose if to print all counterexamples causing the query result in the output | bool |
81+
| Field | Description | Value |
82+
|------------------|-----------------------------------------------------------------------------------------------------------------|----------------------------------------|
83+
| outputFormat | Output format specification. | string [ txt / yaml / csv / md / dot ] |
84+
| outputPath | A file path to redirect output into. | string |
85+
| outputEndpoints | Choose endpoints type in output. | string [ pods / deployments ] |
86+
| subset | A dict object with the defined subset elements to display in the output | [subset](#subset) object |
87+
| fullExplanation | Choose if to print all counterexamples causing the query result in the output | bool |
88+
| excludeIPv6Range | If the policies of the config do not contain any IPv6 addresses, do not include IPv6 range in the query results | bool [default: True] |
89+
8890

8991
#### <a name="subset"></a>Subset object
9092
The supported entries in the subset object are as follows:

nca/CoreDS/Peer.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,23 +327,26 @@ def get_ip_range_or_cidr_str(self):
327327
return self.get_cidr_list_str()
328328

329329
@staticmethod
330-
def get_all_ips_block():
330+
def get_all_ips_block(exclude_ipv6=False):
331331
"""
332-
:return: The full range of ipv4 and ipv6 addresses
332+
:return: The full range of ipv4 and ipv6 addresses if exclude_ipv6 is False
333+
:param bool exclude_ipv6: indicates if to exclude the IPv6 addresses
333334
:rtype: IpBlock
334335
"""
335336
res = IpBlock('0.0.0.0/0')
336-
res.add_cidr('::/0')
337+
if not exclude_ipv6:
338+
res.add_cidr('::/0')
337339
return res
338340

339341
@staticmethod
340-
def get_all_ips_block_peer_set():
342+
def get_all_ips_block_peer_set(exclude_ipv6=False):
341343
"""
342-
:return: The full range of ipv4 and ipv6 addresses
344+
:return: The full range of ipv4 and ipv6 addresses (ipv6 if exclude_ipv6 is False)
345+
:param bool exclude_ipv6: indicates if to exclude the IPv6 addresses
343346
:rtype: PeerSet
344347
"""
345348
res = PeerSet()
346-
res.add(IpBlock.get_all_ips_block())
349+
res.add(IpBlock.get_all_ips_block(exclude_ipv6))
347350
return res
348351

349352
def split(self):
@@ -408,7 +411,7 @@ def _add_interval_to_list(interval, non_overlapping_interval_list):
408411
non_overlapping_interval_list += to_add
409412

410413
@staticmethod
411-
def disjoint_ip_blocks(ip_blocks1, ip_blocks2):
414+
def disjoint_ip_blocks(ip_blocks1, ip_blocks2, exclude_ipv6=False):
412415
"""
413416
Takes all (atomic) ip-ranges in both ip-blocks and returns a new set of ip-ranges where
414417
each ip-range is:
@@ -417,6 +420,7 @@ def disjoint_ip_blocks(ip_blocks1, ip_blocks2):
417420
3. is maximal (extending the range to either side will violate either 1 or 2)
418421
:param ip_blocks1: A set of ip blocks
419422
:param ip_blocks2: A set of ip blocks
423+
:param bool exclude_ipv6: indicates if to exclude the IPv6 addresses in case the result is all_ips_block
420424
:return: A set of ip ranges as specified above
421425
:rtype: PeerSet
422426
"""
@@ -435,10 +439,24 @@ def disjoint_ip_blocks(ip_blocks1, ip_blocks2):
435439
res.add(ip_block)
436440

437441
if not res:
438-
res.add(IpBlock.get_all_ips_block())
442+
res.add(IpBlock.get_all_ips_block(exclude_ipv6))
439443

440444
return res
441445

446+
def is_ipv4_block(self):
447+
"""
448+
checks whether self IpBlock includes only IPv4 addresses
449+
:return: true if self includes only IPv4 addresses
450+
:rtype: bool
451+
"""
452+
cnt = 0
453+
for interval in self.interval_set:
454+
ip_address = interval.start
455+
if isinstance(ip_address, IPNetworkAddress) and isinstance(ip_address.address, ipaddress.IPv4Address) or \
456+
isinstance(ip_address, ipaddress.IPv4Address):
457+
cnt += 1
458+
return cnt == len(self.interval_set)
459+
442460

443461
class PeerSet(set):
444462
"""

nca/NetworkConfig/NetworkConfig.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,36 @@ def get_affected_pods(self, is_ingress, layer_name):
172172

173173
return affected_pods
174174

175-
def get_referenced_ip_blocks(self):
175+
def _check_for_excluding_ipv6_addresses(self, exclude_ipv6):
176176
"""
177+
checks and returns if to exclude non-referenced IPv6 addresses from the config
178+
Excluding the IPv6 addresses will be enabled if the exclude_ipv6 param is True and
179+
IPv6 addresses in all the policies of the config (if existed) were added automatically by the parser
180+
and not referenced by user
181+
:param bool exclude_ipv6: indicates if to exclude ipv_6 non-referenced addresses
182+
:rtype bool
183+
"""
184+
if not exclude_ipv6:
185+
return False
186+
187+
for policy in self.policies_container.policies.values():
188+
if policy.has_ipv6_addresses: # if at least one policy has referenced ipv6 addresses, ipv6 will be included
189+
return False
190+
return True # getting here means all policies didn't reference ipv6, it is safe to exclude ipv6 addresses
191+
192+
def get_referenced_ip_blocks(self, exclude_non_ref_ipv6=False):
193+
"""
194+
:param bool exclude_non_ref_ipv6: indicates if to exclude non-referenced ipv_6 addresses from the result
177195
:return: All ip ranges, referenced in any of the policies' rules
178196
:rtype: Peer.PeerSet
179197
"""
180198
if self.referenced_ip_blocks is not None:
181199
return self.referenced_ip_blocks
182200

201+
exclude_non_ref_ipv6_from_policies = self._check_for_excluding_ipv6_addresses(exclude_non_ref_ipv6)
183202
self.referenced_ip_blocks = Peer.PeerSet()
184203
for policy in self.policies_container.policies.values():
185-
self.referenced_ip_blocks |= policy.referenced_ip_blocks()
204+
self.referenced_ip_blocks |= policy.referenced_ip_blocks(exclude_non_ref_ipv6_from_policies)
186205

187206
return self.referenced_ip_blocks
188207

nca/NetworkConfig/NetworkConfigQuery.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -676,8 +676,10 @@ def exec(self):
676676
self.config.name
677677
peers_to_compare = self.config.peer_container.get_all_peers_group()
678678

679-
ref_ip_blocks = IpBlock.disjoint_ip_blocks(self.config.get_referenced_ip_blocks(),
680-
IpBlock.get_all_ips_block_peer_set())
679+
exclude_ipv6 = self.output_config.excludeIPv6Range
680+
ref_ip_blocks = IpBlock.disjoint_ip_blocks(self.config.get_referenced_ip_blocks(exclude_ipv6),
681+
IpBlock.get_all_ips_block_peer_set(exclude_ipv6),
682+
exclude_ipv6)
681683
connections = defaultdict(list)
682684
peers = PeerSet()
683685
peers_to_compare |= ref_ip_blocks
@@ -876,8 +878,9 @@ def disjoint_referenced_ip_blocks(self):
876878
:return: A set of disjoint ip-blocks
877879
:rtype: PeerSet
878880
"""
879-
return IpBlock.disjoint_ip_blocks(self.config1.get_referenced_ip_blocks(),
880-
self.config2.get_referenced_ip_blocks())
881+
exclude_ipv6 = self.output_config.excludeIPv6Range
882+
return IpBlock.disjoint_ip_blocks(self.config1.get_referenced_ip_blocks(exclude_ipv6),
883+
self.config2.get_referenced_ip_blocks(exclude_ipv6), exclude_ipv6)
881884

882885
@staticmethod
883886
def clone_without_ingress(config):
@@ -1100,10 +1103,13 @@ def compute_diff(self): # noqa: C901
11001103
removed_peers = old_peers - intersected_peers
11011104
added_peers = new_peers - intersected_peers
11021105
captured_pods = (self.config1.get_captured_pods() | self.config2.get_captured_pods()) & intersected_peers
1103-
old_ip_blocks = IpBlock.disjoint_ip_blocks(self.config1.get_referenced_ip_blocks(),
1104-
IpBlock.get_all_ips_block_peer_set())
1105-
new_ip_blocks = IpBlock.disjoint_ip_blocks(self.config2.get_referenced_ip_blocks(),
1106-
IpBlock.get_all_ips_block_peer_set())
1106+
exclude_ipv6 = self.output_config.excludeIPv6Range
1107+
old_ip_blocks = IpBlock.disjoint_ip_blocks(self.config1.get_referenced_ip_blocks(exclude_ipv6),
1108+
IpBlock.get_all_ips_block_peer_set(exclude_ipv6),
1109+
exclude_ipv6)
1110+
new_ip_blocks = IpBlock.disjoint_ip_blocks(self.config2.get_referenced_ip_blocks(exclude_ipv6),
1111+
IpBlock.get_all_ips_block_peer_set(exclude_ipv6),
1112+
exclude_ipv6)
11071113

11081114
conn_graph_removed_per_key = dict()
11091115
conn_graph_added_per_key = dict()
@@ -1164,7 +1170,7 @@ def compute_diff(self): # noqa: C901
11641170

11651171
# 3.2. lost/new connections between intersected peers and ipBlocks due to changes in policies and labels
11661172
key = 'Changed connections between persistent peers and ipBlocks'
1167-
disjoint_ip_blocks = IpBlock.disjoint_ip_blocks(old_ip_blocks, new_ip_blocks)
1173+
disjoint_ip_blocks = IpBlock.disjoint_ip_blocks(old_ip_blocks, new_ip_blocks, exclude_ipv6)
11681174
peers = captured_pods | disjoint_ip_blocks
11691175
keys_list.append(key)
11701176
conn_graph_removed_per_key[key] = self.get_conn_graph_changed_conns(key, disjoint_ip_blocks, False)

nca/Parsers/CalicoPolicyYamlParser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ def _get_rule_peers(self, entity_rule):
301301
elif nets or not_nets:
302302
rule_peers = PeerSet()
303303
rule_peers.add(rule_ips)
304+
if not self.has_ipv6_addresses: # if already true, means a previous rule already had ipv6
305+
# and then policy has ipv6 no need for more checks
306+
self.check_and_update_has_ipv6_addresses(rule_peers)
304307
else:
305308
rule_peers = self.peer_container.get_all_peers_group(True)
306309

@@ -641,4 +644,5 @@ def parse_policy(self):
641644
self._apply_extra_labels(policy_spec, is_profile, res_policy.name)
642645
res_policy.findings = self.warning_msgs
643646
res_policy.referenced_labels = self.referenced_labels
647+
res_policy.has_ipv6_addresses = self.has_ipv6_addresses
644648
return res_policy

nca/Parsers/GenericYamlParser.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from nca.CoreDS.ConnectionSet import ConnectionSet
1313
from nca.CoreDS.PortSet import PortSet
1414
from nca.Utils.NcaLogger import NcaLogger
15+
from nca.CoreDS.Peer import IpBlock
1516

1617

1718
class GenericYamlParser:
@@ -35,6 +36,7 @@ def __init__(self, yaml_file_name=''):
3536
"""
3637
self.yaml_file_name = yaml_file_name
3738
self.warning_msgs = [] # Collect all warning messages during parsing here
39+
self.has_ipv6_addresses = False
3840

3941
def set_file_name(self, yaml_file_name):
4042
"""
@@ -239,3 +241,15 @@ def _get_connection_set_from_properties(dest_ports, method_set=MethodSet(True),
239241
res = ConnectionSet()
240242
res.add_connections('TCP', tcp_properties)
241243
return res
244+
245+
def check_and_update_has_ipv6_addresses(self, peers):
246+
"""
247+
checks if the peer list has ipv6 addresses
248+
updates self.has_ipv6_addresses=true if at least on peer is an IPblock with IPv6 addresses
249+
:param PeerSet peers: list of peers
250+
"""
251+
for peer in peers:
252+
if isinstance(peer, IpBlock):
253+
if not peer.is_ipv4_block():
254+
self.has_ipv6_addresses = True
255+
return # if at least one peer is ipv6 block , this policy has_ipv6, no need to continue

nca/Parsers/IstioPolicyYamlParser.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,23 +157,26 @@ def parse_namespaces(self, ns_list, not_ns_list):
157157
res -= self._parse_ns_str(ns)
158158
return res
159159

160-
@staticmethod
161-
def parse_ip_block(ips_list, not_ips_list):
160+
def parse_ip_block(self, ips_list, not_ips_list):
162161
"""
163162
parse ipBlocks elements (within a source component of a rule)
164163
:param list[str] ips_list: list of ip-block addresses (either ip address or ip-block cidr)
165164
:param list[str] not_ips_list: negative list of ip-block addresses (either ip address or ip-block cidr)
166165
:return: A PeerSet containing the relevant IpBlocks
167166
:rtype: Peer.PeerSet
168167
"""
169-
ips_list = ['0.0.0.0/0', '::/0'] if ips_list is None else ips_list # If not set, any IP is allowed
168+
ips_list = IpBlock.get_all_ips_block() if ips_list is None else ips_list # If not set, any IP is allowed
170169
not_ips_list = [] if not_ips_list is None else not_ips_list
171170
res_ip_block = IpBlock()
172171
for cidr in ips_list:
173172
res_ip_block |= IpBlock(cidr)
174173
for cidr in not_ips_list:
175174
res_ip_block -= IpBlock(cidr)
176-
return res_ip_block.split()
175+
res_peer_set = res_ip_block.split()
176+
if not self.has_ipv6_addresses: # if already true, means a previous rule already had ipv6
177+
# and then policy has ipv6 no need for more checks
178+
self.check_and_update_has_ipv6_addresses(res_peer_set)
179+
return res_peer_set
177180

178181
def parse_key_values(self, key, values, not_values):
179182
"""
@@ -558,5 +561,5 @@ def parse_policy(self):
558561

559562
res_policy.findings = self.warning_msgs
560563
res_policy.referenced_labels = self.referenced_labels
561-
564+
res_policy.has_ipv6_addresses = self.has_ipv6_addresses
562565
return res_policy

nca/Parsers/K8sPolicyYamlParser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ def parse_ip_block(self, block):
216216
self.syntax_error(str(e.args), block)
217217
except TypeError as e:
218218
self.syntax_error(str(e.args), block)
219+
220+
if not self.has_ipv6_addresses: # if already true, means a previous peer already had ipv6
221+
# and then policy has ipv6 no need for more checks
222+
self.check_and_update_has_ipv6_addresses(res)
219223
return res
220224

221225
def parse_peer(self, peer):
@@ -437,4 +441,5 @@ def parse_policy(self):
437441

438442
res_policy.findings = self.warning_msgs
439443
res_policy.referenced_labels = self.referenced_labels
444+
res_policy.has_ipv6_addresses = self.has_ipv6_addresses
440445
return res_policy

nca/Resources/CalicoNetworkPolicy.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,21 @@ def clone_without_rule(self, rule_to_exclude, ingress_rule):
140140
res.add_ingress_rule(rule)
141141
return res
142142

143-
def referenced_ip_blocks(self):
143+
def referenced_ip_blocks(self, exclude_ipv6=False):
144144
"""
145+
:param bool exclude_ipv6: indicates if to exclude the automatically added IPv6 addresses in the referenced ip_blocks.
146+
IPv6 addresses that are referenced in the policy by the user will always be included
145147
:return: A set of all ipblocks referenced in one of the policy rules (one Peer object per one ip range)
146148
:rtype: Peer.PeerSet
147149
"""
148150
res = Peer.PeerSet()
149151
for rule in self.egress_rules:
150152
for peer in rule.dst_peers:
151-
if isinstance(peer, Peer.IpBlock):
153+
if isinstance(peer, Peer.IpBlock) and self._include_ip_block(peer, exclude_ipv6):
152154
res |= peer.split()
153155
for rule in self.ingress_rules:
154156
for peer in rule.src_peers:
155-
if isinstance(peer, Peer.IpBlock):
157+
if isinstance(peer, Peer.IpBlock) and self._include_ip_block(peer, exclude_ipv6):
156158
res |= peer.split()
157159

158160
return res

0 commit comments

Comments
 (0)