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-name>.goskope.com/api/v1/public_cloud/account?token=<token>&op=create https://<tenant-name>.goskope.com/api/v1/public_cloud/account?token=<token>&op=update https://<tenant-name>.goskope.com/api/v1/public_cloud/account?token=<token>&op=delete
Note
For more information on REST API endpoints see, Public Cloud API Endpoints for REST API v1.
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 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: 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: !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": 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: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 Effect: Allow Resource: - '*' 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 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 |
ExternalID | Follow the steps below to find the external ID. Note These are dummy steps to identify the external ID only.
|
MalwareScan | Set to |
SecurityScan | Set to |
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.
|