assemblyline-core 4.5.1.dev426__tar.gz → 4.5.1.dev434__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 (88) hide show
  1. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/PKG-INFO +1 -1
  2. assemblyline_core-4.5.1.dev434/assemblyline_core/VERSION +1 -0
  3. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/loader/run.py +1 -1
  4. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/controllers/interface.py +1 -10
  5. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/controllers/kubernetes_ctl.py +28 -65
  6. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/scaler_server.py +6 -36
  7. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/updater/run_updater.py +15 -3
  8. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core.egg-info/PKG-INFO +1 -1
  9. assemblyline_core-4.5.1.dev426/assemblyline_core/VERSION +0 -1
  10. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/LICENCE.md +0 -0
  11. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/README.md +0 -0
  12. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/__init__.py +0 -0
  13. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/alerter/__init__.py +0 -0
  14. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/alerter/processing.py +0 -0
  15. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/alerter/run_alerter.py +0 -0
  16. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/archiver/__init__.py +0 -0
  17. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/archiver/run_archiver.py +0 -0
  18. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/badlist_client.py +0 -0
  19. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/__init__.py +0 -0
  20. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/__main__.py +0 -0
  21. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/client.py +0 -0
  22. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/dispatcher.py +0 -0
  23. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/schedules.py +0 -0
  24. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/dispatching/timeout.py +0 -0
  25. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/expiry/__init__.py +0 -0
  26. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/expiry/run_expiry.py +0 -0
  27. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/ingester/__init__.py +0 -0
  28. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/ingester/__main__.py +0 -0
  29. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/ingester/constants.py +0 -0
  30. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/ingester/ingester.py +0 -0
  31. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/__init__.py +0 -0
  32. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/es_metrics.py +0 -0
  33. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/heartbeat_formatter.py +0 -0
  34. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/helper.py +0 -0
  35. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/metrics_server.py +0 -0
  36. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/run_heartbeat_manager.py +0 -0
  37. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/run_metrics_aggregator.py +0 -0
  38. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/metrics/run_statistics_aggregator.py +0 -0
  39. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/plumber/__init__.py +0 -0
  40. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/plumber/run_plumber.py +0 -0
  41. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/__init__.py +0 -0
  42. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/client.py +0 -0
  43. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/creator/__init__.py +0 -0
  44. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/creator/run.py +0 -0
  45. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/creator/run_worker.py +0 -0
  46. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/loader/__init__.py +0 -0
  47. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/loader/run_worker.py +0 -0
  48. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/replay/replay.py +0 -0
  49. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/safelist_client.py +0 -0
  50. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/__init__.py +0 -0
  51. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/collection.py +0 -0
  52. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/controllers/__init__.py +0 -0
  53. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/controllers/docker_ctl.py +0 -0
  54. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/scaler/run_scaler.py +0 -0
  55. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/server_base.py +0 -0
  56. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/signature_client.py +0 -0
  57. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/submission_client.py +0 -0
  58. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/tasking_client.py +0 -0
  59. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/updater/__init__.py +0 -0
  60. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/updater/helper.py +1 -1
  61. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/__init__.py +0 -0
  62. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/crawler.py +0 -0
  63. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/department_map.py +0 -0
  64. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/safelist.py +0 -0
  65. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/stream_map.py +0 -0
  66. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/vacuum/worker.py +0 -0
  67. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/workflow/__init__.py +0 -0
  68. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core/workflow/run_workflow.py +0 -0
  69. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core.egg-info/SOURCES.txt +0 -0
  70. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core.egg-info/dependency_links.txt +0 -0
  71. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core.egg-info/requires.txt +0 -0
  72. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/assemblyline_core.egg-info/top_level.txt +0 -0
  73. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/setup.cfg +0 -0
  74. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/setup.py +0 -0
  75. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_alerter.py +0 -0
  76. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_badlist_client.py +0 -0
  77. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_dispatcher.py +0 -0
  78. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_expiry.py +0 -0
  79. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_plumber.py +0 -0
  80. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_replay.py +0 -0
  81. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_safelist_client.py +0 -0
  82. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_scaler.py +0 -0
  83. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_scheduler.py +0 -0
  84. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_signature_client.py +0 -0
  85. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_simulation.py +0 -0
  86. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_vacuum.py +0 -0
  87. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_worker_ingest.py +0 -0
  88. {assemblyline_core-4.5.1.dev426 → assemblyline_core-4.5.1.dev434}/test/test_worker_submit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: assemblyline-core
