illumio-pylo 0.3.4__tar.gz → 0.3.6__tar.gz

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.
Files changed (110) hide show
  1. {illumio_pylo-0.3.4/illumio_pylo.egg-info → illumio_pylo-0.3.6}/PKG-INFO +1 -1
  2. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/APIConnector.py +19 -13
  3. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/CredentialsManager.py +61 -42
  4. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/AgentStore.py +3 -0
  5. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/IPList.py +1 -7
  6. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/IPMap.py +3 -0
  7. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/LabelCommon.py +2 -0
  8. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/LabelGroup.py +2 -0
  9. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/LabelStore.py +2 -0
  10. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/LabeledObject.py +2 -0
  11. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Organization.py +4 -1
  12. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Rule.py +15 -0
  13. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Ruleset.py +6 -0
  14. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/RulesetStore.py +2 -0
  15. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/SecurityPrincipal.py +3 -0
  16. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Service.py +8 -0
  17. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/SoftwareVersion.py +1 -3
  18. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/VirtualService.py +3 -0
  19. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/VirtualServiceStore.py +2 -0
  20. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Workload.py +3 -0
  21. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/WorkloadStore.py +2 -0
  22. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/WorkloadStoreSubClasses.py +1 -0
  23. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/__init__.py +2 -5
  24. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/credential_manager.py +13 -14
  25. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/tmp.py +27 -8
  26. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6/illumio_pylo.egg-info}/PKG-INFO +1 -1
  27. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.devcontainer/Dockerfile +0 -0
  28. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.devcontainer/devcontainer.json +0 -0
  29. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.gitattributes +0 -0
  30. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.github/workflows/doxygen-publish.yml +0 -0
  31. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.github/workflows/make-binaries.yml +0 -0
  32. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.github/workflows/python-publish.yml +0 -0
  33. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/.gitignore +0 -0
  34. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/LICENSE +0 -0
  35. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/README.md +0 -0
  36. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/check_unique_hostnames.py +0 -0
  37. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/check_unique_services.py +0 -0
  38. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/delete_all_workloads.py +0 -0
  39. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/delete_unused_services.py +0 -0
  40. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/explorer_report_exporter.py +0 -0
  41. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/export_rules_to_firewall.py +0 -0
  42. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/generate-random-workloads.py +0 -0
  43. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/healthcheck_log.py +0 -0
  44. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/import-labels.py +0 -0
  45. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/import_workloads_placeholders.py +0 -0
  46. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/iplists_stats_duplicates_unused_finder.py +0 -0
  47. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/recalculate_explorer_logs.py +0 -0
  48. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/recalculate_explorer_logs_multithreaded.py +0 -0
  49. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/rules_exporter.py +0 -0
  50. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/rules_exporter_special.py +0 -0
  51. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/test.py +0 -0
  52. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/test_change_workload_desc.py +0 -0
  53. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/test_query.py +0 -0
  54. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/test_query2.py +0 -0
  55. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/test_securityprincipals.py +0 -0
  56. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/ven_idle_to_illumination.py +0 -0
  57. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/dev_playground/ven_reassign_pce.py +0 -0
  58. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/examples/explorer_query.py +0 -0
  59. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/examples/extend_cli.py +0 -0
  60. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/AuditLog.py +0 -0
  61. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/ClusterHealth.py +0 -0
  62. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/Explorer.py +0 -0
  63. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/JsonPayloadTypes.py +0 -0
  64. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/RuleSearchQuery.py +0 -0
  65. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/API/__init__.py +0 -0
  66. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Exception.py +0 -0
  67. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Helpers/__init__.py +0 -0
  68. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Helpers/exports.py +0 -0
  69. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Helpers/functions.py +0 -0
  70. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Label.py +0 -0
  71. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/Query.py +0 -0
  72. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/ReferenceTracker.py +0 -0
  73. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/NativeParsers.py +0 -0
  74. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/__init__.py +0 -0
  75. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/__main__.py +0 -0
  76. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/__init__.py +0 -0
  77. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/iplist_analyzer.py +0 -0
  78. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/iplist_import_from_file.py +0 -0
  79. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/ruleset_export.py +0 -0
  80. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/update_pce_objects_cache.py +0 -0
  81. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/utils/LabelCreation.py +0 -0
  82. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/utils/__init__.py +0 -0
  83. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/utils/misc.py +0 -0
  84. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/ven_compatibility_report_export.py +0 -0
  85. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/ven_duplicate_remover.py +0 -0
  86. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/ven_idle_to_visibility.py +0 -0
  87. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/ven_upgrader.py +0 -0
  88. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/workload_export.py +0 -0
  89. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/workload_import.py +0 -0
  90. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/workload_reset_names_to_null.py +0 -0
  91. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/workload_update.py +0 -0
  92. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/cli/commands/workload_used_in_rule_finder.py +0 -0
  93. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/docs/Doxygen +0 -0
  94. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/__init__.py +0 -0
  95. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/cli.py +0 -0
  96. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/credentials.example.json +0 -0
  97. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/health_monitoring.py +0 -0
  98. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/resources/iplists-import-example.csv +0 -0
  99. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  100. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +0 -0
  101. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/resources/workloads-import-example.csv +0 -0
  102. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  103. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo.egg-info/SOURCES.txt +0 -0
  104. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo.egg-info/dependency_links.txt +0 -0
  105. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo.egg-info/requires.txt +0 -0
  106. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/illumio_pylo.egg-info/top_level.txt +0 -0
  107. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/pyproject.toml +0 -0
  108. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/requirements.txt +0 -0
  109. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/setup.cfg +0 -0
  110. {illumio_pylo-0.3.4 → illumio_pylo-0.3.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: A set of tools and library for working with Illumio PCE
5
5
  Home-page: https://github.com/cpainchaud/pylo
6
6
  Author: Christophe Painchaud
@@ -625,12 +625,14 @@ class APIConnector:
625
625
  return None
626
626
 
627
627
  def objects_ven_get(self,
628
- include_deleted=False,
629
- filter_by_ip: str = None,
630
- filter_by_label: Optional[WorkloadsGetQueryLabelFilterJsonStructure] = None,
631
- filter_by_name: str = None,
632
- max_results: int = None,
633
- async_mode=True) -> List[VenObjectJsonStructure]:
628
+ include_deleted=False,
629
+ filter_by_ip: str = None,
630
+ filter_by_label: Optional[WorkloadsGetQueryLabelFilterJsonStructure] = None,
631
+ filter_by_name: str = None,
632
+ max_results: int = None,
633
+ async_mode=True,
634
+ representation: Optional[Literal['ven_labels']] = None
635
+ ) -> List[VenObjectJsonStructure]:
634
636
  path = '/vens'
