Skip to content

Commit

Permalink
Light action support (#642)
Browse files Browse the repository at this point in the history
* Added light action config to helm and event_handler

* refactored authentication and enabled light action via relay

* fixing helm format

* fixing action name

* added cluster status

also added verification that both parts of the signature are present

* require private key verification if atleast one key is sent

* slight refactoring, added logs

* changed logs, removed redundant code

* added removed comment

* adding other needed light actions

* return error even if signature is signed correctly

functionally only impacts edge case where signature is correct but the request shouldnt be signed

* removing sign_action param

* updated light action helm + supported actions

* remove unneeded log changes

* remove additional signature match check

* changing default java toolkit

* Removing debugger until newer version
  • Loading branch information
Avi-Robusta authored Jan 3, 2023
1 parent d069d29 commit de709a0
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 17 deletions.
4 changes: 4 additions & 0 deletions helm/robusta/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ global_config:
{{- if .Values.globalConfig }}
{{ toYaml .Values.globalConfig | indent 2 }}
{{- end }}

light_actions:
{{ toYaml .Values.lightActions | indent 2 }}

active_playbooks:
{{- if .Values.playbooks }}
{{- fail "The `playbooks` value is deprecated. Rename `playbooks` to `customPlaybooks` and remove builtin playbooks which are now defined separately" -}}
Expand Down
34 changes: 34 additions & 0 deletions helm/robusta/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,40 @@ globalConfig:
account_id: ""
signing_key: ""

# safe actions to enable authenticated users to run
lightActions:
- related_pods
- prometheus_enricher
- add_silence
- delete_pod
- delete_silence
- get_silences
- logs_enricher
- pod_events_enricher
- deployment_events_enricher
- job_events_enricher
- job_pod_enricher
- get_resource_yaml
- incluster_ping
- node_cpu_enricher
- node_disk_analyzer
- node_running_pods_enricher
- node_allocatable_resources_enricher
- node_status_enricher
- node_graph_enricher
- oomkilled_container_graph_enricher
- pod_oom_killer_enricher
- oom_killer_enricher
- volume_analysis
- python_profiler
- pod_ps
- python_memory
- debugger_stack_trace
- python_process_inspector
- prometheus_alert
- create_pvc_snapshot
- http_stress_test

# install prometheus, alert-manager, and grafana along with Robusta?
enablePrometheusStack: false
enableServiceMonitors: false
Expand Down
3 changes: 2 additions & 1 deletion src/robusta/core/model/cluster_status.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from pydantic.main import BaseModel, Optional
from pydantic.main import BaseModel, Optional, List


class ClusterStatus(BaseModel):
account_id: str
cluster_id: str
version: str
last_alert_at: Optional[str] # ts
light_actions: List[str]
1 change: 1 addition & 0 deletions src/robusta/core/model/runner_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class RunnerConfig(BaseModel):
]
]
]
light_actions: Optional[List[str]]
global_config: Optional[dict] = {}
active_playbooks: Optional[List[PlaybookDefinition]] = []

Expand Down
7 changes: 7 additions & 0 deletions src/robusta/core/playbooks/playbooks_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ def get_global_config(
"""Return runner global config"""
pass

@abstractmethod
def get_light_actions(
self,
) -> List[str]:
"""Returns configured light actions"""
pass

@abstractmethod
def get_telemetry(
self,
Expand Down
3 changes: 3 additions & 0 deletions src/robusta/core/playbooks/playbooks_event_handler_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,8 @@ def __handle_findings(self, execution_event: ExecutionBaseEvent):
def get_global_config(self) -> dict:
return self.registry.get_playbooks().get_global_config()

def get_light_actions(self) -> List[str]:
return self.registry.get_light_actions()

def get_telemetry(self) -> Telemetry:
return self.registry.get_telemetry()
1 change: 1 addition & 0 deletions src/robusta/core/sinks/robusta/robusta_sink.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def __update_cluster_status(self):
version=self.registry.get_telemetry().runner_version,
last_alert_at=self.registry.get_telemetry().last_alert_at,
account_id=self.account_id,
light_actions=self.registry.get_light_actions(),
)

self.dal.publish_cluster_status(cluster_status)
Expand Down
2 changes: 1 addition & 1 deletion src/robusta/integrations/kubernetes/custom_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"us-central1-docker.pkg.dev/genuine-flight-317411/devel/debug-toolkit:v5.0"
)
JAVA_DEBUGGER_IMAGE = (
"us-central1-docker.pkg.dev/genuine-flight-317411/devel/java-toolkit-11:v1.1"
"us-central1-docker.pkg.dev/genuine-flight-317411/devel/java-toolkit-11:jattach"
)