3
- Version: 4.5.1.dev426
3
+ Version: 4.5.1.dev434
4
4
  Summary: Assemblyline 4 - Core components
5
5
  Home-page: https://github.com/CybercentreCanada/assemblyline-core/
6
6
  Author: CCCS Assemblyline development team
@@ -0,0 +1 @@
1
+ 4.5.1.dev434
@@ -86,8 +86,8 @@ class ReplayLoader(ReplayBase):
86
86
  self.maintain_threads(threads)
87
87
 
88
88
  def stop(self):
89
- super().stop()
90
89
  self.cache.close()
90
+ return super().stop()
91
91
 
92
92
 
93
93
  if __name__ == '__main__':
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING, Optional
3
3
 
4
-
5
4
  if TYPE_CHECKING:
6
5
  from assemblyline_core.scaler.scaler_server import ServiceProfile
7
6
 
@@ -12,14 +11,6 @@ class ServiceControlError(RuntimeError):
12
11
  self.service_name = service_name
13
12
 
14
13
 
15
- class ContainerEvent:
16
- def __init__(self, object_name: str, message: str, service_name=None, updater=None) -> None:
17
- self.object_name = object_name
18
- self.message = message
19
- self.service_name = service_name
20
- self.updater = updater
21
-
22
-
23
14
  class ControllerInterface:
24
15
  def add_profile(self, profile, scale=0):
25
16
  """Tell the controller about a service profile it needs to manage."""
@@ -59,7 +50,7 @@ class ControllerInterface:
59
50
  def get_running_container_names(self):
60
51
  raise NotImplementedError()
61
52
 
62
- def new_events(self) -> list[ContainerEvent]:
53
+ def new_events(self):
63
54
  return []
64
55
 
65
56
  def stateful_container_key(self, service_name: str, container_name: str, spec, change_key: str) -> Optional[str]:
@@ -26,11 +26,12 @@ from kubernetes.client import V1Deployment, V1DeploymentSpec, V1PodTemplateSpec,
26
26
  V1PersistentVolumeClaimSpec, V1NetworkPolicy, V1NetworkPolicySpec, V1NetworkPolicyEgressRule, V1NetworkPolicyPeer, \
27
27
  V1NetworkPolicyIngressRule, V1Secret, V1SecretVolumeSource, V1LocalObjectReference, V1Service, \
28
28
  V1ServiceSpec, V1ServicePort, V1PodSecurityContext, V1Probe, V1ExecAction, V1SecurityContext, \
29
- V1Affinity, V1NodeAffinity, V1NodeSelector, V1NodeSelectorTerm, V1NodeSelectorRequirement, CoreV1Event, V1Toleration
29
+ V1Affinity, V1NodeAffinity, V1NodeSelector, V1NodeSelectorTerm, V1NodeSelectorRequirement, V1Toleration, \
30
+ V1Capabilities, V1SeccompProfile
30
31
  from kubernetes.client.rest import ApiException
31
32
  from assemblyline.odm.models.service import DependencyConfig, DockerConfig, PersistentVolume
32
33
 
33
- from assemblyline_core.scaler.controllers.interface import ContainerEvent, ControllerInterface
34
+ from assemblyline_core.scaler.controllers.interface import ControllerInterface
34
35
 
35
36
  # RESERVE_MEMORY_PER_NODE = os.environ.get('RESERVE_MEMORY_PER_NODE')
36
37
 
@@ -45,6 +46,14 @@ SERVICE_LIVENESS_TIMEOUT = int(os.environ.get('SERVICE_LIVENESS_TIMEOUT', 60))
45
46
  UNPRIVILEGED_SERVICE_ACCOUNT_NAME = os.environ.get('UNPRIVILEGED_SERVICE_ACCOUNT_NAME', None)
46
47
  PRIVILEGED_SERVICE_ACCOUNT_NAME = os.environ.get('PRIVILEGED_SERVICE_ACCOUNT_NAME', None)
47
48
  CERTIFICATE_VALIDITY_PERIOD = int(os.environ.get('CERTIFICATE_VALIDITY_PERIOD', '36500'))