635
637
  data = {}
636
638
 
@@ -647,12 +649,14 @@ class APIConnector:
647
649
  if filter_by_name is not None:
648
650
  data['name'] = filter_by_name
649
651
 
652
+ if representation is not None:
653
+ data['representation'] = representation
654
+
650
655
  if max_results is not None:
651
656
  data['max_results'] = max_results
652
657
 
653
658
  return self.do_get_call(path=path, async_call=async_mode, params=data)
654
659
 
655
-
656
660
  def objects_workload_get(self,
657
661
  include_deleted=False,
658
662
  filter_by_ip: str = None,
@@ -935,18 +939,20 @@ class APIConnector:
935
939
 
936
940
  return self.do_get_call(path=path, async_call=False, include_org_id=False )
937
941
 
938
- def object_network_device_endpoint_create(self, network_device_href: str, name: str, endpoint_type: Literal['switch_port'], workloads_href: List[str]) -> List[NetworkDeviceEndpointObjectJsonStructure]:
942
+ def object_network_device_endpoint_create(self, network_device_href: str, name: str,
943
+ endpoint_type: Literal['switch_port'], workloads_href: List[str]) \
944
+ -> List[NetworkDeviceEndpointObjectJsonStructure]:
945
+
939
946
  path = '{}/network_endpoints'.format(network_device_href)
940
947
 
941
- worklaods_href_objects = []
948
+ workloads_href_objects = []
942
949
  for workload_href in workloads_href:
943
- worklaods_href_objects.append({'href': workload_href})
950
+ workloads_href_objects.append({'href': workload_href})
944
951
 
945
- data = { 'config': { 'name': name, 'endpoint_type': endpoint_type }, 'workloads': worklaods_href_objects}
952
+ data = {'config': {'name': name, 'endpoint_type': endpoint_type}, 'workloads': workloads_href_objects}
946
953
 
947
954
  return self.do_post_call(path=path, async_call=False, include_org_id=False, json_arguments=data, json_output_expected=True)
948
955
 
949
-
950
956
  def objects_ruleset_get(self, max_results: int = None, async_mode=True) -> List[RulesetObjectJsonStructure]:
951
957
  path = '/sec_policy/draft/rule_sets'
952
958
  data = {}
@@ -1069,7 +1075,7 @@ class APIConnector:
1069
1075
  'ingress_services': services_json
1070
1076
  }
1071
1077
 
1072
- path = ruleset_href+'/sec_rules'
1078
+ path = ruleset_href + '/sec_rules'
1073
1079
 
1074
1080
  return self.do_post_call(path, json_arguments=data, json_output_expected=True, include_org_id=False)
1075
1081
 
@@ -4,6 +4,7 @@ from typing import Dict, TypedDict, Union, List, Optional
4
4
  import json
5
5
  import os
6
6
  from cryptography.fernet import Fernet
7
+ from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
7
8
  from ..Exception import PyloEx
8
9
  from .. import log
9
10
 
