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
For more information on REST API endpoints see, Public Cloud API Endpoints for REST API v1.

This setup requires you to perform the following:

  1. Create the CFT File

  2. Import the CFT to AWS Management Console

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,

  1. 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.

  2. In the CloudFormation page, click Create stack.

  3. Select Upload a template file and click Choose file to upload the add_accounts_cft.yml . Click Next.

    Auto Add Cft Aws1.png
  4. 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.

  5. Define the parameters in the template and click Next. For information, see Provide Input Parameters in the Stack Details Page.

    Auto Add Cft Aws2.png
  6. In the Configure stack options page, use the default configuration, and click Next.

  7. 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.

ParameterValue
APITokenEnter Netskope’s REST API token.
AccountNameEnter an AWS account name, preferably the AWS account alias.
AdminEmailThis parameter is optional. Enter an admin email.
DLPScanSet to true by default to enable DLP scans. To disable DLP scans, enter false.
ExternalIDFollow the steps below to find the external ID.

Note

These are dummy steps to identify the external ID only.

  1. Navigate to Settings > Configure App Access > Classic > IaaS, then select AWS and click Setup.
  2. In the New Setup window, enter the following details:
    1. 12 digit AWS account ID, followed by the account name. Follow the format as described in the text box.
    2. Keep the default service checked and click Next.
  3. Download the CFT file.
  4. Exit the New Setup window.
  5. Open the CFT file and find the ExternalId.
MalwareScanSet to true by default to enable threat and malware scans. To disable threat and malware scans, enter false.
SecurityScanSet to true by default to enable security assessment scans. To disable security assessment scans, enter false.
SecurityScanIntervalThe default scan interval is set to 60 minutes. You can change the interval time in minutes to one of the following values.
  • 30
  • 120
  • 360
  • 1440
TenantUrlEnter Netskope’s tenant URL.
TrustedAccountIDFollow the steps below to find the trusted account ID.

Note

These are dummy steps to identify the trusted account ID only.

  1. Log in to the Netskope tenant UI.
  2. Navigate to Settings > Configure App Access > Classic > IaaS, then select AWS and click Setup.
  3. In the New Setup window, enter the following details:
    1. 12 digit AWS account ID, followed by the account name. Follow the format as described in the text box.
    2. Keep the default service checked and click Next.
  4. Download the CFT file.
  5. Exit the New Setup window.
  6. Open the CFT file and find AWS: – arn:aws:iam::<TrustedAccountID>:root.
Share this Doc

Automatically Add New accounts to Netskope

Or copy link

In this topic ...