illumio-pylo 0.3.12__py3-none-any.whl → 0.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- illumio_pylo/API/APIConnector.py +61 -14
- illumio_pylo/API/CredentialsManager.py +130 -3
- illumio_pylo/API/Explorer.py +619 -14
- illumio_pylo/API/JsonPayloadTypes.py +64 -4
- illumio_pylo/FilterQuery.py +892 -0
- illumio_pylo/LabelCommon.py +13 -3
- illumio_pylo/LabelDimension.py +109 -0
- illumio_pylo/LabelStore.py +97 -38
- illumio_pylo/WorkloadStore.py +58 -0
- illumio_pylo/__init__.py +9 -3
- illumio_pylo/cli/__init__.py +5 -2
- illumio_pylo/cli/commands/__init__.py +1 -0
- illumio_pylo/cli/commands/credential_manager.py +176 -0
- illumio_pylo/cli/commands/traffic_export.py +358 -0
- illumio_pylo/cli/commands/ui/credential_manager_ui/app.js +191 -2
- illumio_pylo/cli/commands/ui/credential_manager_ui/index.html +50 -1
- illumio_pylo/cli/commands/ui/credential_manager_ui/styles.css +179 -28
- illumio_pylo/cli/commands/update_pce_objects_cache.py +1 -2
- illumio_pylo/cli/commands/workload_export.py +29 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/METADATA +1 -1
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/RECORD +24 -22
- illumio_pylo/Query.py +0 -331
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/WHEEL +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/licenses/LICENSE +0 -0
- {illumio_pylo-0.3.12.dist-info → illumio_pylo-0.3.13.dist-info}/top_level.txt +0 -0
illumio_pylo/API/Explorer.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from typing import Optional, List, Dict, Literal, TypeVar, Generic, Union
|
|
3
|
-
from datetime import datetime, timedelta
|
|
2
|
+
from typing import Optional, List, Dict, Literal, TypeVar, Generic, Union, Set
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
4
|
|
|
5
5
|
import illumio_pylo as pylo
|
|
6
|
-
from .JsonPayloadTypes import RuleCoverageQueryEntryJsonStructure
|
|
6
|
+
from .JsonPayloadTypes import RuleCoverageQueryEntryJsonStructure, ExplorerTrafficRecordsApiReplyPayloadJsonStructure, \
|
|
7
|
+
ExplorerTrafficRecordJsonStructure
|
|
7
8
|
from illumio_pylo.API.APIConnector import APIConnector
|
|
8
9
|
|
|
9
10
|
|
|
@@ -346,7 +347,6 @@ class ExplorerResultSetV1:
|
|
|
346
347
|
draft_manager.execute()
|
|
347
348
|
|
|
348
349
|
|
|
349
|
-
|
|
350
350
|
class RuleCoverageQueryManager:
|
|
351
351
|
|
|
352
352
|
class QueryServices:
|
|
@@ -697,7 +697,11 @@ class RuleCoverageQueryManager:
|
|
|
697
697
|
|
|
698
698
|
self.apply_policy_decisions_to_logs()
|
|
699
699
|
|
|
700
|
+
|
|
700
701
|
class ExplorerFilterSetV1:
|
|
702
|
+
"""
|
|
703
|
+
Legacy Filters for use with PCE version < 23.2"
|
|
704
|
+
"""
|
|
701
705
|
exclude_processes_emulate: Dict[str, str]
|
|
702
706
|
_exclude_processes: List[str]
|
|
703
707
|
_exclude_direct_services: List['pylo.DirectServiceInRule']
|
|
@@ -879,13 +883,6 @@ class ExplorerFilterSetV1:
|
|
|
879
883
|
for item in map.to_list_of_cidr_string(skip_netmask_for_32=True):
|
|
880
884
|
self.provider_exclude_cidr(item)
|
|
881
885
|
|
|
882
|
-
def provider_include_cidr(self, ipaddress: str):
|
|
883
|
-
self.__filter_provider_ip_include.append(ipaddress)
|
|
884
|
-
|
|
885
|
-
def provider_include_ip4map(self, map: 'pylo.IP4Map'):
|
|
886
|
-
for item in map.to_list_of_cidr_string():
|
|
887
|
-
self.provider_include_cidr(item)
|
|
888
|
-
|
|
889
886
|
def service_include_add(self, service: Union['pylo.DirectServiceInRule',str]):
|
|
890
887
|
if isinstance(service, str):
|
|
891
888
|
self._include_direct_services.append(pylo.DirectServiceInRule.create_from_text(service))
|
|
@@ -929,7 +926,7 @@ class ExplorerFilterSetV1:
|
|
|
929
926
|
self._time_from = time
|
|
930
927
|
|
|
931
928
|
def set_time_from_x_seconds_ago(self, seconds: int):
|
|
932
|
-
self._time_from = datetime.
|
|
929
|
+
self._time_from = datetime.now(timezone.utc) - timedelta(seconds=seconds)
|
|
933
930
|
|
|
934
931
|
def set_time_from_x_days_ago(self, days: int):
|
|
935
932
|
return self.set_time_from_x_seconds_ago(days*60*60*24)
|
|
@@ -941,7 +938,7 @@ class ExplorerFilterSetV1:
|
|
|
941
938
|
self._time_to = time
|
|
942
939
|
|
|
943
940
|
def set_time_to_x_seconds_ago(self, seconds: int):
|
|
944
|
-
self._time_to = datetime.
|
|
941
|
+
self._time_to = datetime.now(timezone.utc) - timedelta(seconds=seconds)
|
|
945
942
|
|
|
946
943
|
def set_time_to_x_days_ago(self, days: int):
|
|
947
944
|
return self.set_time_to_x_seconds_ago(days*60*60*24)
|
|
@@ -1091,6 +1088,12 @@ class ExplorerFilterSetV1:
|
|
|
1091
1088
|
for process in self._exclude_processes:
|
|
1092
1089
|
filters['services']['exclude'].append({'process_name': process})
|
|
1093
1090
|
|
|
1091
|
+
# empty sources/destinations include will return no results, so we add an empty array to mean "any"
|
|
1092
|
+
if len(filters['sources']['include']) == 0:
|
|
1093
|
+
filters['sources']['include'].append([])
|
|
1094
|
+
if len(filters['destinations']['include']) == 0:
|
|
1095
|
+
filters['destinations']['include'].append([])
|
|
1096
|
+
|
|
1094
1097
|
# print(filters)
|
|
1095
1098
|
return filters
|
|
1096
1099
|
|
|
@@ -1104,7 +1107,6 @@ class ExplorerQuery:
|
|
|
1104
1107
|
self.max_running_time_seconds = max_running_time_seconds
|
|
1105
1108
|
self.check_for_update_interval_seconds = check_for_update_interval_seconds
|
|
1106
1109
|
|
|
1107
|
-
|
|
1108
1110
|
def execute(self) -> ExplorerResultSetV1:
|
|
1109
1111
|
"""
|
|
1110
1112
|
Execute the query and stores the results in the 'results' property.
|
|
@@ -1116,6 +1118,609 @@ class ExplorerQuery:
|
|
|
1116
1118
|
return self.results
|
|
1117
1119
|
|
|
1118
1120
|
|
|
1121
|
+
class ExplorerFilterSetV2:
|
|
1122
|
+
"""
|
|
1123
|
+
New Filters for use with PCE version >= 23.2"
|
|
1124
|
+
"""
|
|
1125
|
+
def __init__(self, max_results=2500):
|
|
1126
|
+
self._source_filters: List[ExplorerFilterSetV2Filter] = [] # List of source filters, in the backend logic they will be treated with OR logic
|
|
1127
|
+
self._destination_filters: List[ExplorerFilterSetV2Filter] = [] # List of destination filters, in the backend logic they will be treated with OR logic
|
|
1128
|
+
|
|
1129
|
+
self.__filter_consumer_ip_exclude = []
|
|
1130
|
+
self.__filter_provider_ip_exclude = []
|
|
1131
|
+
|
|
1132
|
+
self._consumer_exclude_labels: Dict[str, Union[pylo.Label, pylo.LabelGroup]] = {}
|
|
1133
|
+
self._provider_exclude_labels: Dict[str, Union[pylo.Label, pylo.LabelGroup]] = {}
|
|
1134
|
+
|
|
1135
|
+
self._consumer_iplists_exclude = {}
|
|
1136
|
+
self._provider_iplists_exclude = {}
|
|
1137
|
+
|
|
1138
|
+
self.max_results = max_results
|
|
1139
|
+
|
|
1140
|
+
self._policy_decision_filter = []
|
|
1141
|
+
self._time_from = None
|
|
1142
|
+
self._time_to = None
|
|
1143
|
+
|
|
1144
|
+
self._include_direct_services = []
|
|
1145
|
+
|
|
1146
|
+
self._exclude_broadcast = False
|
|
1147
|
+
self._exclude_multicast = False
|
|
1148
|
+
self._exclude_direct_services = []
|
|
1149
|
+
self._exclude_processes = []
|
|
1150
|
+
|
|
1151
|
+
@staticmethod
|
|
1152
|
+
def __filter_prop_add_label(prop_dict, label_or_href):
|
|
1153
|
+
"""
|
|
1154
|
+
|
|
1155
|
+
@type prop_dict: dict
|
|
1156
|
+
@type label_or_href: str|pylo.Label|pylo.LabelGroup
|
|
1157
|
+
"""
|
|
1158
|
+
if isinstance(label_or_href, str):
|
|
1159
|
+
prop_dict[label_or_href] = label_or_href
|
|
1160
|
+
return
|
|
1161
|
+
elif isinstance(label_or_href, pylo.Label):
|
|
1162
|
+
prop_dict[label_or_href.href] = label_or_href
|
|
1163
|
+
return
|
|
1164
|
+
elif isinstance(label_or_href, pylo.LabelGroup):
|
|
1165
|
+
# since 21.5 labelgroups can be included directly
|
|
1166
|
+
# for nested_label in label_or_href.expand_nested_to_array():
|
|
1167
|
+
# prop_dict[nested_label.href] = nested_label
|
|
1168
|
+
prop_dict[label_or_href.href] = label_or_href
|
|
1169
|
+
return
|
|
1170
|
+
else:
|
|
1171
|
+
raise pylo.PyloEx("Unsupported object type {}".format(type(label_or_href)))
|
|
1172
|
+
|
|
1173
|
+
def new_source_filter(self) -> 'ExplorerFilterSetV2Filter':
|
|
1174
|
+
filter = ExplorerFilterSetV2Filter()
|
|
1175
|
+
self._source_filters.append(filter)
|
|
1176
|
+
return filter
|
|
1177
|
+
|
|
1178
|
+
def new_destination_filter(self) -> 'ExplorerFilterSetV2Filter':
|
|
1179
|
+
filter = ExplorerFilterSetV2Filter()
|
|
1180
|
+
self._destination_filters.append(filter)
|
|
1181
|
+
return filter
|
|
1182
|
+
|
|
1183
|
+
def consumer_exclude_label(self, label_or_href: Union[str, 'pylo.Label', 'pylo.LabelGroup']):
|
|
1184
|
+
self.__filter_prop_add_label(self._consumer_exclude_labels, label_or_href)
|
|
1185
|
+
|
|
1186
|
+
def consumer_exclude_labels(self, labels: List[Union[str, 'pylo.Label', 'pylo.LabelGroup']]):
|
|
1187
|
+
for label in labels:
|
|
1188
|
+
self.consumer_exclude_label(label)
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
def consumer_exclude_cidr(self, ipaddress: str):
|
|
1192
|
+
self.__filter_consumer_ip_exclude.append(ipaddress)
|
|
1193
|
+
|
|
1194
|
+
def consumer_exclude_iplist(self, iplist_or_href: Union[str, 'pylo.IPList']):
|
|
1195
|
+
if isinstance(iplist_or_href, str):
|
|
1196
|
+
self._consumer_iplists_exclude[iplist_or_href] = iplist_or_href
|
|
1197
|
+
return
|
|
1198
|
+
|
|
1199
|
+
if isinstance(iplist_or_href, pylo.IPList):
|
|
1200
|
+
self._consumer_iplists_exclude[iplist_or_href.href] = iplist_or_href.href
|
|
1201
|
+
return
|
|
1202
|
+
|
|
1203
|
+
raise pylo.PyloEx("Unsupported object type {}".format(type(iplist_or_href)))
|
|
1204
|
+
|
|
1205
|
+
def consumer_exclude_ip4map(self, map: 'pylo.IP4Map'):
|
|
1206
|
+
for item in map.to_list_of_cidr_string():
|
|
1207
|
+
self.consumer_exclude_cidr(item)
|
|
1208
|
+
|
|
1209
|
+
def provider_exclude_label(self, label_or_href: Union[str, 'pylo.Label', 'pylo.LabelGroup']):
|
|
1210
|
+
self.__filter_prop_add_label(self._provider_exclude_labels, label_or_href)
|
|
1211
|
+
|
|
1212
|
+
def provider_exclude_labels(self, labels_or_hrefs: List[Union[str, 'pylo.Label', 'pylo.LabelGroup']]):
|
|
1213
|
+
for label in labels_or_hrefs:
|
|
1214
|
+
self.provider_exclude_label(label)
|
|
1215
|
+
|
|
1216
|
+
def provider_exclude_cidr(self, ipaddress: str):
|
|
1217
|
+
self.__filter_provider_ip_exclude.append(ipaddress)
|
|
1218
|
+
|
|
1219
|
+
def provider_exclude_iplist(self, iplist_or_href: Union[str, 'pylo.IPList']):
|
|
1220
|
+
if isinstance(iplist_or_href, str):
|
|
1221
|
+
self._provider_iplists_exclude[iplist_or_href] = iplist_or_href
|
|
1222
|
+
return
|
|
1223
|
+
|
|
1224
|
+
if isinstance(iplist_or_href, pylo.IPList):
|
|
1225
|
+
self._provider_iplists_exclude[iplist_or_href.href] = iplist_or_href.href
|
|
1226
|
+
return
|
|
1227
|
+
|
|
1228
|
+
raise pylo.PyloEx("Unsupported object type {}".format(type(iplist_or_href)))
|
|
1229
|
+
|
|
1230
|
+
def provider_exclude_ip4map(self, map: 'pylo.IP4Map'):
|
|
1231
|
+
for item in map.to_list_of_cidr_string(skip_netmask_for_32=True):
|
|
1232
|
+
self.provider_exclude_cidr(item)
|
|
1233
|
+
|
|
1234
|
+
def service_include_add(self, service: Union['pylo.DirectServiceInRule',str]):
|
|
1235
|
+
if isinstance(service, str):
|
|
1236
|
+
self._include_direct_services.append(pylo.DirectServiceInRule.create_from_text(service))
|
|
1237
|
+
return
|
|
1238
|
+
self._include_direct_services.append(service)
|
|
1239
|
+
|
|
1240
|
+
def service_include_add_protocol(self, protocol: int):
|
|
1241
|
+
self._include_direct_services.append(pylo.DirectServiceInRule(proto=protocol))
|
|
1242
|
+
|
|
1243
|
+
def service_include_add_protocol_tcp(self):
|
|
1244
|
+
self._include_direct_services.append(pylo.DirectServiceInRule(proto=6))
|
|
1245
|
+
|
|
1246
|
+
def service_include_add_protocol_udp(self):
|
|
1247
|
+
self._include_direct_services.append(pylo.DirectServiceInRule(proto=17))
|
|
1248
|
+
|
|
1249
|
+
def service_exclude_add(self, service: 'pylo.DirectServiceInRule'):
|
|
1250
|
+
self._exclude_direct_services.append(service)
|
|
1251
|
+
|
|
1252
|
+
def service_exclude_add_protocol(self, protocol: int):
|
|
1253
|
+
self._exclude_direct_services.append(pylo.DirectServiceInRule(proto=protocol))
|
|
1254
|
+
|
|
1255
|
+
def service_exclude_add_protocol_tcp(self):
|
|
1256
|
+
self._exclude_direct_services.append(pylo.DirectServiceInRule(proto=6))
|
|
1257
|
+
|
|
1258
|
+
def service_exclude_add_protocol_udp(self):
|
|
1259
|
+
self._exclude_direct_services.append(pylo.DirectServiceInRule(proto=17))
|
|
1260
|
+
|
|
1261
|
+
def process_exclude_add(self, process_name: str, emulate_on_client=False):
|
|
1262
|
+
if emulate_on_client:
|
|
1263
|
+
self.exclude_processes_emulate[process_name] = process_name
|
|
1264
|
+
else:
|
|
1265
|
+
self._exclude_processes.append(process_name)
|
|
1266
|
+
|
|
1267
|
+
def set_exclude_broadcast(self, exclude=True):
|
|
1268
|
+
self._exclude_broadcast = exclude
|
|
1269
|
+
|
|
1270
|
+
def set_exclude_multicast(self, exclude=True):
|
|
1271
|
+
self._exclude_multicast = exclude
|
|
1272
|
+
|
|
1273
|
+
def set_time_from(self, time: datetime):
|
|
1274
|
+
self._time_from = time
|
|
1275
|
+
|
|
1276
|
+
def set_time_from_x_seconds_ago(self, seconds: int):
|
|
1277
|
+
self._time_from = datetime.now(timezone.utc) - timedelta(seconds=seconds)
|
|
1278
|
+
|
|
1279
|
+
def set_time_from_x_days_ago(self, days: int):
|
|
1280
|
+
return self.set_time_from_x_seconds_ago(days*60*60*24)
|
|
1281
|
+
|
|
1282
|
+
def set_max_results(self, max: int):
|
|
1283
|
+
self.max_results = max
|
|
1284
|
+
|
|
1285
|
+
def set_time_to(self, time: datetime):
|
|
1286
|
+
self._time_to = time
|
|
1287
|
+
|
|
1288
|
+
def set_time_to_x_seconds_ago(self, seconds: int):
|
|
1289
|
+
self._time_to = datetime.now(timezone.utc) - timedelta(seconds=seconds)
|
|
1290
|
+
|
|
1291
|
+
def set_time_to_x_days_ago(self, days: int):
|
|
1292
|
+
return self.set_time_to_x_seconds_ago(days*60*60*24)
|
|
1293
|
+
|
|
1294
|
+
def filter_on_policy_decision_unknown(self):
|
|
1295
|
+
self._policy_decision_filter.append('unknown')
|
|
1296
|
+
|
|
1297
|
+
def filter_on_policy_decision_blocked(self):
|
|
1298
|
+
self._policy_decision_filter.append('blocked')
|
|
1299
|
+
|
|
1300
|
+
def filter_on_policy_decision_potentially_blocked(self):
|
|
1301
|
+
self._policy_decision_filter.append('potentially_blocked')
|
|
1302
|
+
|
|
1303
|
+
def filter_on_policy_decision_all_blocked(self):
|
|
1304
|
+
self.filter_on_policy_decision_blocked()
|
|
1305
|
+
self.filter_on_policy_decision_potentially_blocked()
|
|
1306
|
+
|
|
1307
|
+
def filter_on_policy_decision_allowed(self):
|
|
1308
|
+
self._policy_decision_filter.append('allowed')
|
|
1309
|
+
|
|
1310
|
+
def generate_json_query(self):
|
|
1311
|
+
"""
|
|
1312
|
+
Generate the JSON query payload for this filter set
|
|
1313
|
+
:return:
|
|
1314
|
+
"""
|
|
1315
|
+
filters = {
|
|
1316
|
+
"sources": {"include": [], "exclude": []},
|
|
1317
|
+
"destinations": {"include": [], "exclude": []},
|
|
1318
|
+
"services": {"include": [], "exclude": []},
|
|
1319
|
+
"sources_destinations_query_op": "and",
|
|
1320
|
+
"policy_decisions": self._policy_decision_filter,
|
|
1321
|
+
"max_results": self.max_results,
|
|
1322
|
+
"query_name": "api call"
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
if self._exclude_broadcast:
|
|
1326
|
+
filters['destinations']['exclude'].append({'transmission': 'broadcast'})
|
|
1327
|
+
|
|
1328
|
+
if self._exclude_multicast:
|
|
1329
|
+
filters['destinations']['exclude'].append({'transmission': 'multicast'})
|
|
1330
|
+
|
|
1331
|
+
if self._time_from is not None:
|
|
1332
|
+
filters["start_date"] = self._time_from.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
1333
|
+
else:
|
|
1334
|
+
filters["start_date"] = "2010-10-13T11:27:28.824Z",
|
|
1335
|
+
|
|
1336
|
+
if self._time_to is not None:
|
|
1337
|
+
filters["end_date"] = self._time_to.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
1338
|
+
else:
|
|
1339
|
+
filters["end_date"] = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
1340
|
+
|
|
1341
|
+
if len(self._consumer_exclude_labels) > 0:
|
|
1342
|
+
for label_href in self._consumer_exclude_labels.keys():
|
|
1343
|
+
filters['sources']['exclude'].append({'label': {'href': label_href}})
|
|
1344
|
+
|
|
1345
|
+
if len(self._consumer_iplists_exclude) > 0:
|
|
1346
|
+
for iplist_href in self._consumer_iplists_exclude.keys():
|
|
1347
|
+
filters['sources']['exclude'].append({'ip_list': {'href': iplist_href}})
|
|
1348
|
+
|
|
1349
|
+
if len(self.__filter_consumer_ip_exclude) > 0:
|
|
1350
|
+
for ipaddress in self.__filter_consumer_ip_exclude:
|
|
1351
|
+
filters['sources']['exclude'].append({'ip_address': ipaddress})
|
|
1352
|
+
|
|
1353
|
+
if len(self._provider_exclude_labels) > 0:
|
|
1354
|
+
for label_href in self._provider_exclude_labels.keys():
|
|
1355
|
+
filters['destinations']['exclude'].append({'label': {'href': label_href}})
|
|
1356
|
+
|
|
1357
|
+
if len(self._provider_iplists_exclude) > 0:
|
|
1358
|
+
for iplist_href in self._provider_iplists_exclude.keys():
|
|
1359
|
+
filters['destinations']['exclude'].append({'ip_list': {'href': iplist_href}})
|
|
1360
|
+
|
|
1361
|
+
if len(self.__filter_provider_ip_exclude) > 0:
|
|
1362
|
+
for ipaddress in self.__filter_provider_ip_exclude:
|
|
1363
|
+
filters['destinations']['exclude'].append({'ip_address': ipaddress})
|
|
1364
|
+
|
|
1365
|
+
if len(self._include_direct_services) > 0:
|
|
1366
|
+
for service in self._include_direct_services:
|
|
1367
|
+
filters['services']['include'] .append(service.get_api_json())
|
|
1368
|
+
|
|
1369
|
+
if len(self._exclude_direct_services) > 0:
|
|
1370
|
+
for service in self._exclude_direct_services:
|
|
1371
|
+
filters['services']['exclude'].append(service.get_api_json())
|
|
1372
|
+
|
|
1373
|
+
if len(self._exclude_processes) > 0:
|
|
1374
|
+
for process in self._exclude_processes:
|
|
1375
|
+
filters['services']['exclude'].append({'process_name': process})
|
|
1376
|
+
|
|
1377
|
+
for source_filter in self._source_filters:
|
|
1378
|
+
source_payloads = source_filter.generate_json_payloads()
|
|
1379
|
+
for payload in source_payloads:
|
|
1380
|
+
filters['sources']['include'].append(payload)
|
|
1381
|
+
|
|
1382
|
+
for destination_filter in self._destination_filters:
|
|
1383
|
+
destination_payloads = destination_filter.generate_json_payloads()
|
|
1384
|
+
for payload in destination_payloads:
|
|
1385
|
+
filters['destinations']['include'].append(payload)
|
|
1386
|
+
|
|
1387
|
+
# empty sources/destinations include will return no results, so we add an empty array to mean "any"
|
|
1388
|
+
if len(filters['sources']['include']) == 0:
|
|
1389
|
+
filters['sources']['include'].append([])
|
|
1390
|
+
if len(filters['destinations']['include']) == 0:
|
|
1391
|
+
filters['destinations']['include'].append([])
|
|
1392
|
+
|
|
1393
|
+
return filters
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
class ExplorerFilterSetV2Filter:
|
|
1397
|
+
"""
|
|
1398
|
+
A single filter for ExplorerFilterSetV2. The filter can contain multiple types items which will be treated differently
|
|
1399
|
+
"""
|
|
1400
|
+
def __init__(self):
|
|
1401
|
+
self._labels_href_by_type: Dict[str, Set[str]] = {} # key is label type (e.g., 'environment'), value is set of label hrefs
|
|
1402
|
+
self._workloads_href: Set[str] = set()
|
|
1403
|
+
self._iplists_href: Set[str] = set()
|
|
1404
|
+
|
|
1405
|
+
def add_label(self, label: Union['pylo.Label', 'pylo.LabelGroup']):
|
|
1406
|
+
label_type = label.type
|
|
1407
|
+
if label_type not in self._labels_href_by_type:
|
|
1408
|
+
self._labels_href_by_type[label_type] = set()
|
|
1409
|
+
self._labels_href_by_type[label_type].add(label.href)
|
|
1410
|
+
|
|
1411
|
+
def add_workload(self, workload: 'pylo.Workload'):
|
|
1412
|
+
self._workloads_href.add(workload.href)
|
|
1413
|
+
|
|
1414
|
+
def add_iplist(self, iplist: 'pylo.IPList'):
|
|
1415
|
+
self._iplists_href.add(iplist.href)
|
|
1416
|
+
|
|
1417
|
+
def generate_json_payloads(self):
|
|
1418
|
+
"""
|
|
1419
|
+
Generate the JSON payloads for this filter. It may return more than 1 payload in the result array because some items
|
|
1420
|
+
like labels of different types combination require a cartesian product to be generated.
|
|
1421
|
+
:return:
|
|
1422
|
+
"""
|
|
1423
|
+
result_payloads = []
|
|
1424
|
+
|
|
1425
|
+
# First, generate all combinations of labels by type
|
|
1426
|
+
label_type_keys = list(self._labels_href_by_type.keys())
|
|
1427
|
+
label_combinations = [[]]
|
|
1428
|
+
for label_type in label_type_keys:
|
|
1429
|
+
new_combinations = []
|
|
1430
|
+
for href in self._labels_href_by_type[label_type]:
|
|
1431
|
+
for existing_combination in label_combinations:
|
|
1432
|
+
new_combination = existing_combination + [(label_type, href)]
|
|
1433
|
+
new_combinations.append(new_combination)
|
|
1434
|
+
label_combinations = new_combinations
|
|
1435
|
+
|
|
1436
|
+
if len(label_combinations) == 0:
|
|
1437
|
+
label_combinations = [[]] # Ensure at least one combination exists so payloads can be generated when no labels are present
|
|
1438
|
+
|
|
1439
|
+
for label_combination in label_combinations:
|
|
1440
|
+
payload = []
|
|
1441
|
+
for label_type, href in label_combination:
|
|
1442
|
+
payload.append({'label': {'type': label_type, 'href': href}})
|
|
1443
|
+
|
|
1444
|
+
for workload_href in self._workloads_href:
|
|
1445
|
+
payload.append({'workload': {'href': workload_href}})
|
|
1446
|
+
for iplist_href in self._iplists_href:
|
|
1447
|
+
payload.append({'ip_list': {'href': iplist_href}})
|
|
1448
|
+
|
|
1449
|
+
result_payloads.append(payload)
|
|
1450
|
+
|
|
1451
|
+
return result_payloads
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
class ExplorerResultV2:
|
|
1455
|
+
|
|
1456
|
+
def __init__(self, data: ExplorerTrafficRecordJsonStructure):
|
|
1457
|
+
self.raw_json = data
|
|
1458
|
+
self.num_connections = data['num_connections']
|
|
1459
|
+
|
|
1460
|
+
self.policy_decision_string = data['policy_decision']
|
|
1461
|
+
self._draft_mode_policy_decision = data.get('draft_policy_decision')
|
|
1462
|
+
|
|
1463
|
+
self.source_ip_fqdn: Optional[str] = None
|
|
1464
|
+
self.destination_ip_fqdn: Optional[str] = None
|
|
1465
|
+
|
|
1466
|
+
src = data['src']
|
|
1467
|
+
self.source_ip: str = src['ip']
|
|
1468
|
+
self._source_iplists = src.get('ip_lists')
|
|
1469
|
+
self._source_iplists_href: List[str] = []
|
|
1470
|
+
if self._source_iplists is not None:
|
|
1471
|
+
for href in self._source_iplists:
|
|
1472
|
+
self._source_iplists_href.append(href['href'])
|
|
1473
|
+
|
|
1474
|
+
self.source_workload_href: Optional[str] = None
|
|
1475
|
+
self.source_workload_hostname: Optional[str] = None
|
|
1476
|
+
self.source_workload_labels_by_type: Dict[str, str] = {} # key is label type, value is label name
|
|
1477
|
+
workload_data = src.get('workload')
|
|
1478
|
+
if workload_data is not None:
|
|
1479
|
+
self.source_workload_href: Optional[str] = workload_data.get('href')
|
|
1480
|
+
if self.source_workload_href is None:
|
|
1481
|
+
raise pylo.PyloApiUnexpectedSyntax("Explorer API has return a record referring to a Workload with no HREF given:", data)
|
|
1482
|
+
|
|
1483
|
+
self.source_workload_hostname = workload_data.get('hostname')
|
|
1484
|
+
|
|
1485
|
+
self.source_workload_labels_href: Optional[List[str]] = []
|
|
1486
|
+
workload_labels_data = workload_data.get('labels')
|
|
1487
|
+
if workload_labels_data is not None:
|
|
1488
|
+
for label_data in workload_labels_data:
|
|
1489
|
+
self.source_workload_labels_href.append(label_data.get('href'))
|
|
1490
|
+
label_type = label_data.get('key')
|
|
1491
|
+
label_name = label_data.get('value')
|
|
1492
|
+
if label_type is not None and label_name is not None:
|
|
1493
|
+
self.source_workload_labels_by_type[label_type] = label_name
|
|
1494
|
+
|
|
1495
|
+
dst = data['dst']
|
|
1496
|
+
self.destination_ip: str = dst['ip']
|
|
1497
|
+
self.destination_ip_fqdn = dst.get('fqdn')
|
|
1498
|
+
self._destination_iplists = dst.get('ip_lists')
|
|
1499
|
+
self._destination_iplists_href: List[str] = []
|
|
1500
|
+
if self._destination_iplists is not None:
|
|
1501
|
+
for href in self._destination_iplists:
|
|
1502
|
+
self._destination_iplists_href.append(href['href'])
|
|
1503
|
+
|
|
1504
|
+
self.destination_workload_href: Optional[str] = None
|
|
1505
|
+
self.destination_workload_hostname: Optional[str] = None
|
|
1506
|
+
self.destination_workload_labels_by_type: Dict[str, str] = {} # key is label type, value is label name
|
|
1507
|
+
workload_data = dst.get('workload')
|
|
1508
|
+
if workload_data is not None:
|
|
1509
|
+
self.destination_workload_href = workload_data.get('href')
|
|
1510
|
+
if self.destination_workload_href is None:
|
|
1511
|
+
raise pylo.PyloApiUnexpectedSyntax("Explorer API has return a record referring to a Workload with no HREF given:", data)
|
|
1512
|
+
|
|
1513
|
+
self.destination_workload_hostname = workload_data.get('hostname')
|
|
1514
|
+
|
|
1515
|
+
self.destination_workload_labels_href: Optional[List[str]] = []
|
|
1516
|
+
workload_labels_data = workload_data.get('labels')
|
|
1517
|
+
if workload_labels_data is not None:
|
|
1518
|
+
for label_data in workload_labels_data:
|
|
1519
|
+
self.destination_workload_labels_href.append(label_data.get('href'))
|
|
1520
|
+
label_type = label_data.get('key')
|
|
1521
|
+
label_name = label_data.get('value')
|
|
1522
|
+
if label_type is not None and label_name is not None:
|
|
1523
|
+
self.destination_workload_labels_by_type[label_type] = label_name
|
|
1524
|
+
|
|
1525
|
+
service_json = data['service']
|
|
1526
|
+
self.service_json = service_json
|
|
1527
|
+
|
|
1528
|
+
self.service_protocol: int = service_json['proto']
|
|
1529
|
+
self.service_port: Optional[int] = service_json.get('port')
|
|
1530
|
+
self.process_name: Optional[str] = service_json.get('process_name')
|
|
1531
|
+
self.username: Optional[str] = service_json.get('user_name')
|
|
1532
|
+
|
|
1533
|
+
self.first_detected: str = data['timestamp_range']['first_detected'] # e.g., "2023-10-05T12:34:56Z" ISO 8601
|
|
1534
|
+
self.last_detected: str = data['timestamp_range']['last_detected'] # e.g., "2023-10-05T12:39:56Z" ISO 8601
|
|
1535
|
+
|
|
1536
|
+
self._cast_type: Optional[str] = data.get('transmission')
|
|
1537
|
+
|
|
1538
|
+
def service_to_str(self, protocol_first=True):
|
|
1539
|
+
if protocol_first:
|
|
1540
|
+
if self.service_port is None or self.service_port == 0:
|
|
1541
|
+
return 'proto/{}'.format(self.service_protocol)
|
|
1542
|
+
|
|
1543
|
+
if self.service_protocol == 17:
|
|
1544
|
+
return 'udp/{}'.format(self.service_port)
|
|
1545
|
+
|
|
1546
|
+
if self.service_protocol == 6:
|
|
1547
|
+
return 'tcp/{}'.format(self.service_port)
|
|
1548
|
+
else:
|
|
1549
|
+
if self.service_port is None or self.service_port == 0:
|
|
1550
|
+
return '{}/proto'.format(self.service_protocol)
|
|
1551
|
+
|
|
1552
|
+
if self.service_protocol == 17:
|
|
1553
|
+
return '{}/udp'.format(self.service_port)
|
|
1554
|
+
|
|
1555
|
+
if self.service_protocol == 6:
|
|
1556
|
+
return '{}/tcp'.format(self.service_port)
|
|
1557
|
+
|
|
1558
|
+
def service_to_str_array(self):
|
|
1559
|
+
if self.service_port is None or self.service_port == 0:
|
|
1560
|
+
return [self.service_protocol, 'proto']
|
|
1561
|
+
|
|
1562
|
+
if self.service_protocol == 17:
|
|
1563
|
+
return [self.service_port, 'udp']
|
|
1564
|
+
|
|
1565
|
+
if self.service_protocol == 6:
|
|
1566
|
+
return [self.service_port, 'tcp']
|
|
1567
|
+
|
|
1568
|
+
return ['n/a', 'n/a']
|
|
1569
|
+
|
|
1570
|
+
def source_is_workload(self):
|
|
1571
|
+
return self.source_workload_href is not None
|
|
1572
|
+
|
|
1573
|
+
def destination_is_workload(self):
|
|
1574
|
+
return self.destination_workload_href is not None
|
|
1575
|
+
|
|
1576
|
+
def get_source_workload_href(self):
|
|
1577
|
+
return self.source_workload_href
|
|
1578
|
+
|
|
1579
|
+
def get_destination_workload_href(self):
|
|
1580
|
+
return self.destination_workload_href
|
|
1581
|
+
|
|
1582
|
+
def get_source_workload(self, org_for_resolution: 'pylo.Organization') -> Optional['pylo.Workload']:
|
|
1583
|
+
if self.source_workload_href is None:
|
|
1584
|
+
return None
|
|
1585
|
+
return org_for_resolution.WorkloadStore.find_by_href_or_create_tmp(self.source_workload_href, '*DELETED*')
|
|
1586
|
+
|
|
1587
|
+
def get_destination_workload(self, org_for_resolution: 'pylo.Organization') -> Optional['pylo.Workload']:
|
|
1588
|
+
if self.destination_workload_href is None:
|
|
1589
|
+
return None
|
|
1590
|
+
return org_for_resolution.WorkloadStore.find_by_href_or_create_tmp(self.destination_workload_href, '*DELETED*')
|
|
1591
|
+
|
|
1592
|
+
def get_source_labels_href(self) -> Optional[List[str]]:
|
|
1593
|
+
if not self.source_is_workload():
|
|
1594
|
+
return None
|
|
1595
|
+
return self.source_workload_labels_href
|
|
1596
|
+
|
|
1597
|
+
def get_destination_labels_href(self) -> Optional[List[str]]:
|
|
1598
|
+
if not self.destination_is_workload():
|
|
1599
|
+
return None
|
|
1600
|
+
return self.destination_workload_labels_href
|
|
1601
|
+
|
|
1602
|
+
def get_source_iplists(self, org_for_resolution: 'pylo.Organization') ->Dict[str, 'pylo.IPList']:
|
|
1603
|
+
if self._source_iplists is None:
|
|
1604
|
+
return {}
|
|
1605
|
+
|
|
1606
|
+
result = {}
|
|
1119
1607
|
|
|
1608
|
+
for record in self._source_iplists:
|
|
1609
|
+
href = record.get('href')
|
|
1610
|
+
if href is None:
|
|
1611
|
+
raise pylo.PyloEx('Cannot find HREF for IPList in Explorer result json', record)
|
|
1612
|
+
iplist = org_for_resolution.IPListStore.find_by_href(href)
|
|
1613
|
+
if iplist is None:
|
|
1614
|
+
raise pylo.PyloEx('Cannot find HREF for IPList in Explorer result json', record)
|
|
1120
1615
|
|
|
1616
|
+
result[href] = iplist
|
|
1617
|
+
|
|
1618
|
+
return result
|
|
1619
|
+
|
|
1620
|
+
def get_source_iplists_href(self) -> Optional[List[str]]:
|
|
1621
|
+
if self.source_is_workload():
|
|
1622
|
+
return None
|
|
1623
|
+
if self._source_iplists_href is None:
|
|
1624
|
+
return []
|
|
1625
|
+
return self._source_iplists_href.copy()
|
|
1626
|
+
|
|
1627
|
+
def get_destination_iplists_href(self) -> Optional[List[str]]:
|
|
1628
|
+
if self.destination_is_workload():
|
|
1629
|
+
return None
|
|
1630
|
+
|
|
1631
|
+
if self._destination_iplists_href is None:
|
|
1632
|
+
return []
|
|
1633
|
+
return self._destination_iplists_href.copy()
|
|
1634
|
+
|
|
1635
|
+
def get_destination_iplists(self, org_for_resolution: 'pylo.Organization') -> Dict[str, 'pylo.IPList']:
|
|
1636
|
+
if self._destination_iplists is None:
|
|
1637
|
+
return {}
|
|
1638
|
+
|
|
1639
|
+
result = {}
|
|
1640
|
+
|
|
1641
|
+
for record in self._destination_iplists:
|
|
1642
|
+
href = record.get('href')
|
|
1643
|
+
if href is None:
|
|
1644
|
+
raise pylo.PyloEx('Cannot find HREF for IPList in Explorer result json', record)
|
|
1645
|
+
iplist = org_for_resolution.IPListStore.find_by_href(href)
|
|
1646
|
+
if iplist is None:
|
|
1647
|
+
raise pylo.PyloEx('Cannot find HREF for IPList in Explorer result json', record)
|
|
1648
|
+
|
|
1649
|
+
result[href] = iplist
|
|
1650
|
+
|
|
1651
|
+
return result
|
|
1652
|
+
|
|
1653
|
+
def pd_is_potentially_blocked(self):
|
|
1654
|
+
return self.policy_decision_string == 'potentially_blocked'
|
|
1655
|
+
|
|
1656
|
+
def cast_is_broadcast(self):
|
|
1657
|
+
return self._cast_type == 'broadcast'
|
|
1658
|
+
|
|
1659
|
+
def cast_is_multicast(self):
|
|
1660
|
+
return self._cast_type == 'multicast'
|
|
1661
|
+
|
|
1662
|
+
def cast_is_unicast(self):
|
|
1663
|
+
return self._cast_type is not None
|
|
1664
|
+
|
|
1665
|
+
def draft_mode_policy_decision_is_blocked(self) -> Optional[bool]:
|
|
1666
|
+
"""
|
|
1667
|
+
@return: None if draft_mode was not enabled
|
|
1668
|
+
"""
|
|
1669
|
+
return self._draft_mode_policy_decision is not None and \
|
|
1670
|
+
(self._draft_mode_policy_decision == 'blocked' or self._draft_mode_policy_decision == 'blocked_by_boundary')
|
|
1671
|
+
|
|
1672
|
+
def draft_mode_policy_decision_is_allowed(self) -> Optional[bool]:
|
|
1673
|
+
"""
|
|
1674
|
+
@return: None if draft_mode was not enabled
|
|
1675
|
+
"""
|
|
1676
|
+
return self._draft_mode_policy_decision is not None and self._draft_mode_policy_decision == "allowed"
|
|
1677
|
+
|
|
1678
|
+
def draft_mode_policy_decision_is_unavailable(self) -> Optional[bool]:
|
|
1679
|
+
"""
|
|
1680
|
+
@return: None if draft_mode was not enabled
|
|
1681
|
+
"""
|
|
1682
|
+
return self._draft_mode_policy_decision is None
|
|
1683
|
+
|
|
1684
|
+
def draft_mode_policy_decision_is_not_defined(self) -> Optional[bool]:
|
|
1685
|
+
return self._draft_mode_policy_decision is None
|
|
1686
|
+
|
|
1687
|
+
def draft_mode_policy_decision_to_str(self) -> str:
|
|
1688
|
+
if self._draft_mode_policy_decision is None:
|
|
1689
|
+
return 'not_available'
|
|
1690
|
+
return self._draft_mode_policy_decision
|
|
1691
|
+
|
|
1692
|
+
|
|
1693
|
+
class ExplorerResultSetV2:
|
|
1694
|
+
def __init__(self, data_array: ExplorerTrafficRecordsApiReplyPayloadJsonStructure):
|
|
1695
|
+
self.raw_json = data_array
|
|
1696
|
+
self.records: List[ExplorerResultV2] = []
|
|
1697
|
+
for record_json in data_array:
|
|
1698
|
+
record = ExplorerResultV2(record_json)
|
|
1699
|
+
self.records.append(record)
|
|
1700
|
+
|
|
1701
|
+
def get_all_records(self) -> List[ExplorerResultV2]:
|
|
1702
|
+
return self.records
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
class ExplorerQueryV2:
|
|
1706
|
+
def __init__(self, connector: APIConnector, max_results: int = 1500, draft_mode_enabled=False, max_running_time_seconds: int = 1800,
|
|
1707
|
+
check_for_update_interval_seconds: int = 10):
|
|
1708
|
+
self.api: APIConnector = connector
|
|
1709
|
+
self.filters = ExplorerFilterSetV2(max_results=max_results)
|
|
1710
|
+
self.max_running_time_seconds = max_running_time_seconds
|
|
1711
|
+
self.check_for_update_interval_seconds = check_for_update_interval_seconds
|
|
1712
|
+
self.draft_mode_enabled = draft_mode_enabled
|
|
1713
|
+
|
|
1714
|
+
def execute(self) -> Union[ExplorerResultSetV2]:
|
|
1715
|
+
"""
|
|
1716
|
+
Execute the query and stores the results in the 'results' property.
|
|
1717
|
+
It will also return said results for convenience.
|
|
1718
|
+
:return:
|
|
1719
|
+
"""
|
|
1720
|
+
self.results = self.api.explorer_search(self.filters, max_running_time_seconds=self.max_running_time_seconds,
|
|
1721
|
+
check_for_update_interval_seconds=self.check_for_update_interval_seconds,
|
|
1722
|
+
draft_mode_enabled=self.draft_mode_enabled)
|
|
1723
|
+
|
|
1724
|
+
|
|
1725
|
+
return self.results
|
|
1121
1726
|
|