@@ -122,7 +123,7 @@ def get_credentials_from_file(fqdn_or_profile_name: str = None,
122
123
 
123
124
  if fail_with_an_exception:
124
125
  raise PyloEx("No profile found in credential file '{}' with fqdn: {}".
125
- format(credential_file, fqdn_or_profile_name))
126
+ format(credential_file, fqdn_or_profile_name))
126
127
 
127
128
  return None
128
129
 
@@ -133,8 +134,8 @@ def list_potential_credential_files() -> List[str]:
133
134
  :return:
134
135
  """
135
136
  potential_credential_files = []
136
- if os.environ.get('Pylo_CREDENTIAL_FILE', None) is not None:
137
- potential_credential_files.append(os.environ.get('Pylo_CREDENTIAL_FILE'))
137
+ if os.environ.get('PYLO_CREDENTIAL_FILE', None) is not None:
138
+ potential_credential_files.append(os.environ.get('PYLO_CREDENTIAL_FILE'))
138
139
  potential_credential_files.append(os.path.expanduser("~/.pylo/credentials.json"))
139
140
  potential_credential_files.append(os.path.join(os.getcwd(), "credentials.json"))
140
141
 
@@ -153,7 +154,7 @@ def get_all_credentials() -> List[CredentialProfile]:
153
154
  return credentials
154
155
 
155
156
 
156
- def create_credential_in_file(file_full_path: str, data: CredentialFileEntry, overwrite_existing_profile = False) -> str:
157
+ def create_credential_in_file(file_full_path: str, data: CredentialFileEntry, overwrite_existing_profile=False) -> str:
157
158
  """
158
159
  Create a credential in a file and return the full path to the file
159
160
  :param file_full_path:
