Inside the AWS Onboarding Script
The AWS CloudFormation template automates the onboarding of an AWS account into ACE. This template provisions the required AWS resources and IAM permissions, enabling AlgoSec to integrate with and monitor the environment. The onboarding process is fully automated through an AWS Lambda function.
There are two scripts available.
-
Regular AWS flow
-
Management accounts flow
This topic outlines each step of the script to help users understand its purpose and functionality.
The following is a breakdown of the regular AWS flows sections and their purposes:
1. Template Version
AWSTemplateFormatVersion: "2010-09-09"
Purpose: Specifies the CloudFormation template version.
2. Define Resources
Resources:
Purpose: Begins the section where all AWS resources are defined.
3. AlgoSec Role
AlgosecRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Principal:
AWS: '<AlgoSec Account>'
Condition:
StringEquals:
sts:ExternalId: algosec-xxx
Effect: "Allow"
Action:
- "sts:AssumeRole"
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/SecurityAudit'
Purpose: Defines an IAM Role that AlgoSec can assume to access AWS resources. It specifies the trusted entity (AlgoSec's AWS account) and an external ID for additional security.
arn:aws:iam::aws:policy/SecurityAudit - grants read-only access to resources across AWS account. This allows Cloud App Analyzer to scan configurations, permissions, and logs without making changes, enabling a comprehensive security posture assessment.
4. Onboarding Role
OnboardingFunctionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Purpose: Defines an IAM Role for the Lambda function, allowing it to be assumed by the Lambda service and granting basic execution permissions.
5 AlgoSec Notification Function
AlgosecNotificationFunction:
Type: 'AWS::Lambda::Function'
DependsOn:
- OnboardingFunctionRole
Properties:
FunctionName: 'AlgosecOnboardingNotification'
Code:
ZipFile: |
Purpose: Begins the definition of the Lambda function, specifying its name and that the code will be provided inline.
6. Modules
const response = require('cfn-response');
const https = require('https');
Purpose: Imports required modules.
7. Function Handler
exports.handler = async (event, context, callback) => {
Purpose: Defines the Lambda function handler.
8. Helper Function
const httpsRequest = (options, data) => {
return new Promise((resolve, reject) => {
const request = https.request(options, response => {
let responseBody = '';
response.on('data', chunk => responseBody += chunk.toString());
if (response.statusCode >= 200 && response.statusCode < 300) {
response.on('end', () => resolve(responseBody));
} else {
response.on('end', () => reject(responseBody));
}
});
request.on('error', err => reject(err));
request.write(data)
request.end();
});
};
Purpose: Defines a helper function for making HTTPS requests.
9. Login Access Keys
try {
const loginOptions = {
hostname: 'us.app.algosec.com',
path: '/api/algosaas/auth/v1/access-keys/login',
method: 'POST'
};
Purpose: Begins a try-catch block and sets up options for the login request.
10. Login Credentials
const additionals = Buffer.from('eyJ0ZW', 'base64').toString('utf-8');
Purpose: Decodes base64-encoded login credentials.
11. Login Request
const loginResponse = JSON.parse(await httpsRequest(loginOptions, additionals));
const loginResponse = JSON.parse(await httpsRequest(loginOptions, additionals));
Purpose: Sends login request and parses the response.
12. Role Assignment
const onboardingOptions = {
hostname: 'us.app.algosec.com',
path: '/api/algosaas/onboarding/v1/aws',
method: 'POST',
headers: {
'Authorization': 'Bearer ' + loginResponse.access_token
}
};
Purpose: Sets up options for the onboarding request, including the auth token.
13. Onboarding Request
const onboardingData = JSON.stringify({ event, role_arn: process.env.algosecRole, external_id: process.env.externalId, supportChanges: false, flowLogs: true}); await httpsRequest(onboardingOptions, onboardingData);
Purpose: Sends the onboarding request.
14. Cloud Network Security Onboarding Call
callback(null, { 'Status': 'SUCCEEDED' });
response.send(event, context, response.SUCCESS);
Purpose: Sends success responses.
15. Response Handling
Handler: index.handler
MemorySize: 128
Runtime: nodejs18.x
Timeout: 10
Role: !GetAtt OnboardingFunctionRole.Arn
Environment:
Variables:
algosecRole: !GetAtt AlgosecRole.Arn
externalId: algosec-xxx
Purpose: Configures the Lambda function's runtime settings and environment variables.
16. Lambda Function
AlgosecNotificationInvoke:
Type: Custom::OnboardingNotificationInvokation
Version: "1.0"
Properties:
ServiceToken: !GetAtt AlgosecNotificationFunction.Arn
Touch: 1742387171667
Purpose: Defines a custom resource to invoke the Lambda function.
17. Read Only Policy
CloudFlowReadOnlyPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "CloudFlowReadOnly"
Roles:
- Ref: "AlgosecRole"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "CloudFlowReadOnly"
Action:
# ... (list of allowed actions)
Effect: "Allow"
Resource: "*"
Purpose: Defines an IAM policy granting read-only access to various AWS services.
18. Flow Logs Policy
CloudFlowReadFlowLogsPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "CloudFlowReadFlowLogs"
Roles:
- Ref: "AlgosecRole"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "CloudFlowReadFlowLogs"
Action:
- "s3:GetObject*"
Effect: "Allow"
Resource: "*"
Purpose: Defines another IAM policy specifically for reading S3 objects (likely flow logs).
19. Write Policy
CloudFlowWritePolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "CloudFlowWrite"
Roles:
- Ref: "AlgosecRole"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "CloudFlowWrite"
Action:
- "ec2:AuthorizeSecurityGroupEgress"
- "ec2:AuthorizeSecurityGroupIngress"
- "ec2:RevokeSecurityGroupEgress"
- "ec2:RevokeSecurityGroupIngress"
Effect: "Allow"
Resource: "*"
Purpose: Write Policy for Cloud Network security
20. Prevasio Base Cloud Service Policy
PrevasioCSPMPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "PrevasioCSPM"
Roles:
- Ref: "AlgosecRole"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "PrevasioCSPM"
Effect: "Allow"
Action:
- "ses:DescribeActiveReceiptRuleSet"
- "logs:DescribeLogGroups"
- "logs:DescribeMetricFilters"
- "dlm:GetLifecyclePolicies"
- "kms:GetKeyRotationStatus"
- "ecr-public:GetAuthorizationToken"
- "ecr:GetAuthorizationToken"
- "ecr:BatchGetImage"
- "ecr:GetDownloadUrlForLayer"
- "ecr:GetRepositoryPolicy"
- "ecr:SetRepositoryPolicy"
- "ecr:DeleteRepositoryPolicy"
- "cloudwatch:GetMetricStatistics"
- "sts:GetServiceBearerToken"
- "inspector2:ListFindings"
- "inspector2:ListCoverage"
- "appflow:DescribeFlow"
- "imagebuilder:List*"
- "imagebuilder:Get*"
- "wafv2:DescribeManagedRuleGroup"
- "wafv2:GetRuleGroup"
- "wafv2:ListManagedRuleSets"
- "wafv2:ListResourcesForWebACL"
- "apprunner:ListAssociatedServicesForWebAcl"
- "cognito-idp:ListResourcesForWebACL"
- "ec2:DescribeVerifiedAccessInstanceWebAclAssociations"
- "wafv2:CheckCapacity"
Resource: "*"
- Effect: "Allow"
Action:
- "s3:GetObject"
Resource:
- "arn:aws:s3:::elasticbeanstalk*"
Purpose: Creates the primary IAM role that Prevasio will assume to perform security scanning. Includes additional permissions needed for comprehensive security scanning.
21 Prevasio ECR Exclusion Role (CD-Mitigation)
PrevasioECRExclusionsRole: # Optional for CD Mitigation
Type: 'AWS::IAM::Role'
Properties:
RoleName: !Sub 'PrevasioECRExclusionsRole'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Policies:
- PolicyName: PrevasioECRExclusionsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetRepositoryPolicy
- ecr:SetRepositoryPolicy
- ecr:DescribeRepositories
Resource: "*"
Purpose:PrevasioECRExclusionsRole Defines an IAM role for Lambda functions with permissions to:
-
Execute basic Lambda operations
-
Assume the Prevasio secret role
-
Access and update the API connection secret
-
Interact with ECR repositories
The inline policy grants specific permissions for cross-account secret access and ECR repository management, which are crucial for the onboarding process.
22. Prevasio Image Push Handler Function (CD-Mitigation)
PrevasioImagePushHandlerFunction: # Optional for CD Mitigation
Type: 'AWS::Lambda::Function'
DependsOn:
- OnboardingFunctionRole
Properties:
FunctionName: !Sub 'PrevasioImagePushHandlerFunction'
Code:
ZipFile: |
Purpose: A Lambda function that processes ECR image push events, the function sends container image push events to Prevasio for potential container security scanning.
23. ImagePushedEventBridgeRule and InvokeImagePushHandlerFunctionPermission (CD-Mitigation)
PrevasioImagePushedEventBridgeRule: # Optional for CD Mitigation
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.ecr
detail-type:
- ECR Image Action
detail:
result:
- SUCCESS
Targets:
- Arn:
Fn::GetAtt:
- PrevasioImagePushHandlerFunction
- Arn
Id: PrevasioImagePushHandlerFunction
PrevasioInvokeImagePushHandlerFunctionPermission: # Optional for CD Mitigation
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt:
- PrevasioImagePushHandlerFunction
- Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn:
Fn::GetAtt:
- PrevasioImagePushedEventBridgeRule
- Arn
Purpose: Sets up EventBridge to trigger the image push handler when images are pushed to ECR.
This enables automated notifications to Prevasio when new container images are available, facilitating continuous security scanning.
24 PrevasioECRExclusionsFunction
PrevasioECRExclusionsFunction: # Optional for CD Mitigation
Type: 'AWS::Lambda::Function'
DependsOn:
- PrevasioECRExclusionsRole
Properties:
FunctionName: !Sub 'PrevasioECRExclusionsFunction${TenantId}'
Code:
ZipFile: |
import json
import boto3
import os
def handler(event, context):
is_cf_event = 'ResponseURL' in event
if is_cf_event:
import cfnresponse
try:
account_id = boto3.client('sts').get_caller_identity().get('Account')
role_arn = os.environ['algosecRole']
assumed_role_arn = role_arn.replace(':iam::', ':sts::').replace(':role/', ':assumed-role/') + '/algosec'
root_role = f'arn:aws:iam::{account_id}:root'
ecr_client = boto3.client('ecr')
for repository in ecr_client.describe_repositories().get('repositories', []):
try:
policy = json.loads(ecr_client.get_repository_policy(repositoryName=repository['repositoryName']).get('policyText', '{}'))
statements = policy.get('Statement', [])
should_save_policy = False
for statement in statements:
if statement.get('Sid') == 'Prevasio Locker':
should_save_policy = True
not_principal = statement.get('NotPrincipal', {'AWS': []})
roles = not_principal.get('AWS', [])
if event['RequestType'] == 'Delete':
for role in [root_role, role_arn, assumed_role_arn]:
try:
if isinstance(roles, list):
roles.remove(role)
elif roles == role:
roles = []
except ValueError:
pass
if not roles:
not_principal = {}
else:
for role in [root_role, role_arn, assumed_role_arn]:
roles.append(role)
if not_principal:
statement.pop('Principal', None)
statement['NotPrincipal'] = not_principal
else:
statement.pop('NotPrincipal', None)
statement['Principal'] = {'AWS': '*'}
if should_save_policy:
ecr_client.set_repository_policy(repositoryName=repository['repositoryName'],
policyText=json.dumps(policy))
except ecr_client.exceptions.RepositoryPolicyNotFoundException:
pass
except Exception as e:
err_msg = f'Failed to update ECR repositories policies: {str(e)}'
print(err_msg)
if is_cf_event:
cfnresponse.send(event, context, cfnresponse.FAILED, {'Message': err_msg})
return
if is_cf_event:
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
Handler: index.handler
MemorySize: 128
Runtime: python3.11
Timeout: 60
Role: !GetAtt PrevasioECRExclusionsRole.Arn
Environment:
Variables:
algosecRole: !GetAtt AlgosecRole.Arn
Purpose:
This Lambda function manages ECR repository policies to ensure or remove exclusion of specific IAM roles (e.g., root, AlgosecRole, and its assumed variant) under the "Prevasio Locker" statement. It's designed to run both as a CloudFormation Custom Resource (handling Create, Update, and Delete events) and as a standalone script.
-
On Create/Update, it adds the relevant roles to the NotPrincipal field of applicable ECR policies.
-
On Delete, it removes those roles and resets the policy if no exclusions remain.
25. PrevasioECRExclusionsFunctionInvoke
PrevasioECRExclusionsFunctionInvoke: # Optional for CD Mitigation
Type: AWS::CloudFormation::CustomResource
Version: "1.0"
Properties:
ServiceToken: !GetAtt PrevasioECRExclusionsFunction.Arn
Purpose: This custom resource triggers the PrevasioECRExclusionsFunction Lambda during CloudFormation stack operations (Create, Update, Delete). It ensures that the ECR exclusion logic is automatically executed as part of the deployment lifecycle.
-
Uses the Lambda’s ARN as a service token.
-
Ensures the function logic runs without manual invocation.
-
Typically used for policy enforcement or cleanup during provisioning.
26. Kubernetes Security
27. VM Scan
The following is a breakdown of the AWS Management accounts flow sections and their purposes:
1. Template Version
AWSTemplateFormatVersion: "2010-09-09"
Purpose: Specifies the CloudFormation template version.
2. Define Resources
Resources:
Purpose: Begins the section where all AWS resources are defined.
3. Onboarding Management Lambda Role
OnboardingManagementLambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Purpose: Defines an IAM Role for the Lambda function, allowing it to be assumed by the Lambda service and granting basic execution permissions.
4. AlgoSec Management Role
AlgosecManagementRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Principal:
AWS: arn:aws:iam::<algosec_account_id>:root
Condition:
StringEquals:
sts:ExternalId: <external_id>
Policies:
- PolicyName: management-policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'organizations:ListAccounts'
Resource: '*'
- Effect: Allow
Action:
- organizations:ListParents
- organizations:DescribeOrganizationalUnit
Resource:
- Fn::Sub: arn:aws:organizations::${AWS::AccountId}:account/o-*/*
- Fn::Sub: arn:aws:organizations::${AWS::AccountId}:ou/o-*/*
Purpose: Defines an IAM Role that AlgoSec can assume to access AWS resources. It specifies the trusted entity (AlgoSec's AWS account) and an external ID for additional security.
5. Onboarding Management Function
OnboardManagementFunction:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: !Sub 'AlgosecOnboardingManagementNotification'
Environment:
Variables:
algosecManagementRole: !GetAtt AlgosecManagementRole.Arn
externalId: <external_id>
Code:
ZipFile: |
Purpose: Begins the definition of the Lambda function, specifying its name and that the code will be provided inline.
6. Modules
const response = require('cfn-response');
const https = require('https');
Purpose: Imports required modules.
7. Function Handler
exports.handler = async (event, context, callback) => {
Purpose: Defines the Lambda function handler.
8. Helper Function
const httpsRequest = (options, data) => {
return new Promise((resolve, reject) => {
const request = https.request(options, response => {
let responseBody = '';
response.on('data', chunk => responseBody += chunk.toString());
if (response.statusCode >= 200 && response.statusCode < 300) {
response.on('end', () => resolve(responseBody));
} else {
response.on('end', () => reject(responseBody));
}
});
request.on('error', err => reject(err));
request.write(data)
request.end();
});
};
Purpose: Defines a helper function for making HTTPS requests.
9. Login Access Keys
try {
const loginOptions = {
hostname: 'us.app.algosec.com',
path: '/api/algosaas/auth/v1/access-keys/login',
method: 'POST'
};
Purpose: Begins a try-catch block and sets up options for the login request.
10. Login Credentials
const additionals = Buffer.from('eyJ0ZW5hb', 'base64').toString('utf-8');
Purpose: Decodes base64-encoded login credentials.
11. Login Request
const loginResponse = JSON.parse(await httpsRequest(loginOptions, additionals));
const loginResponse = JSON.parse(await httpsRequest(loginOptions, additionals));
Purpose: Sends login request and parses the response.
12. Role Assignment
const onboardingManagementOptions = {
hostname: 'us.app.algosec.com',
path: '/api/algosaas/onboarding/v1/aws/management-account',
method: 'POST',
headers: {
'Authorization': 'Bearer ' + loginResponse.access_token
}
};
Purpose: Sets up options for the onboarding request, including the auth token.
13. Onboarding Request
const onboardingManagementData = JSON.stringify({ event, role_arn: process.env.algosecManagementRole, external_id: process.env.externalId});
await httpsRequest(onboardingManagementOptions, onboardingManagementData);
Purpose: Sends the management account onboarding request.
14. Set PhysicalResourceId for the lambda resource
let physicalResourceId = event.PhysicalResourceId;
if (event.RequestType === 'Create') {
const stackName = event.StackId.split('/')[1];
physicalResourceId = `algosec-management-notification-${stackName}-${event.LogicalResourceId}`;
}
Purpose: Generates a unique physical resource ID for the AlgosecManagementNotificationInvoke custom CloudFormation resource on creation, based on the stack name and logical resource ID.
15. CloudFormation Response
await response.send(event, context, response.SUCCESS, {message: `RequestType: ${event.RequestType}, physicalResourceId: ${physicalResourceId}`}, physicalResourceId).
Purpose: Sends success response with the physical resource ID.
16. Onboarding Management Function Lambda Configuration
Handler: index.handler
MemorySize: 128
Runtime: nodejs22.x
Timeout: 10
Role: !GetAtt OnboardingManagementLambdaRole.Arn
Purpose: Configures the Lambda function's runtime settings and environment variables.
17. Custom Resource Definition
AlgosecManagementNotificationInvoke:
Type: Custom::OnboardingManagementNotificationInvokation
Version: "1.0"
Properties:
ServiceToken: !GetAtt OnboardManagementFunction.Arn
Touch: <custom-resource>
Purpose: Defines a custom resource to invoke the Onboarding Management Lambda Function.