Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions post-scan-actions/aws-python-publish-malware-to-sns/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.PHONY: zip

MKDIR := mkdir
ZIP := zip

zip:
$(MKDIR) -p zip
$(ZIP) zip/send-malware-to-sns.zip handler.py

create-target-sns-topic:
aws sns create-topic --name $(TARGET_SNS_TOPIC_NAME)

create-publish-malware-policy:
aws iam create-policy \
--policy-name $(PUBLISH_MALWARE_POLICY_NAME) \
--policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"VisualEditor0\",\"Effect\":\"Allow\",\"Action\":[\"sns:TagResource\",\"sns:Publish\",\"account:GetAlternateContact\"],\"Resource\":\"*\"}]}"

create-publish-malware-role:
aws iam create-role \
--role-name $(PUBLISH_MALWARE_ROLE_NAME) \
--assume-role-policy-document "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Action\": \"sts:AssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"}}]}"

attach-policy:
aws iam attach-role-policy \
--role-name $(PUBLISH_MALWARE_ROLE_NAME) \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

aws iam attach-role-policy \
--role-name $(PUBLISH_MALWARE_ROLE_NAME) \
--policy-arn $(PUBLISH_MALWARE_POLICY_ARN)

create-function: zip
aws lambda create-function \
--function-name $(PUBLISH_MALWARE_LAMBDA_NAME) \
--role $(PUBLISH_MALWARE_ROLE_ARN) \
--runtime python3.9 \
--timeout 30 \
Comment on lines +36 to +37

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not matched with ones in the template. Why?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will align them in the next commit.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runtime is not aligned still.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the lost, fixed already.

--memory-size 128 \
--handler handler.lambda_handler \
--environment Variables=\{TARGET_SNS_ARN=$(TARGET_SNS_TOPIC_ARN)\} \
--zip-file fileb://zip/send-malware-to-sns.zip

subscribe-to-sns-topic:
aws sns subscribe \
--topic-arn $(SCAN_RESULT_TOPIC_ARN) \
--protocol lambda \
--notification-endpoint $(PUBLISH_MALWARE_LAMBDA_ARN) \
--region $(REGION)

add-lambda-permission:
aws lambda add-permission \
--function-name $(PUBLISH_MALWARE_LAMBDA_NAME) \
--region $(REGION) \
--statement-id sns \
--action lambda:InvokeFunction \
--principal sns.amazonaws.com \
--source-arn $(SCAN_RESULT_TOPIC_ARN)

update-function-code: zip
aws lambda update-function-code \
--function-name $(FUNCTION_NAME) \
--zip-file fileb://zip/send-malware-to-sns.zip
111 changes: 111 additions & 0 deletions post-scan-actions/aws-python-publish-malware-to-sns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Cloud One File Storage Security Post Scan Action - Publish Malware to SNS Topic

After a scan occurs, this example Lambda function publish the malware information to specific SNS topic.

## Prerequisites

### Find the 'ScanResultTopicARN' SNS topic ARN

- In the AWS console, go to **Services > CloudFormation** > your all-in-one stack > **Outputs** or **Services > CloudFormation** > your storage stack > **Outputs**.
- Scroll down to locate the **ScanResultTopicARN** Key.
- Copy the **ScanResultTopic** ARN to a temporary location. Example: `arn:aws:sns:us-east-1:123445678901:FileStorageSecurity-All-In-One-Stack-StorageStack-1IDPU1PZ2W5RN-ScanResultTopic-N8DD2JH1GRKF`

## Installation

### From Cloudformation Template

1. Visit AWS cloudformation console.
2. Click **Create Stack**.
3. Upload `template.yml` in this folder.
4. Fill in the paramters.
5. Click **Next**.
6. Check the require access capabilities checkboxes.
7. Click **Create Stack**.

### From Makefile