# TODO: import these from the python-tools project
Expand Down
44 changes: 30 additions & 14 deletions src/robusta/integrations/receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,25 +226,41 @@ def __validate_request(self, action_request: ExternalActionRequest, validate_tim
error_msg="Illegal timestamp")

signing_key = self.event_handler.get_global_config().get("signing_key")
body = action_request.body
if not signing_key:
logging.error(f"Signing key not available. Cannot verify request {body}")
logging.error(f"Signing key not available. Cannot verify request {action_request.body}")
return ValidationResponse(http_code=500,
error_code=ErrorCodes.NO_SIGNING_KEY.value,
error_msg="No signing key")

# First auth protocol option, based on signature only
signature = action_request.signature
if signature:
generated_signature = sign_action_request(body, signing_key)
if hmac.compare_digest(generated_signature, signature):
return ValidationResponse()
else:
return ValidationResponse(http_code=500,
error_code=ErrorCodes.SIGNATURE_MISMATCH.value,
error_msg="Signature mismatch")

# Second auth protocol option, based on public key
if action_request.signature:
# First auth protocol option, based on signature only
return self.validate_action_request_signature(action_request, signing_key)
elif action_request.partial_auth_a or action_request.partial_auth_b:
# Second auth protocol option, based on public key
return self.validate_with_private_key(action_request, signing_key)
else: # Light action protocol option, authenticated in relay
return self.validate_light_action(action_request)

def validate_light_action(self, action_request: ExternalActionRequest) -> ValidationResponse:
light_actions = self.event_handler.get_light_actions()
if action_request.body.action_name not in light_actions:
return ValidationResponse(http_code=500,
error_code=ErrorCodes.UNAUTHORIZED_LIGHT_ACTION.value,
error_msg="Unauthorized action requested")
return ValidationResponse()

@staticmethod
def validate_action_request_signature(action_request: ExternalActionRequest, signing_key: str) -> ValidationResponse:
generated_signature = sign_action_request(action_request.body, signing_key)
if hmac.compare_digest(generated_signature, action_request.signature):
return ValidationResponse()
else:
return ValidationResponse(http_code=500,
error_code=ErrorCodes.SIGNATURE_MISMATCH.value,
error_msg="Signature mismatch")

def validate_with_private_key(self, action_request: ExternalActionRequest, signing_key: str) -> ValidationResponse:
body = action_request.body
partial_auth_a = action_request.partial_auth_a
partial_auth_b = action_request.partial_auth_b
if not partial_auth_a or not partial_auth_b:
Expand Down
7 changes: 7 additions & 0 deletions src/robusta/model/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def get_global_config(self) -> dict:

class Registry:
_actions: ActionsRegistry = ActionsRegistry()
_light_actions: List[str] = []
_playbooks: PlaybooksRegistry = PlaybooksRegistry()
_sinks: SinksRegistry = None
_scheduler = None
Expand All @@ -184,6 +185,12 @@ class Registry:
prometheus_enabled=PROMETHEUS_ENABLED,
)

def set_light_actions(self, light_actions: List[str]):
self._light_actions = light_actions

def get_light_actions(self) -> List[str]:
return self._light_actions

def set_actions(self, actions: ActionsRegistry):
self._actions = actions

Expand Down
2 changes: 1 addition & 1 deletion src/robusta/runner/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def __reload_playbook_packages(self, change_name):
GitRepoManager.clear_git_repos()

self.__reload_scheduler(playbooks_registry)

self.registry.set_light_actions(runner_config.light_actions)
self.registry.set_actions(action_registry)
self.registry.set_playbooks(playbooks_registry)
self.registry.set_sinks(sinks_registry)
Expand Down
1 change: 1 addition & 0 deletions src/robusta/utils/error_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ErrorCodes(Enum):
NOT_EXTERNAL_ACTION = 4604
EVENT_PARAMS_INSTANTIATION_FAILED = 4605
EVENT_INSTANTIATION_FAILED = 4606
UNAUTHORIZED_LIGHT_ACTION = 4607

ACTION_UNEXPECTED_ERROR = 4700
RESOURCE_NOT_SUPPORTED = 4701
Expand Down

0 comments on commit de709a0

Please sign in to comment.