49
+ RESTRICTED_POD_SECURITY_CONTEXT = V1SecurityContext(
50
+ run_as_user=1000,
51
+ run_as_group=1000,
52
+ capabilities=V1Capabilities(drop=["ALL"]),
53
+ run_as_non_root=True,
54
+ allow_privilege_escalation=False,
55
+ seccomp_profile=V1SeccompProfile(type="RuntimeDefault")
56
+ )
48
57
 
49
58
  AL_ROOT_CA = os.environ.get('AL_ROOT_CA', '/etc/assemblyline/ssl/al_root-ca.crt')
50
59
  AL_ROOT_CA_PK = os.environ.get('AL_ROOT_CA_PK', '/etc/assemblyline/ssl/al_root-ca.key')
@@ -241,7 +250,8 @@ def parse_cpu(string: str) -> float:
241
250
  class KubernetesController(ControllerInterface):
242
251
  def __init__(self, logger, namespace: str, prefix: str, priority: str, dependency_priority: str,
243
252
  cpu_reservation: float, linux_node_selector: Selector, labels=None, log_level="INFO", core_env={},
244
- default_service_account=None, cluster_pod_list=True, default_service_tolerations = [],
253
+ default_service_account=None, cluster_pod_list=True, enable_pod_security=False,
254
+ default_service_tolerations=[],
245
255
  priv_labels=None):
246
256
  # Try loading a kubernetes connection from either the fact that we are running
247
257
  # inside of a cluster, or have a config file that tells us how
@@ -264,7 +274,7 @@ class KubernetesController(ControllerInterface):
264
274
  config.load_kube_config(client_configuration=cfg)
265
275
 
266
276
  self.running: bool = True
267
- self.prefix: str = prefix.lower().replace('_', '-')
277
+ self.prefix: str = prefix.lower()
268
278
  self.priority: str = priority
269
279
  self.dependency_priority: str = dependency_priority
270
280
  self.cpu_reservation: float = max(0.0, min(cpu_reservation, 1.0))
@@ -287,6 +297,7 @@ class KubernetesController(ControllerInterface):
287
297
  self._service_limited_env: dict[str, dict[str, str]] = defaultdict(dict)
288
298
  self.default_service_account: Optional[str] = default_service_account
289
299
  self.cluster_pod_list = cluster_pod_list
300
+ self.security_policy = RESTRICTED_POD_SECURITY_CONTEXT if enable_pod_security else None
290
301
  self.default_service_tolerations = [V1Toleration(**toleration.as_primitives()) for toleration in default_service_tolerations]
291
302
 
292
303
  # A record of previously reported events so that we don't report the same message repeatedly, fill it with
@@ -396,7 +407,8 @@ class KubernetesController(ControllerInterface):
396
407
  """Tell the controller about a service profile it needs to manage."""
397
408
  self._create_deployment(profile.name, self._deployment_name(profile.name),
398
409
  profile.container_config, profile.shutdown_seconds, scale,
399
- change_key=profile.config_blob, core_mounts=profile.privileged)
410
+ change_key=profile.config_blob, core_mounts=profile.privileged,
411
+ security_context=self.security_policy),
400
412
  self._external_profiles[profile.name] = profile
401
413
 
402
414
  def _loop_forever(self, function):
@@ -679,7 +691,7 @@ class KubernetesController(ControllerInterface):
679
691
  return self._quota_mem_limit - self._get_pod_used_namespace_ram(), self._quota_mem_limit
680
692
  return self._node_pool_max_ram - self._get_pod_used_ram(), self._node_pool_max_ram
681
693
 
682
- def _create_containers(self, service_name: str, deployment_name: str, container_config, mounts,
694
+ def _create_containers(self, service_name: str, deployment_name: str, container_config, mounts, security_context,
683
695
  core_container=False):
684
696
  cores = container_config.cpu_cores
685
697
  memory = container_config.ram_mb
@@ -719,6 +731,7 @@ class KubernetesController(ControllerInterface):
719
731
  env=environment_variables,
720
732
  image_pull_policy=image_pull_policy,
721
733
  volume_mounts=mounts,
734
+ security_context=security_context,
722
735
  resources=V1ResourceRequirements(
723
736
  limits={'cpu': cores, 'memory': f'{memory}Mi'},
724
737
  requests={'cpu': cores*self.cpu_reservation, 'memory': f'{min_memory}Mi'},
@@ -731,7 +744,8 @@ class KubernetesController(ControllerInterface):
731
744
  shutdown_seconds: int, scale: int, labels: dict[str, str] = None,
732
745
  volumes: list[V1Volume] = None, mounts: list[V1VolumeMount] = None,
733
746
  core_mounts: bool = False, change_key: str = '', high_priority: bool = False,
734
- deployment_strategy: V1DeploymentStrategy = V1DeploymentStrategy()):
747
+ deployment_strategy: V1DeploymentStrategy = V1DeploymentStrategy(),
748
+ security_context: V1SecurityContext = None):
735
749
  # Build a cache key to check for changes, just trying to only patch what changed
