Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding Sentry and Network Security options to Account Deployment #96

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Add AWS Account to Cloud One
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it is important to note here that you need to ensure the AWS Account before hand can handle the amount of S3 buckets that will be created by Sentry. I have seen this cause many failed deployments and rollbacks are even more of a challenge to navigate with Sentry.
Or IMHO, just remove sentry part

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do we have this requirement documented in the product documentation somewhere?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Its not documented. The stack creates 2 S3 buckets per AWS Region that we support. There are 31 total AWS regions.


To fully integrate an AWS account in Cloud One, you must deploy resources in your AWS account and do manual steps in Trend Cloud One dashboard. This CloudFormation template automates all these steps on your behalf, including integrating it to Vision One.
To fully integrate an AWS account in Cloud One, you must deploy resources in your AWS account and do manual steps in Trend Cloud One dashboard. This CloudFormation template automates all these steps on your behalf, including integrating it to Vision One. You can also optionally also deploy Sentry and Network Security with hosted infrastructure to these AWS accounts via this automation.

## What does it actually do?

Expand Down Expand Up @@ -47,6 +47,10 @@ To fully integrate an AWS account in Cloud One, you must deploy resources in you
- Description: Decides if a new Trail should be created. Defaults to False, so you must enter a S3 Bucket name in the ExistingCloudtrailBucketName parameter. In case you pick True, a new trail and bucket will be created for you. Setting this to True will incur in extra costs.
- ExistingCloudtrailBucketName:
- Description: Specify the name of an existing bucket that you want to use for forwarding to Trend Micro Cloud One. Only used if CreateNewTrail is set to False.
- DeployCloudSentry:
- Description: Decides if the Cloud Sentry integration should be deployed. Defaults to True.
- DeployNetworkSecurityIntegration:
- Description: Decides if the Network Security integration should be deployed. Defaults to True.

### Shouldn't be Changed from Default

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ Parameters:
- jp-1
- ca-1
- de-1

CloudOneFeatures:
Description: Comma separated list of Cloud One Features to deploy. Defaults to deploy Sentry.
More info at https://cloudone.trendmicro.com/docs/cloud-account-management/api-reference/tag/AWS#operation/describeAWSStackTemplate
Type: String
Default: cloud-sentry
RegionsToDeploy:
Description: Comma separated list of AWS Regions where the Features will be deployed to. If empty, all accessible regions will be used.
More info at https://cloudone.trendmicro.com/docs/cloud-account-management/api-reference/tag/AWS#operation/describeAWSStackTemplate
Type: String
Default: ""


Resources:
CloudOneIntegrationStack:
Expand All @@ -33,7 +43,7 @@ Resources:
CloudOneRegion: !Ref CloudOneRegion
CloudOneAccountID: !Ref CloudOneAccountID
CloudOneOIDCProviderURL: !Sub 'cloudaccounts.${CloudOneRegion}.cloudone.trendmicro.com'
TemplateURL: !Sub 'https://cloud-one-cloud-accounts-${AWS::Region}.s3.${AWS::Region}.amazonaws.com/templates/aws/cloud-account-management-role.template'
TemplateURL: !GetAtt GetCloudOneIntegrationTemplate.templateURL

AddAWSAccountToCloudOneFunction:
Type: AWS::Lambda::Function
Expand All @@ -51,63 +61,61 @@ Resources:
CloudOneApiKey: !Ref CloudOneApiKey
Code:
ZipFile:
!Sub
|-
import json
import os
import urllib3
import boto3
import cfnresponse

def lambda_handler(event, context):
status = cfnresponse.SUCCESS
response_data = {}
physicalResourceId = None
try:

cloudOneRoleArn = os.environ['CloudOneRoleArn']
cloudOneRegion = os.environ['CloudOneRegion']
cloudOneApiKey = os.environ['CloudOneApiKey']

headers = {
'api-version': 'v1',
'Authorization': 'ApiKey '+cloudOneApiKey+'',
'Content-Type': 'application/json'
}
|-
import json
import os
import urllib3
import cfnresponse