@@ -173,7 +174,10 @@ def create_credential_in_file(file_full_path: str, data: CredentialFileEntry, ov
173
174
  for profile in credentials:
174
175
  if profile['name'].lower() == data['name'].lower():
175
176
  if overwrite_existing_profile:
176
- profile = data
177
+ # profile is a dict, remove of all its entries
178
+ for key in list(profile.keys()):
179
+ del profile[key]
180
+ profile.update(data)
177
181
  break
178
182
  else:
179
183
  raise PyloEx("Profile with name {} already exists in file {}".format(data['name'], file_full_path))
@@ -187,7 +191,7 @@ def create_credential_in_file(file_full_path: str, data: CredentialFileEntry, ov
187
191
  else:
188
192
  credentials = [credentials, data]
189
193
  else:
190
- credentials = [data]
194
+ credentials = [data]
191
195
 
192
196
  # write to the file
193
197
  with open(file_full_path, 'w') as f:
@@ -195,6 +199,7 @@ def create_credential_in_file(file_full_path: str, data: CredentialFileEntry, ov
195
199
 
196
200
  return file_full_path
197
201
 
202
+
198
203
  def create_credential_in_default_file(data: CredentialFileEntry) -> str:
199
204
  """
200
205
  Create a credential in the default credential file and return the full path to the file
@@ -206,32 +211,20 @@ def create_credential_in_default_file(data: CredentialFileEntry) -> str:
206
211
  return file_path
207
212
 
208
213
 
209
- def encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key: paramiko.AgentKey, api_key: str) -> str:
210
- def encrypt(raw: str, key: bytes) -> bytes:
211
- """
212
-
213
- :param raw:
214
- :param key:
215
- :return: base64 encoded encrypted string
216
- """
217
- f = Fernet(base64.urlsafe_b64encode(key))
218
- token = f.encrypt(bytes(raw, 'utf-8'))
219
- return token
220
-
221
-
222
- # generate a random 128bit key
223
- session_key_to_sign = os.urandom(32)
214
+ def encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(ssh_key: paramiko.AgentKey, api_key: str) -> str:
215
+ seed_key_to_be_signed = os.urandom(32)
216
+ signed_seed_key = ssh_key.sign_ssh_data(seed_key_to_be_signed)
217
+ encryption_key = sha256(signed_seed_key).digest()
224
218
 
225
- signed_message = ssh_key.sign_ssh_data(session_key_to_sign)
219
+ nonce = seed_key_to_be_signed[:12]
220
+ chacha20_object = ChaCha20Poly1305(encryption_key)
226
221
 
227
- # use SHA256 to hash the signed message and use it as final AES 256 key
228
- encryption_key = sha256(signed_message).digest()
229
- #print("Encryption key: {}".format(encryption_key.hex()))
230
- encrypted_text = encrypt(api_key, encryption_key)
222
+ encrypted_text = chacha20_object.encrypt(nonce, bytes(api_key, 'utf-8'), ssh_key.get_fingerprint())
231
223
 
232
- api_key = "$encrypted$:ssh-Fernet:{}:{}:{}".format(base64.urlsafe_b64encode(ssh_key.get_fingerprint()).decode('utf-8'),
233
- base64.urlsafe_b64encode(session_key_to_sign).decode('utf-8'),
234
- encrypted_text.decode('utf-8'))
224
+ api_key = "$encrypted$:ssh-ChaCha20Poly1305:{}:{}:{}".format(
225
+ base64.urlsafe_b64encode(ssh_key.get_fingerprint()).decode('utf-8'),
226
+ base64.urlsafe_b64encode(seed_key_to_be_signed).decode('utf-8'),
227
+ base64.urlsafe_b64encode(encrypted_text).decode('utf-8'))
235
228
 
236
229
  return api_key
237
230
 
@@ -251,36 +244,62 @@ def decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload: str)
251
244
  session_key = base64.urlsafe_b64decode(api_key_parts[3])
252
245
  encrypted_api_key = api_key_parts[4]
253
246
 
254
- # find the key in the agent
255
- keys = paramiko.Agent().get_keys()
256
- found_key = None
257
- for key in keys:
258
- if key.get_fingerprint() == fingerprint:
259
- found_key = key
260
- break
261
-
262
- if found_key is None:
247
+ ssh_key = find_ssh_key_from_fingerprint(fingerprint)
248
+ if ssh_key is None:
263
249
  raise PyloEx("No key found in the agent with fingerprint {}".format(fingerprint.hex()))
264
250
 
265
251
  # sign the session key
266
- signed_session_key = found_key.sign_ssh_data(session_key)
252
+ signed_session_key = ssh_key.sign_ssh_data(session_key)
267
253
  encryption_key = sha256(signed_session_key).digest()
268
- #print("Encryption key: {}".format(encryption_key.hex()))
269
- #print("Encrypted from KEY fingerprint: {}".format(fingerprint.hex()))
254
+ # print("Encryption key: {}".format(encryption_key.hex()))
255
+ # print("Encrypted from KEY fingerprint: {}".format(fingerprint.hex()))
270
256
 
271
257
  return decrypt(token_b64_encoded=encrypted_api_key,
272
258
  key=encryption_key
273
259
  )
274
260
 
261
+
262
+ def decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(encrypted_api_key_payload: str) -> str:
263
+ api_key_parts = encrypted_api_key_payload.split(":")
264
+ if len(api_key_parts) != 5:
265
+ raise PyloEx("Invalid encrypted API key format")
266
+
267
+ fingerprint = base64.urlsafe_b64decode(api_key_parts[2])
268
+ seed_key_to_be_signed = base64.urlsafe_b64decode(api_key_parts[3])
269
+ encrypted_api_key = base64.urlsafe_b64decode(api_key_parts[4])
270
+
271
+ ssh_key = find_ssh_key_from_fingerprint(fingerprint)
272
+ if ssh_key is None:
273
+ raise PyloEx("No key found in the agent with fingerprint {}".format(fingerprint.hex()))
274
+
275
+ signed_session_key = ssh_key.sign_ssh_data(seed_key_to_be_signed)
276
+ encryption_key = sha256(signed_session_key).digest()
277
+
278
+ chacha20_object = ChaCha20Poly1305(encryption_key)
279
+ nonce = seed_key_to_be_signed[:12]
280
+
281
+ return chacha20_object.decrypt(nonce, encrypted_api_key, ssh_key.get_fingerprint()).decode('utf-8')
282
+
283
+
275
284
  def decrypt_api_key(encrypted_api_key_payload: str) -> str:
276
285
  # detect the encryption method
277
286
  if not encrypted_api_key_payload.startswith("$encrypted$:"):
278
287
  raise PyloEx("Invalid encrypted API key format")
279
288
  if encrypted_api_key_payload.startswith("$encrypted$:ssh-Fernet:"):
280
289
  return decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload)
290
+ elif encrypted_api_key_payload.startswith("$encrypted$:ssh-ChaCha20Poly1305:"):
291
+ return decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(encrypted_api_key_payload)
281
292
 
282
293
  raise PyloEx("Unsupported encryption method: {}".format(encrypted_api_key_payload.split(":")[1]))
283
294
 
284
295
 
285
296
  def is_api_key_encrypted(encrypted_api_key_payload: str) -> bool:
286
- return encrypted_api_key_payload.startswith("$encrypted$:")
297
+ return encrypted_api_key_payload.startswith("$encrypted$:")
298
+
299
+
300
+ def find_ssh_key_from_fingerprint(fingerprint: bytes) -> Optional[paramiko.AgentKey]:
301
+ keys = paramiko.Agent().get_keys()
302
+ for key in keys:
303
+ if key.get_fingerprint() == fingerprint:
304
+ return key
305
+ return None
@@ -12,6 +12,9 @@ from typing import *
12
12
 
13
13
  class VENAgent(pylo.ReferenceTracker):
14
14
 
15
+ __slots__ = ['href', 'owner', 'workload', 'software_version', '_last_heartbeat', '_status_security_policy_sync_state',
16
+ '_status_security_policy_applied_at', '_status_rule_count', 'mode', 'raw_json']
17
+
15
18
  def __init__(self, href: str, owner: 'pylo.AgentStore', workload: 'pylo.Workload' = None):
16
19
  pylo.ReferenceTracker.__init__(self)
17
20
  self.href: str = href
@@ -8,13 +8,7 @@ from .Helpers import *
8
8
 
9
9
  class IPList(pylo.ReferenceTracker):
10
10
 
11
- """
12
- :type owner: IPListStore
13
- :type description: str|None
14
- :type raw_entries: dict[str,str]
15
- """
16
- name: str
17
- href: str
11
+ __slots__ = ['owner', 'name', 'href', 'description', 'raw_json', 'raw_entries']
18
12
 
19
13
  def __init__(self, name: str, href: str, owner: 'pylo.IPListStore', description=None):
20
14
  """
@@ -20,6 +20,9 @@ for i in range(32):
20
20
 
21
21
 
22
22
  class IP4Map:
23
+
24
+ __slots__ = ['_entries']
25
+
23
26
  def __init__(self):
24
27
  self._entries = []
25
28
 
@@ -5,6 +5,8 @@ from .LabelStore import label_type_role, label_type_env, label_type_loc, label_t
5
5
 
6
6
  class LabelCommon:
7
7
 
8
+ __slots__ = ['owner', 'name', 'href', 'type']
9
+
8
10
  def __init__(self, name: str, href: str, label_type: str, owner: LabelStore):
9
11
  self.owner: LabelStore = owner
10
12
  self.name: str = name
@@ -8,6 +8,8 @@ from .API.JsonPayloadTypes import LabelGroupObjectJsonStructure
8
8
 
9
9
  class LabelGroup(pylo.ReferenceTracker, pylo.LabelCommon):
10
10
 
11
+ __slots__ = ['_members_by_href', 'raw_json']
12
+
11
13
  def __init__(self, name: str, href: str, label_type: str, owner):
12
14
  pylo.ReferenceTracker.__init__(self)
13
15
  pylo.LabelCommon.__init__(self, name, href, label_type, owner)
@@ -47,6 +47,8 @@ class LabelStore:
47
47
  result.append(label)
48
48
  return result
49
49
 
50
+ __slots__ = ['owner', '_items_by_href', 'label_types', 'label_types_as_set', 'label_resolution_cache']
51
+
50
52
  def __init__(self, owner: 'pylo.Organization'):
51
53
  self.owner: "pylo.Organization" = owner
52
54
  self._items_by_href: Dict[str, Union[pylo.Label, pylo.LabelGroup]] = {}
@@ -4,6 +4,8 @@ import illumio_pylo as pylo
4
4
 
5
5
  class LabeledObject:
6
6
 
7
+ __slots__ = ['_labels']
8
+
7
9
  def __init__(self):
8
10
  self._labels: Dict[str, 'pylo.Label'] = {}
9
11
 
@@ -9,6 +9,9 @@ from .API.CredentialsManager import get_credentials_from_file
9
9
 
10
10
  class Organization:
11
11
 
12
+ __slots__ = ['id', 'connector', 'LabelStore', 'IPListStore', 'WorkloadStore', 'VirtualServiceStore', 'AgentStore',
13
+ 'ServiceStore', 'RulesetStore', 'SecurityPrincipalStore', 'pce_version']
14
+
12
15
  def __init__(self, org_id):
13
16
  self.id: int = org_id
14
17
  self.connector: Optional['pylo.APIConnector'] = None
@@ -58,7 +61,7 @@ class Organization:
58
61
  """
59
62
  Credentials files will be looked for in the following order:
60
63
  1. The path provided in the credential_file argument
61
- 2. The path provided in the Pylo_CREDENTIAL_FILE environment variable
64
+ 2. The path provided in the PYLO_CREDENTIAL_FILE environment variable
62
65
  3. The path ~/.pylo/credentials.json
63
66
  4. Current working directory credentials.json
64
67
  :param fqdn_or_profile_name:
@@ -28,6 +28,9 @@ class RuleApiUpdateStack:
28
28
 
29
29
  class Rule:
30
30
 
31
+ __slots__ = ['owner', 'description', 'services', 'providers', 'consumers', 'consuming_principals', 'href', 'enabled',
32
+ 'secure_connect', 'unscoped_consumers', 'stateless', 'machine_auth', 'raw_json', 'batch_update_stack']
33
+
31
34
  def __init__(self, owner: 'Ruleset'):
32
35
  self.owner: Ruleset = owner
33
36
  self.description: Optional[str] = None
@@ -123,6 +126,9 @@ class Rule:
123
126
 
124
127
 
125
128
  class RuleSecurityPrincipalContainer(pylo.Referencer):
129
+
130
+ __slots__ = ['owner', '_items']
131
+
126
132
  def __init__(self, owner: 'pylo.Rule'):
127
133
  Referencer.__init__(self)
128
134
  self.owner = owner
@@ -140,6 +146,9 @@ class RuleSecurityPrincipalContainer(pylo.Referencer):
140
146
 
141
147
 
142
148
  class DirectServiceInRule:
149
+
150
+ __slots__ = ['protocol', 'port', 'to_port']
151
+
143
152
  def __init__(self, proto: int, port: int = None, toport: int = None):
144
153
  self.protocol = proto
145
154
  self.port = port
@@ -245,6 +254,9 @@ class DirectServiceInRule:
245
254
 
246
255
 
247
256
  class RuleServiceContainer(pylo.Referencer):
257
+
258
+ __slots__ = ['owner', '_items', '_direct_services', '_cached_port_map']
259
+
248
260
  def __init__(self, owner: 'pylo.Rule'):
249
261
  Referencer.__init__(self)
250
262
  self.owner = owner
@@ -380,6 +392,9 @@ class RuleServiceContainer(pylo.Referencer):
380
392
 
381
393
 
382
394
  class RuleHostContainer(pylo.Referencer):
395
+
396
+ __slots__ = ['owner', '_items', 'name', '_hasAllWorkloads']
397
+
383
398
  def __init__(self, owner: 'pylo.Rule', name: str):
384
399
  Referencer.__init__(self)
385
400
  self.owner = owner
@@ -10,6 +10,8 @@ ruleset_id_extraction_regex = re.compile(r"^/orgs/([0-9]+)/sec_policy/([0-9]+)?(
10
10
 
11
11
  class RulesetScope:
12
12
 
13
+ __slots__ = ['owner', 'scope_entries']
14
+
13
15
  def __init__(self, owner: 'pylo.Ruleset'):
14
16
  self.owner: 'pylo.Ruleset' = owner
15
17
  self.scope_entries: Dict['pylo.RulesetScopeEntry', 'pylo.RulesetScopeEntry'] = {}
@@ -39,6 +41,8 @@ class RulesetScope:
39
41
 
40
42
  class RulesetScopeEntry:
41
43
 
44
+ __slots__ = ['owner', '_labels']
45
+
42
46
  def __init__(self, owner: 'pylo.RulesetScope'):
43
47
  self.owner: pylo.RulesetScope = owner
44
48
  self._labels: Dict[str, Union['pylo.Label', 'pylo.LabelGroup']] = {}
@@ -144,6 +148,8 @@ class RulesetScopeEntry:
144
148
 
145
149
  class Ruleset:
146
150
 
151
+ __slots__ = ['owner', 'href', 'name', 'description', 'scopes', '_rules_by_href', '_rules', 'disabled']
152
+
147
153
  def __init__(self, owner: 'pylo.RulesetStore'):
148
154
  self.owner: 'pylo.RulesetStore' = owner
149
155
  self.href: Optional[str] = None
@@ -7,6 +7,8 @@ from .Helpers import nice_json
7
7
 
8
8
  class RulesetStore:
9
9
 
10
+ __slots__ = ['owner', '_items_by_href']
11
+
10
12
  def __init__(self, owner: 'pylo.Organization'):
11
13
  self.owner: pylo.Organization = owner
12
14
  self._items_by_href: Dict[str, 'pylo.Ruleset'] = {}
@@ -5,6 +5,9 @@ from illumio_pylo import log
5
5
 
6
6
 
7
7
  class SecurityPrincipal(pylo.ReferenceTracker):
8
+
9
+ __slots__ = ['owner', 'name', 'href', 'sid', 'deleted', 'raw_json']
10
+
8
11
  def __init__(self, name: str, href: str, owner: 'pylo.SecurityPrincipalStore'):
9
12
  pylo.ReferenceTracker.__init__(self)
10
13
  self.owner: 'pylo.SecurityPrincipalStore' = owner
@@ -6,6 +6,9 @@ from typing import *
6
6
 
7
7
 
8
8
  class PortMap:
9
+
10
+ __slots__ = ['_tcp_map', '_udp_map', '_protocol_map']
11
+
9
12
  def __init__(self):
10
13
  self._tcp_map: List[List[2]] = [] # [start, end]
11
14
  self._udp_map: List[List[2]] = [] # [start, end]
@@ -99,6 +102,9 @@ class PortMap:
99
102
 
100
103
 
101
104
  class ServiceEntry:
105
+
106
+ __slots__ = ['protocol', 'port', 'to_port', 'icmp_code', 'icmp_type']
107
+
102
108
  def __init__(self, protocol: int, port: int = None, to_port: Optional[int] = None, icmp_code: Optional[int] = None,
103
109
  icmp_type: Optional[int] = None):
104
110
  self.protocol = protocol
@@ -160,6 +166,8 @@ class ServiceEntry:
160
166
 
161
167
  class Service(pylo.ReferenceTracker):
162
168
 
169
+ __slots__ = ['name', 'href', 'owner', 'entries', 'description', 'processName', 'deleted', 'raw_json']
170
+
163
171
  def __init__(self, name: str, href: str, owner: 'pylo.ServiceStore'):
164
172
  pylo.ReferenceTracker.__init__(self)
165
173
 
@@ -6,9 +6,7 @@ version_regex = re.compile(r"^(?P<major>[0-9]+)\.(?P<middle>[0-9]+)\.(?P<minor>[
6
6
 
7
7
  class SoftwareVersion:
8
8
 
9
- """
10
- :type version_string: str
11
- """
9
+ __slots__ = ['version_string', 'is_unknown', 'major', 'middle', 'minor', 'build']
12
10
 
13
11
  def __init__(self, version_string: str):
14
12
  self.version_string = version_string
@@ -4,6 +4,9 @@ import illumio_pylo as pylo
4
4
 
5
5
 
6
6
  class VirtualService(pylo.ReferenceTracker):
7
+
8
+ __slots__ = ['owner', 'name', 'href', 'raw_json']
9
+
7
10
  def __init__(self, name: str, href: str, owner: 'pylo.VirtualServiceStore'):
8
11
  pylo.ReferenceTracker.__init__(self)
9
12
  self.owner = owner
@@ -6,6 +6,8 @@ from .API.JsonPayloadTypes import VirtualServiceObjectJsonStructure
6
6
 
7
7
  class VirtualServiceStore:
8
8
 
9
+ __slots__ = ['owner', 'items_by_href', 'itemsByName']
10
+
9
11
  def __init__(self, owner: 'pylo.Organization'):
10
12
  self.owner: 'pylo.Organization' = owner
11
13
  self.items_by_href: Dict[str, 'pylo.VirtualService'] = {}
@@ -47,6 +47,9 @@ class WorkloadApiUpdateStack:
47
47
 
48
48
  class Workload(pylo.ReferenceTracker, pylo.Referencer, LabeledObject):
49
49
 
50
+ __slots__ = ['owner', 'name', 'href', 'forced_name', 'hostname', 'description', 'interfaces', 'online', 'os_id',
51
+ 'os_detail', 'ven_agent', 'unmanaged', 'temporary', 'deleted', 'raw_json', '_batch_update_stack']
52
+
50
53
  def __init__(self, name: str, href: str, owner: 'pylo.WorkloadStore'):
51
54
  ReferenceTracker.__init__(self)
52
55
  Referencer.__init__(self)
@@ -10,6 +10,8 @@ from .WorkloadStoreSubClasses import UnmanagedWorkloadDraft, UnmanagedWorkloadDr
10
10
 
11
11
  class WorkloadStore:
12
12
 
13
+ __slots__ = ['owner', 'itemsByHRef']
14
+
13
15
  def __init__(self, owner: 'Organization'):
14
16
  self.owner: Organization = owner
15
17
  self.itemsByHRef: Dict[str, Workload] = {}
@@ -66,6 +66,7 @@ class CreationTracker:
66
66
  workload: Optional[pylo.Workload] = None
67
67
  workload_href: Optional[str] = None
68
68
 
69
+
69
70
  class UnmanagedWorkloadDraftMultiCreatorManager:
70
71
  def __init__(self, owner: 'pylo.WorkloadStore'):
71
72
 
@@ -1,7 +1,5 @@
1
- __version__ = "0.3.4"
1
+ __version__ = "0.3.6"
2
2
 
3
- import os
4
- import sys
5
3
  from typing import Callable
6
4
 
7
5
  from .tmp import *
@@ -60,7 +58,7 @@ def get_organization_using_credential_file(fqdn_or_profile_name: str = None,
60
58
  """
61
59
  Credentials files will be looked for in the following order:
62
60
  1. The path provided in the credential_file argument
63
- 2. The path provided in the Pylo_CREDENTIAL_FILE environment variable
61
+ 2. The path provided in the PYLO_CREDENTIAL_FILE environment variable
64
62
  3. The path ~/.pylo/credentials.json
65
63
  4. Current working directory credentials.json
66
64
  :param fqdn_or_profile_name:
@@ -77,7 +75,6 @@ def get_organization_using_credential_file(fqdn_or_profile_name: str = None,
77
75
  callback_api_objects_downloaded=callback_api_objects_downloaded)
78
76
 
79
77
 
80
-
81
78
  ignoreWorkloadsWithSameName = True
82
79
 
83
80
  objectNotFound = object()
@@ -9,8 +9,9 @@ import paramiko
9
9
  import illumio_pylo as pylo
10
10
  import click
11
11
  from illumio_pylo.API.CredentialsManager import get_all_credentials, create_credential_in_file, CredentialFileEntry, \
12
- create_credential_in_default_file, encrypt_api_key_with_paramiko_ssh_key_fernet, decrypt_api_key_with_paramiko_ssh_key_fernet, \
13
- get_credentials_from_file
12
+ create_credential_in_default_file, \
13
+ get_credentials_from_file, encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305, \
14
+ decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305, decrypt_api_key_with_paramiko_ssh_key_fernet
14
15
 
15
16
  from illumio_pylo import log
16
17
  from . import Command
@@ -28,7 +29,6 @@ def fill_parser(parser: argparse.ArgumentParser):
28
29
  test_parser.add_argument('--name', required=False, type=str, default=None,
29
30
  help='Name of the credential profile to test')
30
31
 
31
-
32
32
  create_parser = sub_parser.add_parser('create', help='Create a new credential')
33
33
  create_parser.add_argument('--name', required=False, type=str, default=None,
34
34
  help='Name of the credential')
@@ -46,20 +46,18 @@ def fill_parser(parser: argparse.ArgumentParser):
46
46
 
47
47
  def __main(args, **kwargs):
48
48
  if args['sub_command'] == 'list':
49
+ table = PrettyTable(field_names=["Name", "URL", "API User", "Originating File"])
50
+ # all should be left justified
51
+ table.align = "l"
52
+
49
53
  credentials = get_all_credentials()
50
54
  # sort credentials by name
51
55
  credentials.sort(key=lambda x: x.name)
52
56
 
53
- # print credentials in a nice table
54
- table_template = " {:<19} {:<40} {:<22} {:<25}"
55
- print(table_template.format("Name", "URL", "API User", "Originating File"))
56
-
57
57
  for credential in credentials:
58
- print(table_template.format(credential.name,
59
- credential.fqdn + ':' + str(credential.port),
60
- credential.api_user,
61
- credential.originating_file)
62
- )
58
+ table.add_row([credential.name, credential.fqdn, credential.api_user, credential.originating_file])
59
+
60
+ print(table)
63
61
 
64
62
  elif args['sub_command'] == 'create':
65
63
 
@@ -136,10 +134,11 @@ def __main(args, **kwargs):
136
134
  selected_ssh_key.get_fingerprint().hex(),
137
135
  selected_ssh_key.comment))
138
136
  print(" * encrypting API key with selected key (you may be prompted by your SSH agent for confirmation or PIN code) ...", flush=True, end="")
139
- encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key=selected_ssh_key, api_key=api_key)
137
+ # encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_fernet(ssh_key=selected_ssh_key, api_key=api_key)
138
+ encrypted_api_key = encrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(ssh_key=selected_ssh_key, api_key=api_key)
140
139
  print("OK!")