736
750
  # will still potentially result in a lot of restarts due to different kubernetes
737
751
  # systems returning differently formatted data
@@ -742,7 +756,8 @@ class KubernetesController(ControllerInterface):
742
756
  key_labels += sorted(deployment_labels.items())
743
757
  change_key = str(f"n={deployment_name}{change_key}dc={docker_config}ss={shutdown_seconds}"
744
758
  f"l={key_labels}v={volumes}m={mounts}cm={core_mounts}senv={svc_env}"
745
- f"nodes={field_selector or ''}{label_selector or ''}")
759
+ f"nodes={field_selector or ''}{label_selector or ''}"
760
+ f"security_context={security_context or ''}")
746
761
  self.logger.debug(f"{deployment_name} actual change_key: {change_key}")
747
762
  change_key = str(hash(change_key))
748
763
 
@@ -847,7 +862,7 @@ class KubernetesController(ControllerInterface):
847
862
  init_containers=init_containers,
848
863
  volumes=all_volumes,
849
864
  containers=self._create_containers(service_name, deployment_name, docker_config,
850
- all_mounts, core_container=core_mounts),
865
+ all_mounts, security_context, core_container=core_mounts),
851
866
  priority_class_name=self.dependency_priority if high_priority else self.priority,
852
867
  termination_grace_period_seconds=shutdown_seconds,
853
868
  security_context=V1PodSecurityContext(fs_group=1000),
@@ -952,58 +967,14 @@ class KubernetesController(ControllerInterface):
952
967
  def restart(self, service):
953
968
  self._create_deployment(service.name, self._deployment_name(service.name), service.container_config,
954
969
  service.shutdown_seconds, self.get_target(service.name), core_mounts=service.privileged,
955
- change_key=service.config_blob)
970
+ change_key=service.config_blob, security_context=self.security_policy)
956
971
 
957
972
  def get_running_container_names(self):
958
973
  pods = self.api.list_pod_for_all_namespaces(field_selector='status.phase==Running',
959
974
  _request_timeout=API_TIMEOUT)
960
975
  return [pod.metadata.name for pod in pods.items]
961
976
 
962
- @staticmethod
963
- def dropped_message(event: CoreV1Event) -> bool:
964
- """These aren't messages anyone looking at the """
965
-
966
- # These are common ephemeral states
967
- if event.action in ["Scheduling"]:
968
- return True
969
- if event.reason in ["BackOff", "NodeNotReady", "FailedGetResourceMetric", "FailedComputeMetricsReplicas"]:
970
- return True
971
-
972
- # There is a more detailed event that starts with "Failed to pull image"
973
- if event.message == "Error: ErrImagePull":
974
- return True
975
- if event.message == "Error: ImagePullBackOff":
976
- return True
977
-
978
- # Probes failing on exiting containers is normal
979
- if "Readiness probe errored" in event.message and "container is in CONTAINER_EXITED state" in event.message:
980
- return True
981
- if "Liveness probe errored" in event.message and "container is in CONTAINER_EXITED state" in event.message:
982
- return True
983
-
984
- return False
985
-
986
- def parse_container_name(self, name) -> tuple[Optional[str], Optional[bool]]:
987
- if not name.startswith(self.prefix):
988
- return (None, None)
989
- name = name.removeprefix(self.prefix)
990
-
991
- name, _, container_id = name.rpartition('-')
992
- if not container_id:
993
- return (None, None)
994
-
995
- name, _, deployment_id = name.rpartition('-')
996
- if not deployment_id:
997
- return (None, None)
998
-
999
- updater = False
1000
- if name.endswith('-updates'):
1001
- name = name.removesuffix('-updates')
1002
- updater = True
1003
-
1004
- return (name, updater)
1005
-
1006
- def new_events(self) -> list[ContainerEvent]:
977
+ def new_events(self):
1007
978
  response = self.api.list_namespaced_event(namespace=self.namespace, pretty='false',
1008
979
  field_selector='type=Warning', watch=False,
1009
980
  _request_timeout=API_TIMEOUT)