def lambda_handler(event, context):
status = cfnresponse.SUCCESS
response_data = {}
physicalResourceId = None
try:

cloudOneRoleArn = os.environ['CloudOneRoleArn']
cloudOneRegion = os.environ['CloudOneRegion']
cloudOneApiKey = os.environ['CloudOneApiKey']
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was looking through the templates and maybe I missed it:
Is this API Key being encrypted?
I can only see it being flagged as NoEcho in the params value adds

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. I need to update this to use Secrets Manager and KMS.


headers = {
'api-version': 'v1',
'Authorization': 'ApiKey '+cloudOneApiKey+'',
'Content-Type': 'application/json'
}

http = urllib3.PoolManager()
http = urllib3.PoolManager()


if event["RequestType"] == "Create" or event["RequestType"] == "Update":

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws'

payload = json.dumps({
'roleARN': cloudOneRoleArn
})
encoded_payload = payload.encode("utf-8")
print(url)
response = http.request("POST", url=url, headers=headers, body=encoded_payload)
print(response)
response_json_data = json.loads(response.data.decode("utf-8"))
print(response_json_data)
physicalResourceId = response_json_data["id"]
response_data = {"ID": response_json_data["id"]}

else: # if event["RequestType"] == "Delete":
id = event["PhysicalResourceId"]

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id

print(url)
response = http.request("DELETE", url=url, headers=headers)
print(response)

if event["RequestType"] == "Create" or event["RequestType"] == "Update":

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws'

payload = json.dumps({
'roleARN': cloudOneRoleArn
})
encoded_payload = payload.encode("utf-8")
print(url)
response = http.request("POST", url=url, headers=headers, body=encoded_payload)
print(response)
response_json_data = json.loads(response.data.decode("utf-8"))
print(response_json_data)
physicalResourceId = response_json_data["id"]
response_data = {"ID": response_json_data["id"]}

else: # if event["RequestType"] == "Delete":
id = event["PhysicalResourceId"]

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id

print(url)
response = http.request("DELETE", url=url, headers=headers)
print(response)

except Exception as e:
print(e)
status = cfnresponse.FAILED

cfnresponse.send(event, context, status, response_data, physicalResourceId)
except Exception as e:
print(e)
status = cfnresponse.FAILED

cfnresponse.send(event, context, status, response_data, physicalResourceId)

AddAWSAccountToCloudOne:
Type: AWS::CloudFormation::CustomResource
Expand All @@ -128,9 +136,124 @@ Resources:
- sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"

GetCloudOneIntegrationTemplateFunction:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.9
Architectures:
- arm64
Timeout: 60
Handler: index.lambda_handler
Role: !GetAtt GetCloudOneIntegrationTemplateFunctionRole.Arn
Environment:
Variables:
CloudOneRegion: !Ref CloudOneRegion
CloudOneApiKey: !Ref CloudOneApiKey
AWSRegion: !Ref AWS::Region
CloudOneFeatures: !Ref CloudOneFeatures
RegionsToDeploy: !Ref RegionsToDeploy
Code:
ZipFile:
|-
import json
import os
import urllib3
import boto3
import cfnresponse

ec2 = boto3.client('ec2')

def lambda_handler(event, context):
status = cfnresponse.SUCCESS
response_data = {}
physicalResourceId = None
try:

cloudOneRegion = os.environ['CloudOneRegion']
cloudOneApiKey = os.environ['CloudOneApiKey']
aws_region = os.environ['AWSRegion'] or os.environ['AWS_REGION']
features = os.environ['CloudOneFeatures']
regions_to_deploy = os.environ['RegionsToDeploy']

headers = {
'api-version': 'v1',
'Authorization': 'ApiKey '+cloudOneApiKey+'',
'Content-Type': 'application/json'
}

http = urllib3.PoolManager()

if event["RequestType"] == "Create" or event["RequestType"] == "Update":

if not regions_to_deploy:
response = [region['RegionName'] for region in ec2.describe_regions()['Regions']]
print('Regions:', response)
regions_to_deploy = ",".join(response)

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/templates'

