-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1266 from nusantara-self/feature/aws-lambda-respo…
…nder Add AWS Invoke Lambda responder
- Loading branch information
Showing
6 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
{ | ||
"name": "AWSLambda_InvokeFunction", | ||
"version": "1.0", | ||
"author": "nusantara-self", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"description": "Invokes the configured AWS Lambda function", | ||
"dataTypeList": ["thehive:case", "thehive:alert", "thehive:case_artifact", "thehive:task"], | ||
"command": "AWSLambda/AWSInvokeLambda.py", | ||
"baseConfig": "AWSLambda", | ||
"configurationItems": [ | ||
{ | ||
"name": "aws_access_key_id", | ||
"description": "AWS Access Key ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "aws_secret_access_key", | ||
"description": "AWS Secret Access Key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "aws_region", | ||
"description": "AWS Region", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "us-east-1" | ||
}, | ||
{ | ||
"name": "lambda_function_name", | ||
"description": "Name of the AWS Lambda function to invoke", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "invocation_type", | ||
"description": "Invocation type for the lambda function. Default is 'RequestResponse'. Change to 'Event' for asynchronous invocation.", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "RequestResponse" | ||
}, | ||
{ | ||
"name": "add_tag_to_case", | ||
"description": "Add a tag to case mentioning the AWS Lambda function that was invoked", | ||
"type": "boolean", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": true | ||
} | ||
], | ||
"registration_required": true, | ||
"subscription_required": true, | ||
"free_subscription": false, | ||
"service_homepage": "https://aws.amazon.com/lambda/", | ||
"service_logo": { | ||
"path": "assets/awslambda.png", | ||
"caption": "AWS Lambda logo" | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#!/usr/bin/env python3 | ||
from cortexutils.responder import Responder | ||
import boto3 | ||
import json | ||
from botocore.exceptions import BotoCoreError, ClientError | ||
|
||
class AWSLambda(Responder): | ||
def __init__(self): | ||
Responder.__init__(self) | ||
self.aws_access_key_id = self.get_param('config.aws_access_key_id', None, 'AWS Access Key ID missing') | ||
self.aws_secret_access_key = self.get_param('config.aws_secret_access_key', None, 'AWS Secret Access Key missing') | ||
self.aws_region = self.get_param('config.aws_region', None, 'AWS Region missing') | ||
self.lambda_function_name = self.get_param('config.lambda_function_name', None, 'Lambda Function Name missing') | ||
self.invocation_type = self.get_param('config.invocation_type', None, 'RequestResponse') | ||
self.add_tag_to_case = self.get_param('config.add_tag_to_case', True) | ||
|
||
def run(self): | ||
Responder.run(self) | ||
|
||
payload_data = self.get_param("data", None, "No data was passed from TheHive") | ||
|
||
# Initialize a session using boto3 | ||
session = boto3.Session( | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
region_name=self.aws_region | ||
) | ||
|
||
# Initialize the Lambda client | ||
lambda_client = session.client('lambda') | ||
|
||
try: | ||
# Invoke the Lambda function | ||
response = lambda_client.invoke( | ||
FunctionName=self.lambda_function_name, | ||
InvocationType=self.invocation_type, | ||
Payload=json.dumps(payload_data) | ||
) | ||
|
||
|
||
if self.invocation_type == 'Event': | ||
# In case of async invocations (Event) , there is no response payload | ||
message = f'Lambda function {self.lambda_function_name} invoked asynchronously (Event mode). Invocation acknowledged, no response payload.' | ||
self.report({"message": message}) | ||
return | ||
|
||
if 'FunctionError' in response: | ||
self._handle_error( | ||
message="Error from Lambda function", | ||
error_type='LambdaFunctionError', | ||
details=response.get('FunctionError', 'Unknown function error'), | ||
additional_info=None | ||
) | ||
return | ||
|
||
# Extract and decode response payload | ||
response_payload = json.loads(response['Payload'].read()) | ||
message=f'Lambda function {self.lambda_function_name} invoked successfully: {response_payload}' | ||
self.report({"message": message}) | ||
|
||
except BotoCoreError as e: | ||
self._handle_error( | ||
message="BotoCoreError occurred", | ||
error_type='BotoCoreError', | ||
details=str(e) | ||
) | ||
|
||
except ClientError as e: | ||
error_message = e.response['Error']['Message'] | ||
self._handle_error( | ||
message="ClientError occurred", | ||
error_type='ClientError', | ||
details=error_message, | ||
additional_info=e.response | ||
) | ||
|
||
except Exception as e: | ||
self._handle_error( | ||
message="An unexpected exception occurred", | ||
error_type='GeneralException', | ||
details=str(e) | ||
) | ||
|
||
def _handle_error(self, message, error_type, details, additional_info=None): | ||
"""Helper function to handle errors and return a string message.""" | ||
error_message = f"[{error_type}] {message}: {details} \n\nAdditional info: {additional_info}" | ||
self.error(error_message) | ||
|
||
def operations(self, raw): | ||
operations = [] | ||
if self.add_tag_to_case: | ||
tag = f"AWSLambdaInvoked-{self.lambda_function_name}" | ||
operations.append(self.build_operation('AddTagToCase', tag=tag)) | ||
return operations | ||
|
||
if __name__ == '__main__': | ||
AWSLambda().run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
FROM python:3 | ||
|
||
WORKDIR /worker | ||
COPY . AWSInvokeLambda | ||
RUN test ! -e AWSInvokeLambda/requirements.txt || pip install --no-cache-dir -r AWSInvokeLambda/requirements.txt | ||
ENTRYPOINT AWSInvokeLambda/AWSInvokeLambda.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
### AWS Lambda Responder | ||
|
||
This responder triggers an AWS Lambda function using the provided credentials and configuration, directly from TheHive. By default, it can be triggered from an alert, case, observable, task and sends the data of the object as input to the AWS Lambda Function for its execution. | ||
Make sure to manage these different objects appropriately if needed. | ||
|
||
#### Setup example | ||
- Log in to your [AWS Management Console](https://aws.amazon.com/console/) go to **IAM** | ||
- Create a **new IAM user** (e.g. CortexAWSlambda-invoke-responder) with AWS Credentials type : Access key - Programmatic | ||
- Choose **attach policies directly** and attach a policy you created with least privilege, for example: | ||
``` | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Action": [ | ||
"lambda:InvokeFunction" | ||
], | ||
"Resource": [ | ||
"arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:function:<LAMBDA_FUNCTION_NAME>" | ||
] | ||
} | ||
] | ||
} | ||
``` | ||
- Go to your newly created user, to **Security tab** and create **access key** for an **Application running outside AWS** | ||
- Configure properly the responder with the right credentials & aws region | ||
|
||
#### Successful Execution | ||
|
||
When an execution is successful in `RequestResponse` mode, the responder will be marked as "Success" with a report message in the following format: | ||
|
||
``` | ||
{ "message": "Lambda function '<name-of-lambda-function>' invoked successfully.", "response": "<response from lambda function>" } | ||
``` | ||
|
||
#### Failed Execution | ||
|
||
When an execution fails in `RequestResponse` mode, the responder will be marked as "Failure" with a report message in the following format: | ||
``` | ||
"[{error_type}] {message}: {details}\n\nAdditional info: {additional_info}" | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cortexutils | ||
boto3 |