In a shell program, enter the following GNU Make command, using backslashes (`\`) to separate lines

1. **Create Malware Target SNS Topic**

```bash
TARGET_SNS_TOPIC_NAME=<YOUR_TARGET_SNS_TOPIC_NAME> \
make create-target-sns-topic
```

- where:
- `<YOUR_TARGET_SNS_TOPIC_NAME>` is the name for the new SNS topic which cloud be published the walmare information.

2. **Create Required Policy**

```bash
PUBLISH_MALWARE_POLICY_NAME=<YOUR_PUBLISH_MALWARE_POLICY_NAME> \
make create-publish-malware-policy
```

- where
- `<YOUR_PUBLISH_MALWARE_POLICY_NAME>` is the name for the new policy.

3. **Create Required Role**

```bash
PUBLISH_MALWARE_POLICY_NAME=<YOUR_PUBLISH_MALWARE_ROLE_NAME> \
make create-publish-malware-role
```

- where
- `<YOUR_PUBLISH_MALWARE_ROLE_NAME>` is the name for the new role.

4. **Attach Policy**

```bash
PUBLISH_MALWARE_ROLE_NAME=<YOUR_PUBLISH_MALWARE_ROLE_NAME> \
PUBLISH_MALWARE_POLICY_ARN=<YOUR_PUBLISH_MALWARE_POLICY_ARN> \
make create-publish-malware-role
```

- where
- `<YOUR_PUBLISH_MALWARE_POLICY_ARN>` is the policy ARN created from step 2.
- `<YOUR_PUBLISH_MALWARE_ROLE_NAME>` is the role name created from step 3.

5. **Create Lambda Function**

```bash
PUBLISH_MALWARE_LAMBDA_NAME=<YOUR_PUBLISH_MALWARE_LAMBDA_NAME> \
PUBLISH_MALWARE_ROLE_ARN=<YOUR_PUBLISH_MALWARE_ROLE_ARN> \
TARGET_SNS_TOPIC_ARN=<YOUR_TARGET_SNS_TOPIC_ARN> \
make create-function
```

- where
- `<YOUR_TARGET_SNS_TOPIC_ARN>` is the SNS topic ARN created from step 1.
- `<YOUR_PUBLISH_MALWARE_ROLE_ARN>` is the role ARN created from step 3.
- `<YOUR_PUBLISH_MALWARE_LAMBDA_NAME>` is the name for new lambda function.

6. **SNS Subscrition**

```bash
SCAN_RESULT_TOPIC_ARN=<YOUR_SCAN_RESULT_TOPIC_ARN> \
PUBLISH_MALWARE_LAMBDA_ARN=<YOUR_PUBLISH_MALWARE_LAMBDA_ARN> \
REGION=<YOUR_REGION> \
make subscribe-to-sns-topic
```

- where
- `<YOUR_PUBLISH_MALWARE_LAMBDA_ARN>` is the lambda function ARN created from step 5.
- `<YOUR_SCAN_RESULT_TOPIC_ARN>` is the SNS topic ARN from the storage stack.
- `<YOUR_REGION>` is the building AWS region.

7. **Add Lambda Permission**

```bash
PUBLISH_MALWARE_LAMBDA_NAME=<YOUR_PUBLISH_MALWARE_LAMBDA_NAME> \
REGION=<YOUR_REGION> \
SCAN_RESULT_TOPIC_ARN=<YOUR_SCAN_RESULT_TOPIC_ARN> \
make add-lambda-permission
```

- where
- `<YOUR_PUBLISH_MALWARE_LAMBDA_NAME>` is the lambda function and created from step 5.
- `<YOUR_REGION>` is the building AWS region.
- `<SCAN_RESULT_TOPIC_ARN>` is the SNS topic ARN from the storage stack.
78 changes: 78 additions & 0 deletions post-scan-actions/aws-python-publish-malware-to-sns/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import os
import json
from datetime import datetime
from urllib.parse import urlparse

import boto3


def get_bucket_name(file_url):

url = urlparse(file_url)
return url.hostname.split('.')[0]


def get_iso_str(ts):

dt = datetime.fromtimestamp(ts)
return dt.isoformat()


def fetch_account_name():

account_client = boto3.client('account')
response = account_client.get_alternate_contact(
AlternateContactType='SECURITY'
)

return response['AlternateContact']['Name']


def lambda_handler(event, context):

target_sns_arn = os.environ['TARGET_SNS_ARN']

scan_results = []

for record in event['Records']:

print('record', record)

# Message details from SNS event
message = json.loads(record['Sns']['Message'])
findings = message['scanning_result'].get('Findings')

# ARN info to get AWS Account ID
# arn = json.dumps(record['EventSubscriptionArn'])
# account_id = arn.split(":")[4].strip()

if not findings:
continue

finding = findings[0]

scan_results.append({
'bucket_name': get_bucket_name(message['file_url']),
'account_name': fetch_account_name(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the account name be cached?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, move out from the for loop.

'malware_type': finding['malware'],
'type': finding['type'],
'time': get_iso_str(message['timestamp'])
})

print('scan_results', scan_results)

sns_client = boto3.client('sns')
request_entries = [
{
'Message': json.dumps(scan_result),
'Id': str(i)
}
for i, scan_result in enumerate(scan_results)
]

response = sns_client.publish_batch(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SNS batch publish supports up to 10 messages, so be careful if the received events count would be more than 10. And you may need to handle partial failure cases.

TopicArn=target_sns_arn,
PublishBatchRequestEntries=request_entries,
)

print(response)
90 changes: 90 additions & 0 deletions post-scan-actions/aws-python-publish-malware-to-sns/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Metadata:
AWS::ServerlessRepo::Application:
Name: cloudone-filestorage-plugin-publish-malware-to-sns
Description: >-
According to the scan result from the scanner, publish the malware information
to the target SNS topic.
Author: Trend Micro Cloud One File Storage Security

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not going to publish this to AWS serverless repository, so this is not required.

Copy link
Owner Author

@Hundao Hundao Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove in next commit.


Parameters:
TargetSnsTopicName:
Type: String
Description: The name for the SNS topic to which malware information publish.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The customer may already have their own SNS topic. Could you figure out a way for using the existing SNS topic?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add 1 more parameter to let user fill in their customer SNS Topic ARN, and create a new one SNS topic if the value is empty string.

PublishMalwareLambdaName:
Type: String
Description: The name for the lambda function publishes the malware information to SNS topic.
PublishMalwarePolicyName:
Type: String
Description: The name for the policy includes sns and account authority.
PublishMalwareRoleName:
Type: String
Description: The name for the role to execute the lambda function.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For internal use resources, we usually don't let the customer choose the names. There might be problems, like name already existed, that would block customer from deploying the template. Any purpose for these parameters?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove these and using the default name (stack name).

ScanResultTopicARN:
Type: String
Description: The ARN of the scan result SNS topic in storage stack.
Resources:
TargetSnsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Ref TargetSnsTopicName
DisplayName: Malware notification
PublishMalwarePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Ref PublishMalwarePolicyName
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- 'sns:TagResource'
- 'sns:Publish'
- 'account:GetAlternateContact'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 'sns:TagResource'
- 'sns:Publish'
- 'account:GetAlternateContact'
- sns:TagResource
- sns:Publish
- account:GetAlternateContact

And what's sns:TagResource for?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sns:TagResource is redundant, I will remove it in next commit.

Resource: '*'
PublishMalwareRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref PublishMalwareRoleName
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- !Ref PublishMalwarePolicy
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
PublishMalwareLambda:
Type: AWS::Lambda::Function
Properties:
CodeUri: ./
Handler: handler.lambda_handler
FunctionName: !Ref PublishMalwareLambdaName
Runtime: python3.8
Timeout: 500

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you choose the timeout for this Lambda?

Role: !GetAtt
- PublishMalwareRole
- Arn
Environment:
Variables:
TARGET_SNS_ARN: !Ref TargetSnsTopic
MemorySize: 128
TopicSubscription:
Type: AWS::SNS::Subscription
Properties:
Protocol: lambda
Endpoint: !GetAtt PublishMalwareLambda.Arn
TopicArn: !Ref ScanResultTopicARN
InvokedLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt PublishMalwareLambda.Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the endpoint of AWS eventbridge. Should be sns.amazonaws.com?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'm very confused for why I can't receive the notification from SNS.

SourceArn: !Ref ScanResultTopicARN