query_parameters = {
"features": features,
"awsRegion": aws_region,
"featureAWSRegions": regions_to_deploy
}

print(url)
response = http.request("GET", url=url, headers=headers, fields=query_parameters)
print(response)
response_json_data = json.loads(response.data.decode("utf-8"))
print(response_json_data)
physicalResourceId = response_json_data["templateURL"]
response_data = {
"templateURL": response_json_data["templateURL"],
"parameters": response_json_data["parameters"]
}

else: # if event["RequestType"] == "Delete":
id = event["PhysicalResourceId"]

url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id

print(url)
response = http.request("DELETE", url=url, headers=headers)
print(response)

except Exception as e:
print(e)
status = cfnresponse.FAILED

cfnresponse.send(event, context, status, response_data, physicalResourceId)

GetCloudOneIntegrationTemplate:
Type: AWS::CloudFormation::CustomResource
Properties:
ServiceToken: !GetAtt GetCloudOneIntegrationTemplateFunction.Arn

GetCloudOneIntegrationTemplateFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: GetAvailableRegions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:DescribeRegions
Resource: '*'


Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Metadata:
- VisionOneServiceToken
- CreateNewTrail
- ExistingCloudtrailBucketName
- DeployCloudSentry
- DeployNetworkSecurityIntegration
- Label:
default: 'Warning: Do not modify the fields below unless you know what you
are doing. Modifications may cause your deployment to fail.'
Expand All @@ -23,12 +25,32 @@ Metadata:
VisionOneServiceToken:
default: VisionOneServiceToken


Parameters:
CloudOneApiKey:
Description: Cloud One API Key. You can learn more about it at https://cloudone.trendmicro.com/docs/identity-and-account-management/c1-api-key/
Type: String
NoEcho: true
# DeployCloudTrailIntegration:
# Description: Decides if the CloudTrail integration should be deployed. Defaults to True.
# Type: String
# AllowedValues:
# - "True"
# - "False"
# Default: "True"
DeployCloudSentry:
Description: Decides if the Cloud Sentry integration should be deployed. Defaults to True.
Type: String
AllowedValues:
- "True"
- "False"
Default: "True"
DeployNetworkSecurityIntegration:
Description: Decides if the Network Security integration should be deployed. Defaults to True.
Type: String
AllowedValues:
- "True"
- "False"
Default: "True"
VisionOneServiceToken:
Description: Vision One Service Token. See step 1 at https://docs.trendmicro.com/en-us/enterprise/trend-micro-xdr-help/ConfiguringCloudOneWorkloadSecurity/
Type: String
Expand Down Expand Up @@ -72,6 +94,17 @@ Conditions:
HasNoExistingCloudtrailBucketName:
!Equals ["True", !Ref CreateNewTrail]

DeployCloudSentry:
!Equals [!Ref DeployCloudSentry, "True"]

DeployNetworkSecurityIntegration:
!Equals [!Ref DeployNetworkSecurityIntegration, "True"]

DeployCloudSentryAndNetworkSecurityIntegration:
!And
- !Equals [!Ref DeployCloudSentry, "True"]
- !Equals [!Ref DeployNetworkSecurityIntegration, "True"]

Resources:
GetCloudOneRegionAndAccountStack:
Type: AWS::CloudFormation::Stack
Expand Down Expand Up @@ -106,6 +139,7 @@ Resources:
CloudOneRegion: !GetAtt GetCloudOneRegionAndAccountStack.Outputs.CloudOneRegion
CloudOneAccountID: !GetAtt GetCloudOneRegionAndAccountStack.Outputs.CloudOneAccountId
CloudOneApiKey: !Ref CloudOneApiKey
CloudOneFeatures: !If [DeployCloudSentryAndNetworkSecurityIntegration, "cloud-sentry,network-security-deployment", !If [DeployCloudSentry, "cloud-sentry", !If [DeployNetworkSecurityIntegration, "network-security-deployment", ""]]]
TemplateURL: !Sub 'https://${QSS3BucketName}.s3.amazonaws.com/${QSS3KeyPrefix}Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml'
DependsOn:
- VisionOneEnrollmentStack
Expand Down
Loading