@@ -1013,15 +984,7 @@ class KubernetesController(ControllerInterface):
1013
984
  for event in response.items:
1014
985
  if self.events_window.get(event.metadata.uid, 0) != event.count:
1015
986
  self.events_window[event.metadata.uid] = event.count
1016
-
1017
- if KubernetesController.dropped_message(event):
1018
- continue
1019
-
1020
- object_name = event.involved_object.name
1021
- service_name, is_updater = None, None
1022
- if event.involved_object.kind == 'Pod':
1023
- service_name, is_updater = self.parse_container_name(object_name)
1024
- new.append(ContainerEvent(object_name, event.message, service_name, is_updater))
987
+ new.append(event.involved_object.name + ': ' + event.message)
1025
988
 
1026
989
  # Flush out events that have moved outside the window
1027
990
  old = set(self.events_window.keys()) - {event.metadata.uid for event in response.items}
@@ -1187,7 +1150,7 @@ class KubernetesController(ControllerInterface):
1187
1150
  self._create_deployment(service_name, deployment_name, spec.container,
1188
1151
  30, 1, labels, volumes=volumes, mounts=mounts, high_priority=True,
1189
1152
  core_mounts=spec.run_as_core, change_key=change_key,
1190
- deployment_strategy=deployment_strategy)
1153
+ deployment_strategy=deployment_strategy, security_context=self.security_policy)
1191
1154
 
1192
1155
  # Setup a service to direct to the deployment
1193
1156
  try:
@@ -27,19 +27,17 @@ from assemblyline.remote.datatypes.hash import ExpiringHash, Hash
27
27
  from assemblyline.remote.datatypes.events import EventWatcher, EventSender
28
28
  from assemblyline.odm.models.service import Service, DockerConfig, EnvironmentVariable
29
29
  from assemblyline.odm.models.config import Mount
30
- from assemblyline.odm.models.error import Error
31
30
  from assemblyline.odm.messages.scaler_heartbeat import Metrics
32
31
  from assemblyline.odm.messages.scaler_status_heartbeat import Status
33
32
  from assemblyline.odm.messages.changes import ServiceChange, Operation
34
33
  from assemblyline.common.dict_utils import get_recursive_sorted_tuples, flatten
35
- from assemblyline.common.uid import get_id_from_data, get_random_id
34
+ from assemblyline.common.uid import get_id_from_data
36
35
  from assemblyline.common.forge import get_classification, get_service_queue, get_apm_client
37
36
  from assemblyline.common.constants import SCALER_TIMEOUT_QUEUE, SERVICE_STATE_HASH, ServiceStatus
38
37
  from assemblyline.common.version import FRAMEWORK_VERSION, SYSTEM_VERSION
39
- from assemblyline.common.isotime import now_as_iso
40
38
  from assemblyline_core.updater.helper import get_registry_config
41
39
  from assemblyline_core.scaler.controllers import KubernetesController
42
- from assemblyline_core.scaler.controllers.interface import ContainerEvent, ServiceControlError
40
+ from assemblyline_core.scaler.controllers.interface import ServiceControlError
43
41
  from assemblyline_core.server_base import ServiceStage, ThreadedCoreBase
44
42
 
45
43
  from .controllers import DockerController
@@ -54,7 +52,6 @@ SCALE_INTERVAL = 5
54
52
  METRIC_SYNC_INTERVAL = 0.5
55
53
  CONTAINER_EVENTS_LOG_INTERVAL = 2
56
54
  HEARTBEAT_INTERVAL = 5
57
- SIXTY_DAYS = 60 * 60 * 24 * 60
58
55
 
59
56
  # The maximum containers we ask to be created in a single scaling iteration
60
57
  # This is to give kubernetes a chance to update our view of resource usage before we ask for more containers
@@ -74,7 +71,6 @@ DOCKER_CONFIGURATION_VOLUME = os.getenv('DOCKER_CONFIGURATION_VOLUME', None)
74
71
 
75
72
  SERVICE_API_HOST = os.getenv('SERVICE_API_HOST', None)
