Automatically Add New accounts to Netskope
Automatically Add New Accounts to Netskope
This feature enables you to add new AWS accounts to the Netskope tenant using the AWS Management Console. The setup requires a CFT, add_accounts_cft.yml
which calls Netskope’s REST API to create an instance of the AWS account in the tenant. The CFT can be used along with an existing AWS script in your environment such as automatic AWS account creation and resource provisioning to provide you the benefit of automatically setting up your new AWS accounts in the Netskope tenant.
The CFT contains a script that calls the following REST APIs to create and manage AWS accounts in the Netskope tenant.
https://<tenant-URL>/api/v1/public_cloud/account?token=<token>&op=create https://<tenant-URL>/api/v1/public_cloud/account?token=<token>&op=update https://<tenant-URL>./api/v1/public_cloud/account?token=<token>&op=delete
This setup requires you to perform the following:
Create the CFT File
Create a file called add_accounts_cft.yml
and copy the following contents into the file.
Description: CrossAccountRole creation Template
Parameters:
APIToken:
Type: String
Description: Enter REST API token for the tenant
NoEcho: true
TenantUrl:
Type: String
Description: Enter the tenant url
AccountName:
Type: String
Description: AWS account alias(if present) else human readable name
Default: ""
AdminEmail:
Type: String
Description: Enter admin email
SecurityScan:
Type: String
Default: 'true'
Description: 'Enter ( "true", "false" ), whether security scan is enabled.'
AllowedValues:
- true
- false
DLPScan:
Type: String
Default: 'true'
Description: 'Enter ( "true", "false" ), whether DLP Scan is enabled.'
AllowedValues:
- true
- false
MalwareScan:
Type: String
Default: 'false'
Description: 'Enter ( "true", "false" ), whether Malware Scan is enabled'
AllowedValues:
- true
- false
SecurityScanInterval:
Type: String
Default: '60'
Description: Select security scan interval
AllowedValues:
- '30'
- '60'
- '120'
- '360'
- '1440'
TrustedAccountID:
Type: String
Description: Enter the account ID provided by Netskope.
ExternalID:
Type: String
Description: Enter the external ID provided by Netskope.
Conditions:
AccountNameEmpty: !Equals
- Ref: AccountName
- ""
StorageScanEnabled: !Or
- !Equals
- 'true'
- Ref: DLPScan
- !Equals
- 'true'
- Ref: MalwareScan
SecurityScanEnabled: !Equals
- 'true'
- Ref: SecurityScan
AnyFeatureEnabled: !Or
- !Equals
- 'true'
- !Ref DLPScan
- !Equals
- 'true'
- !Ref SecurityScan
Outputs:
CrossRoleAccountRoleARN:
Description: The cross-account role that Netskope will use.
Value: !GetAtt
- CrossAccountRole
- Arn
Resources:
AutoAddInstance:
Condition: AnyFeatureEnabled
Properties:
ServiceToken: !GetAtt
- AutoAddInstanceCall
- Arn
adminemail: !Ref AdminEmail
accountname:
Fn::If:
- AccountNameEmpty
- Ref: AWS::AccountId
- Ref: AccountName
apitoken: !Ref APIToken
securityscan: !Ref SecurityScan
introspection: !Ref DLPScan
malware: !Ref MalwareScan
securityscaninterval: !Ref SecurityScanInterval
tenanturl: !Ref TenantUrl
sspolicy:
Fn::If:
- StorageScanEnabled
- Ref: StorageScanPolicy
- Ref: AWS::NoValue
cfpolicy:
Fn::If:
- StorageScanEnabled
- Ref: CloudFormationPolicy
- Ref: AWS::NoValue
scpolicy:
Fn::If:
- SecurityScanEnabled
- Ref: SecurityScanRolePolicy
- Ref: AWS::NoValue
Type: 'Custom::AutoAddInstance'
AutoAddInstanceCall:
Condition: AnyFeatureEnabled
Properties:
Code:
ZipFile: |
import boto3
import json
import time
import ssl
from urllib.request import Request, urlopen
def send(event, context, responseStatus, responseData,
physicalResourceId=None, noEcho=False):
responseUrl = event['ResponseURL']
responseBody = {}
responseBody['Status'] = responseStatus
responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
responseBody['StackId'] = event['StackId']
responseBody['RequestId'] = event['RequestId']
responseBody['LogicalResourceId'] = event['LogicalResourceId']
responseBody['NoEcho'] = noEcho
responseBody['Data'] = responseData
json_responseBody = json.dumps(responseBody)
try:
req = Request(responseUrl, method='PUT')
req.add_header('Content-Type', 'application/json; charset=utf-8')
jsondataasbyte = json_responseBody.encode("utf-8")
req.add_header('Content-Length', len(jsondataasbyte))
response = urlopen(req, jsondataasbyte)
except Exception as e:
print("send(..) failed executing requests.put(..): " + str(e))
def handler(event, context):
properties = event.get('ResourceProperties', {})
tenant_url = properties['tenanturl']
api_token = properties['apitoken']
account_id = properties["ServiceToken"].split(':')[4]
instance_name = properties['accountname']
time.sleep(30)
request_body = {
"app": "aws",
"instance_name": instance_name,
}
request_url = "https://{}/api/v1/public_cloud/account?token={}&op={}".format(tenant_url, api_token, '{}')
if event['RequestType'] == 'Delete':
request_url = request_url.format('delete')
else:
services = []
if properties["introspection"] == "true":
services.append("introspection")
if properties["securityscan"] == "true":
services.append("securityscan")
if properties["malware"] == "true":
services.append("malware")
request_body["use_for"] = services
admin_email = properties["adminemail"]
if 'securityscan' in services:
request_body["securityscan_interval"] = properties['securityscaninterval']
if 'Create' == event['RequestType']:
request_url = request_url.format('create')
request_body['accounts'] = [{
'account_id': account_id,
'account_name': instance_name,
'admin_email': admin_email
}]
elif event['RequestType'] == 'Update':
request_body["admin_email"] = admin_email
request_url = request_url.format('update')
request_body = json.dumps(request_body)
count = 3
while count:
try:
req = Request(request_url)
req.add_header('Content-Type', 'application/json; charset=utf-8')
jsondataasbyte = request_body.encode("utf-8")
req.add_header('Content-Length', len(jsondataasbyte))
response = urlopen(req, jsondataasbyte, context=ssl._create_unverified_context(), timeout=30)
resp = {"data": response.read().decode("utf-8")}
print("RESPONSE: {}".format(resp))
if "status" in resp["data"]:
json_resp = json.loads(resp["data"])
status = json_resp.get("status")
if status != "success":
failed = True
if json_resp.get("errors"):
for err in json_resp.get("errors"):
check_err_str = "instance_name: {} does not exist".format(instance_name)
if check_err_str in err:
failed = False
break
if failed:
send(event, context, "FAILED", resp, 'autoaddaccounts')
break
print("wait for 2 mins")
time.sleep(120)
send(event, context, "SUCCESS", resp, 'autoaddaccounts')
break
except Exception as exc:
print("Response exc : {}".format(str(exc)))
if count > 0:
count -= 1
if count == 0:
send(event, context, "FAILED", str(exc), 'autoaddaccounts')
FunctionName: AutoAddInstanceCall
Handler: index.handler
Role: !GetAtt
- NSLambdaAccessRole
- Arn
Runtime: python3.7
Timeout: 240
Type: 'AWS::Lambda::Function'
CloudFormationPolicy:
Condition: StorageScanEnabled
Properties:
PolicyDocument:
Statement:
- Action:
- cloudformation:CreateStack
- cloudformation:UpdateStack
- cloudformation:DeleteStack
Condition:
ForAllValues:Null:
cloudformation:RoleArn: true
Effect: Allow
Resource:
- arn:aws:cloudformation:*:*:stack/NetskopeStack/*
- Action:
- cloudformation:DescribeStacks
- sns:Publish
- sns:Unsubscribe
- sns:Subscribe
- sns:TagResource
- sns:ConfirmSubscription
- sns:SetTopicAttributes
- sns:ListTopics
- sns:CreateTopic
- sns:DeleteTopic
- sns:GetTopicAttributes
- events:DescribeRule
- events:ListRules
- events:PutEvents
- events:EnableRule
- events:PutRule
- events:PutTargets
- events:RemoveTargets
- events:DeleteRule
Effect: Allow
Resource:
- arn:aws:cloudformation:*:*:stack/NetskopeStack/*
- arn:aws:sns:*:*:CloudWatchEvent*
- arn:aws:events:*:*:rule/NetskopeStack*
Version: '2012-10-17'
PolicyName: CloudFormationPolicy
Roles:
- !Ref CrossAccountRole
Type: 'AWS::IAM::Policy'
CrossAccountRole:
Condition: AnyFeatureEnabled
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Condition:
StringEquals:
'sts:ExternalId': !Ref ExternalID
Effect: Allow
Principal:
AWS: !Join
- ''
- - 'arn:aws:iam::'
- !Ref TrustedAccountID
- ':root'
Sid: ''
Version: 2012-10-17
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/SecurityAudit'
Path: /
RoleName: Netskope_Role
Type: 'AWS::IAM::Role'
NSLambdaAccessRole:
Condition: AnyFeatureEnabled
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Version: 2012-10-17
Path: /
Policies:
- PolicyDocument:
Statement:
- Action:
- 'logs:*'
Effect: Allow
Resource:
- 'arn:aws:logs:*:*:*'
Version: 2012-10-17
PolicyName: LogAccess
Type: 'AWS::IAM::Role'
SecurityScanRolePolicy:
Condition: SecurityScanEnabled
Properties:
PolicyDocument:
Statement:
- Action:
- s3:ListBucket
- ses:ListIdentityPolicies
- s3:GetBucketAcl
- s3:GetBucketLocation
- s3:ListAllMyBuckets
- dynamodb:ListTagsOfResource
- sqs:ListDeadLetterSourceQueues
- sqs:GetQueueUrl
- sqs:GetQueueAttributes
- lambda:Get*
- lambda:List*
- cloudwatch:GetMetricStatistics
- eks:ListFargateProfiles
- ec2:GetEbsEncryptionByDefault
- codebuild:BatchGetProjects
- codebuild:ListSourceCredentials
- wafv2:GetLoggingConfiguration
- ses:GetAccount
- cloudtrail:ListTrails
Effect: Allow
Resource:
- '*'
- Action:
- s3:GetObject
- s3:GetObjectVersion
Effect: Allow
Resource:
- arn:aws:s3:::elasticbeanstalk-*
Version: '2012-10-17'
PolicyName: netskope-csa
Roles:
- !Ref CrossAccountRole
Type: 'AWS::IAM::Policy'
StorageScanPolicy:
Condition: StorageScanEnabled
Properties:
PolicyDocument:
Statement:
- Action:
- s3:ListAllMyBuckets
- s3:ListBucket
- s3:GetObject
- s3:GetObjectAcl
- s3:GetBucketLocation
- ec2:DescribeRegions
- s3:GetObjectTagging
- s3:GetBucketTagging
- s3:GetEncryptionConfiguration
- s3:GetBucketPublicAccessBlock
- s3:GetBucketPolicyStatus
- s3:GetBucketAcl
Effect: Allow
Resource:
- '*'
Version: '2012-10-17'
PolicyName: StorageScanPolicy
Roles:
- !Ref CrossAccountRole
Type: 'AWS::IAM::Policy'
Import the CFT to AWS Management Console
Import add_accounts_cft.yml
to a new CloudFormation stack in each AWS account. To import the CFT,
-
Log in to the AWS Management Console using the credentials of the AWS account you are setting up with Netskope for IaaS and navigate to Services > CloudFormation.
-
In the CloudFormation page, click Create stack.
-
Select Upload a template file and click Choose file to upload the
add_accounts_cft.yml
. Click Next. -
In the Specify stack details page, specify a Stack name. The stack name must:
-
Only contain alphanumeric characters and hyphens,
-
start with an alphabet, and
-
not be longer than 128 characters.
-
-
Define the parameters in the template and click Next. For information, see Provide Input Parameters in the Stack Details Page.
-
In the Configure stack options page, use the default configuration, and click Next.
-
Review your stack details on the Review page, click the acknowledgment and then click Create stack.
When the creation process is complete, your stack will be displayed on the CloudFormation page and the AWS account will be setup in the Netskope tenant with the services specified in the add_accounts_cft.yml
.
Provide Input Parameters in the Stack Details Page
You must provide the following input parameters in the stack created using add_accounts_cft.yml
to set up the AWS account with Netskope’s Public Cloud Security features such a Continuous Security Assessment and Storage Scan.
Parameter | Value |
---|---|
APIToken | Enter Netskope’s REST API token. |
AccountName | Enter an AWS account name, preferably the AWS account alias. |
AdminEmail | This parameter is optional. Enter an admin email. |
DLPScan | Set to true by default to enable DLP scans. To disable DLP scans, enter false . |
ExternalID | Follow the steps below to find the external ID. Note These are dummy steps to identify the external ID only.
|
MalwareScan | Set to true by default to enable threat and malware scans. To disable threat and malware scans, enter false . |
SecurityScan | Set to true by default to enable security assessment scans. To disable security assessment scans, enter false . |
SecurityScanInterval | The default scan interval is set to 60 minutes. You can change the interval time in minutes to one of the following values.
|
TenantUrl | Enter Netskope’s tenant URL. |
TrustedAccountID | Follow the steps below to find the trusted account ID. Note These are dummy steps to identify the trusted account ID only.
|