141
140
  print(" * trying to decrypt the encrypted API key...", flush=True, end="")
142
- decrypted_api_key = decrypt_api_key_with_paramiko_ssh_key_fernet(encrypted_api_key_payload=encrypted_api_key)
141
+ decrypted_api_key = decrypt_api_key_with_paramiko_ssh_key_chacha20poly1305(encrypted_api_key_payload=encrypted_api_key)
143
142
  if decrypted_api_key != api_key:
144
143
  raise pylo.PyloEx("Decrypted API key does not match original API key")
145
144
  print("OK!")
@@ -37,14 +37,33 @@ def find_connector_or_die(obj) -> 'pylo.APIConnector':
37
37
  :param obj:
38
38
  :return:
39
39
  """
40
- connector = obj.__dict__.get('connector') # type: pylo.APIConnector
41
- if connector is None:
42
- owner = obj.__dict__.get('owner')
43
- if owner is None:
44
- raise Exception("Could not find a Connector object")
45
- return find_connector_or_die(owner)
46
-
47
- return connector
40
+
41
+ connector = None
42
+
43
+ # check if object has a __dict__ attribute
44
+ if not hasattr(obj, '__dict__'):
45
+ # check if it's in __slots__
46
+ if hasattr(obj, '__slots__'):
47
+ if 'connector' in obj.__slots__:
48
+ connector = obj.__getattribute__('connector')
49
+ if connector is not None:
50
+ return connector
51
+ raise Exception("Could not find a Connector object")
52
+ if 'owner' in obj.__slots__:
53
+ owner = obj.__getattribute__('owner')
54
+ if owner is None:
55
+ raise Exception("Could not find a Connector object")
56
+ return find_connector_or_die(owner)
57
+ raise Exception("Could not find a Connector object")
58
+ else:
59
+ connector = obj.__dict__.get('connector') # type: pylo.APIConnector
60
+ if connector is None:
61
+ owner = obj.__dict__.get('owner')
62
+ if owner is None:
63
+ raise Exception("Could not find a Connector object")
64
+ return find_connector_or_die(owner)
65
+
66
+ return connector
48
67
 
49
68
 
50
69
  class IDTranslationTable:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: illumio_pylo
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: A set of tools and library for working with Illumio PCE
5
5
  Home-page: https://github.com/cpainchaud/pylo
6
6
  Author: Christophe Painchaud
File without changes
File without changes
File without changes
File without changes
File without changes