76
73
  INTERNAL_ENCRYPT = bool(SERVICE_API_HOST and SERVICE_API_HOST.startswith('https'))
77
- SERVICE_PREFIX = 'alsvc-'
78
74
 
79
75
 
80
76
  @contextmanager
@@ -315,7 +311,7 @@ class ScalerServer(ThreadedCoreBase):
315
311
 
316
312
  if KUBERNETES_AL_CONFIG:
317
313
  self.log.info(f"Loading Kubernetes cluster interface on namespace: {NAMESPACE}")
318
- self.controller = KubernetesController(logger=self.log, prefix=SERVICE_PREFIX, labels=labels,
314
+ self.controller = KubernetesController(logger=self.log, prefix='alsvc_', labels=labels,
319
315
  namespace=NAMESPACE, priority='al-service-priority',
320
316
  dependency_priority='al-core-priority',
321
317
  cpu_reservation=self.config.services.cpu_reservation,
@@ -323,6 +319,7 @@ class ScalerServer(ThreadedCoreBase):
323
319
  log_level=self.config.logging.log_level,
324
320
  core_env=core_env,
325
321
  cluster_pod_list=self.config.core.scaler.cluster_pod_list,
322
+ enable_pod_security=self.config.core.scaler.enable_pod_security,
326
323
  default_service_account=self.config.services.service_account,
327
324
  default_service_tolerations=service_defaults_config.tolerations,
328
325
  priv_labels=priv_labels
@@ -983,34 +980,7 @@ class ScalerServer(ThreadedCoreBase):
983
980
 
984
981
  def log_container_events(self):
985
982
  """The service status table may have references to containers that have crashed. Try to remove them all."""
986
- pool = concurrent.futures.ThreadPoolExecutor(20)
987
983
  while self.sleep(CONTAINER_EVENTS_LOG_INTERVAL):
988
984
  with apm_span(self.apm_client, 'log_container_events'):
989
- for event in self.controller.new_events():
990
- if event.service_name:
991
- pool.submit(self._process_service_event, event)
992
- continue
993
- self.log.warning("Container Event :: " + event.object_name + ": " + event.message)
994
-
995
- def _process_service_event(self, event: ContainerEvent):
996
- """Record the container event in the service event table."""
997
- try:
998
- message = f"Event for service {'updater' if event.updater else ''} container " + \
999
- f"[{event.object_name}] for service: \n" + event.message
1000
-
1001
- error = Error({
1002
- 'expiry_ts': now_as_iso(SIXTY_DAYS),
1003
- 'response': {
1004
- 'message': message,
1005
- 'service_name': event.service_name,
1006
- 'service_version': 'UNKNOWN',
1007
- 'status': 'FAIL_NONRECOVERABLE'
1008
- },
1009
- 'sha256': '0' * 64,
1010
- 'type': 'UNKNOWN'
1011
- })
1012
- error_key = error.build_key(service_tool_version=get_random_id())
1013
- self.datastore.error.save(error_key, error)
1014
-
1015
- except Exception:
1016
- self.log.exception("Error reporting service container event")
985
+ for message in self.controller.new_events():
986
+ self.log.warning("Container Event :: " + message)
@@ -15,7 +15,8 @@ import docker
15
15
 
16
16
  from kubernetes.client import V1Job, V1ObjectMeta, V1JobSpec, V1PodTemplateSpec, V1PodSpec, V1Volume, \
17
17
  V1VolumeMount, V1EnvVar, V1Container, V1ResourceRequirements, \
18
- V1ConfigMapVolumeSource, V1Secret, V1SecretVolumeSource, V1LocalObjectReference, V1Toleration
18
+ V1ConfigMapVolumeSource, V1Secret, V1SecretVolumeSource, V1LocalObjectReference, V1Toleration, V1SecurityContext, \
19
+ V1Capabilities, V1SeccompProfile
19
20
  from kubernetes import client, config
20
21
  from kubernetes.client.rest import ApiException
21
22
 
@@ -44,6 +45,14 @@ INHERITED_VARIABLES: list[str] = ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY', 'http
44
45
  CONFIGURATION_HOST_PATH = os.getenv('CONFIGURATION_HOST_PATH', 'service_config')
45
46
  CONFIGURATION_CONFIGMAP = os.getenv('KUBERNETES_AL_CONFIG', None)
46
47
  AL_CORE_NETWORK = os.environ.get("AL_CORE_NETWORK", 'core')
48
+ RESTRICTED_POD_SECUTITY_CONTEXT = V1SecurityContext(
49
+ run_as_user=1000,
50
+ run_as_group=1000,
51
+ capabilities=V1Capabilities(drop=["ALL"]),
52
+ run_as_non_root=True,
53
+ allow_privilege_escalation=False,
54
+ seccomp_profile=V1SeccompProfile(type="RuntimeDefault")
55
+ )
47
56
 
48
57
  SERVICE_API_HOST = os.getenv('SERVICE_API_HOST')
49
58
  RELEASE_NAME = os.getenv('RELEASE_NAME')
@@ -148,7 +157,7 @@ class DockerUpdateInterface:
148
157
 
149
158
  class KubernetesUpdateInterface:
150
159
  def __init__(self, logger, prefix, namespace, priority_class, extra_labels, linux_node_selector: Selector,
151
- log_level="INFO", default_service_account=None, default_service_tolerations=[]):
160
+ log_level="INFO", default_service_account=None, default_service_tolerations=[], enable_pod_security=False):
152
161
  # Try loading a kubernetes connection from either the fact that we are running
153
162
  # inside of a cluster, or we have a configuration in the normal location
154
163
  try:
@@ -182,6 +191,7 @@ class KubernetesUpdateInterface:
182
191
  self.secret_env = []
183
192
  self.linux_node_selector = linux_node_selector
184
193
  self.default_service_tolerations = [V1Toleration(**toleration.as_primitives()) for toleration in default_service_tolerations]
194
+ self.security_policy = RESTRICTED_POD_SECUTITY_CONTEXT if enable_pod_security else None
185
195
 
186
196
 
187
197
  # Get the deployment of this process. Use that information to fill out the secret info
@@ -324,6 +334,7 @@ class KubernetesUpdateInterface:
324
334
  env=environment_variables,
325
335
  image_pull_policy='Always',
326
336
  volume_mounts=volume_mounts,
337
+ security_context=self.security_policy,
327
338
  resources=V1ResourceRequirements(
328
339
  limits={'cpu': cores, 'memory': f'{memory}Mi'},
329
340
  requests={'cpu': cores / 4, 'memory': f'{memory_min}Mi'},
@@ -478,7 +489,8 @@ class ServiceUpdater(ThreadedCoreBase):
478
489
  log_level=self.config.logging.log_level,
479
490
  default_service_account=self.config.services.service_account,
480
491
  linux_node_selector=self.config.core.scaler.linux_node_selector,
481
- default_service_tolerations=self.config.core.scaler.service_defaults.tolerations)
492
+ default_service_tolerations=self.config.core.scaler.service_defaults.tolerations,
493
+ enable_pod_security=self.config.core.scaler.enable_pod_security)
482
494
  # Add all additional mounts to privileged services
483
495
  self.mounts = self.config.core.scaler.service_defaults.mounts
484
496
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: assemblyline-core
3
- Version: 4.5.1.dev426
3
+ Version: 4.5.1.dev434
4
4
  Summary: Assemblyline 4 - Core components
5
5
  Home-page: https://github.com/CybercentreCanada/assemblyline-core/
6
6
  Author: CCCS Assemblyline development team
@@ -1 +0,0 @@
1
- 4.5.1.dev426
@@ -61,6 +61,7 @@ class DockerRegistry(ContainerRegistry):
61
61
  headers["Authorization"] = f"Bearer {token}"
62
62
 
63
63
  resp = self._perform_request(url, headers, verify, proxies)
64
+
64
65
  # Test for valid response
65
66
  if resp and resp.ok:
66
67
  # Test for positive list of tags
@@ -265,7 +266,6 @@ def get_latest_tag_for_service(service_config: ServiceConfig, system_config: Sys
265
266
  tags = registry._get_proprietary_registry_tags(server, image_name, auth,
266
267
  not system_config.services.allow_insecure_registry,
267
268
  proxies, token_server)
268
-
269
269
  # Pre-filter tags to only consider 'compatible' tags relative to the running system
270
270
  tags = [tag for tag in tags
271
271
  if re.match(f"({FRAMEWORK_VERSION})\\.({SYSTEM_VERSION})\\.\\d+\\.({update_channel})\\d+", tag)]