def get_role_permissions(self): try: policies = self.client.list_attached_role_policies(RoleName=self.arn.resource)["AttachedPolicies"] for policy in policies: self.policy_documents.append(self.get_policy_documents(policy)) except Exception: debug(self.arn.full)
def get_function(self): ''' Fetches Lambda function configuration ''' try: function = self.client.get_function(FunctionName=self.arn.resource) config = function['Configuration'] self.runtime = config['Runtime'] self.handler = config['Handler'] self.description = config['Description'] self.role = Role(config['Role'], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) self.codeURL = function['Code']['Location'] self.layers = [] if 'Layers' in config: for layer in config['Layers']: layer = self.client.get_layer_version_by_arn( Arn=layer['Arn']) self.layers.append({ 'arn': layer['LayerVersionArn'], 'description': layer['Description'], 'codeURL': layer['Content']['Location'] }) except Exception: debug(self.arn.full)
def set_writes(self, writes): try: self.writes["count"] += 1 for arn, policy in writes.items(): self.writes["items"][arn] = policy except Exception: debug(self.arn.full)
def get_caller_identity(self): """ Fetches STS Caller Identity """ try: return self.client.get_caller_identity() except Exception: debug(self.arn.full)
def get_bucket_acl(self): ''' Fetches S3 Bucket (Resource-based) Access Control List ''' try: self.acl = self.client.get_bucket_acl(Bucket=self.arn.resource) except Exception: debug(self.arn.full)
def get_bucket_encryption(self): ''' Fetches S3 Bucket encryption settings ''' try: self.encryption = self.client.get_bucket_encryption(Bucket=self.arn.resource) except Exception: debug(self.arn.full)
def get_security(self): try: self.security = Scan( self.report(), self.args ).security except Exception: debug(self.arn.full)
def get_policy(self): ''' Fetches Function (Resource-based) policy ''' try: policy = self.client.get_policy(FunctionName=self.arn.resource) self.policy = json.loads(policy['Policy']) except Exception: debug(self.arn.full)
def get_bucket_policy(self): ''' Fetches S3 Bucket (Resource-based) policy ''' try: policy = self.client.get_bucket_policy(Bucket=self.arn.resource) self.policy = json.loads(policy['Policy']) except Exception: debug(self.arn.full)
def get_topic_attributes(self): ''' Fetches SNS Topic attributes ''' try: self.policy = json.loads( self.client.get_topic_attributes( TopicArn=self.arn.full)['Attributes']['Policy']) except Exception: debug(self.arn.full)
def get_rotation_status(self): ''' Fetches automatic key rotation status ''' try: status = self.client.get_key_rotation_status( KeyId=self.arn.resource) self.rotation = status['KeyRotationEnabled'] except Exception: debug(self.arn.full)
def get_topic_attributes(self): """ Fetches SNS Topic attributes """ try: self.policy = json.loads( self.client.get_topic_attributes( TopicArn=self.arn.full)["Attributes"]["Policy"]) except Exception: debug(self.arn.full)
def run(arguments=''): args = parse_args(arguments) if args.html: HTMLReport(args.output).save() if args.verbose: print(f'HTML report saved to {args.output}/report.html') exit(0) rmtree(args.output, ignore_errors=True) Path(args.output).mkdir(parents=True, exist_ok=True) configure_log(args.output) identity = STS(f'arn:aws:sts:{args.region}', args.profile, args.keys[0], args.keys[1]).identity if args.verbose: print(header, end='\n\n') for _ in ['UserId', 'Account', 'Arn']: align(_, identity[_], orange) print('') statistics = Statistics(args.output) visibility = VisibilityReport(args.output) for arn_str in get_functions(args): try: arn = arnparse(arn_str) if args.verbose: count = '[' + f'{statistics.statistics["lambdas"]+1}'.rjust( 4, ' ') + '] ' print(f'\r{green}{count}{arn.resource}{nocolor}'.ljust( 100, ' '), end='') lmbd = Lambda(arn.full, args) statistics.parse(lmbd.report()) visibility.save(lmbd.report()) except Exception: debug(arn_str) SecurityReport(args.output).save() HTMLReport(args.output).save() if args.verbose: print('\r' + ' ' * 100, end='\r') # clear align('Lambdas', statistics.statistics["lambdas"]) align('Security', statistics.statistics["security"]["count"]) align('Triggers', statistics.statistics["triggers"]["count"]) align('Resources', statistics.statistics["resources"]["count"]) align('Layers', statistics.statistics["layers"]) align('Runtimes', len(statistics.statistics["runtimes"]["items"])) align('Regions', len(statistics.statistics["regions"]["items"])) print('') align('Report', f'{args.output}/report.html') align('Log', f'{args.output}/lambdaguard.log') print('\n')
def get_queue_attributes(self): ''' Fetches SQS Queue attributes ''' try: self.url = f'https://{self.arn.region}.queue.amazonaws.com/{self.arn.account_id}/{self.arn.resource}' policy = self.client.get_queue_attributes(QueueUrl=self.url, AttributeNames=['Policy']) if 'Attributes' in policy: self.policy = json.loads(policy['Attributes']['Policy']) except: debug(self.arn.full)
def get_queue_attributes(self): """ Fetches SQS Queue attributes """ try: self.url = f"https://{self.arn.region}.queue.amazonaws.com/{self.arn.account_id}/{self.arn.resource}" policy = self.client.get_queue_attributes(QueueUrl=self.url, AttributeNames=["Policy"]) if "Attributes" in policy: self.policy = json.loads(policy["Attributes"]["Policy"]) except Exception: debug(self.arn.full)
def describe_table(self): """ Fetches DynamoDB table metadata """ try: table = self.client.describe_table( TableName=self.arn.resource)["Table"] if "SSEDescription" in table: self.encryption = table["SSEDescription"] except Exception: debug(self.arn.full)
def describe_table(self): ''' Fetches DynamoDB table metadata ''' try: table = self.client.describe_table( TableName=self.arn.resource)['Table'] if 'SSEDescription' in table: self.encryption = table['SSEDescription'] except: debug(self.arn.full)
def get_policy(self, policy_name): ''' Fetches key policy by name ''' try: policy = json.loads( self.client.get_key_policy(KeyId=self.arn.resource, PolicyName=policy_name)['Policy']) self.policies[policy_name] = policy except Exception: debug(self.arn.full)
def get_policies(self): ''' Fetches list of applicable key policy names ''' try: paginator = self.client.get_paginator('list_key_policies') pages = paginator.paginate(KeyId=self.arn.resource) for page in pages: for policy_name in page['PolicyNames']: self.get_policy(policy_name) except Exception: debug(self.arn.full)
def get_user_permissions(self): try: policies = self.client.list_policies_granting_service_access( Arn=self.arn.full, ServiceNamespaces=[ "apigateway", "dynamodb", "kms", "lambda", "iam", "s3", "sns", "sqs", "sts" ])["PoliciesGrantingServiceAccess"] for item in policies: for policy in item["Policies"]: self.policy_documents.append( self.get_policy_documents(policy)) except Exception: debug(self.arn.full)
def get_triggers(self): """ Tracks events that trigger the Lambda function """ # Collect triggers from Event Sources try: eventSource = self.client.list_event_source_mappings( FunctionName=self.arn.resource)["EventSourceMappings"] for event in eventSource: if event["State"] != "Enabled": continue self.triggers["items"][event["EventSourceArn"]] = [ "lambda:InvokeFunction" ] # Track services self.triggers["services"].append( arnparse(event["EventSourceArn"]).service) except Exception: debug(self.arn.full) # Collect triggers from Function policy try: if self.policy: for statement in self.policy["Statement"]: if "Condition" in statement: if "ArnLike" in statement["Condition"]: if "AWS:SourceArn" in statement["Condition"][ "ArnLike"]: arn = statement["Condition"]["ArnLike"][ "AWS:SourceArn"] if type(statement["Action"]) == str: self.triggers["items"][arn] = [ statement["Action"] ] else: self.triggers["items"][arn] = statement[ "Action"] # Track services self.triggers["services"].append( arnparse(arn).service) except Exception: debug(self.arn.full) self.triggers["services"] = list(set(self.triggers["services"]))
def get_triggers(self): ''' Tracks events that trigger the Lambda function ''' # Collect triggers from Event Sources try: eventSource = self.client.list_event_source_mappings( FunctionName=self.arn.resource)['EventSourceMappings'] for event in eventSource: if event['State'] != 'Enabled': continue self.triggers['items'][event['EventSourceArn']] = [ 'lambda:InvokeFunction' ] # Track services self.triggers['services'].append( arnparse(event['EventSourceArn']).service) except Exception: debug(self.arn.full) # Collect triggers from Function policy try: if self.policy: for statement in self.policy['Statement']: if 'Condition' in statement: if 'ArnLike' in statement['Condition']: if 'AWS:SourceArn' in statement['Condition'][ 'ArnLike']: arn = statement['Condition']['ArnLike'][ 'AWS:SourceArn'] if type(statement['Action']) == str: self.triggers['items'][arn] = [ statement['Action'] ] else: self.triggers['items'][arn] = statement[ 'Action'] # Track services self.triggers['services'].append( arnparse(arn).service) except Exception: debug(self.arn.full) self.triggers['services'] = list(set(self.triggers['services']))
def get_function(self): """ Fetches Lambda function configuration """ try: if self.identity.acl.allowed("lambda:GetFunction"): function = self.client.get_function( FunctionName=self.arn.resource) config = function["Configuration"] self.codeURL = function["Code"]["Location"] elif self.identity.acl.allowed("lambda:GetFunctionConfiguration"): config = self.client.get_function_configuration( FunctionName=self.arn.resource) self.codeURL = "" else: exit( "\nMissing both lambda:GetFunction and lambda:GetFunctionConfiguration" ) self.runtime = config["Runtime"] self.handler = config["Handler"] self.description = config["Description"] if "KMSKeyArn" in config: self.kms = KMS( config["KMSKeyArn"], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) self.role = Role( config["Role"], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) self.layers = [] if "Layers" in config: for layer in config["Layers"]: layer = self.client.get_layer_version_by_arn( Arn=layer["Arn"]) self.layers.append({ "arn": layer["LayerVersionArn"], "description": layer["Description"], "codeURL": layer["Content"]["Location"], }) except Exception: debug(self.arn.full)
def get_usage(args): ''' Returns Python dict with number of Lambdas per region ''' usage = {} for region in get_regions(args): args.region = region verbose(args, f'Loading regions ({region})') try: client = get_client(args) settings = client.get_account_settings() function_count = settings['AccountUsage']['FunctionCount'] if function_count: usage[region] = function_count except Exception: debug(region) return usage
def get_usage(args): """ Returns Python dict with number of Lambdas per region """ usage = {} for region in get_regions(args): args.region = region verbose(args, f"Loading regions ({region})") try: client = get_client(args) settings = client.get_account_settings() function_count = settings["AccountUsage"]["FunctionCount"] if function_count: usage[region] = function_count except Exception: debug(region) return usage
def get_function(self): ''' Fetches Lambda function configuration ''' try: function = self.client.get_function(FunctionName=self.arn.resource) config = function['Configuration'] self.runtime = config['Runtime'] self.handler = config['Handler'] self.description = config['Description'] self.role = Role(config['Role'], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) self.codeURL = function['Code']['Location'] except: debug(self.arn.full)
def get_caller_identity(self): ''' Fetches STS Caller Identity ''' try: self.identity = self.client.get_caller_identity() except Exception: exit(print(debug(self.arn.full)))
def test_debug(self): configure_log("/tmp") # Called without an exception to handle self.assertEqual(debug(), None) # Logging critical errors try: 1 / 0 except Exception: trace = debug().strip().split("\n") etype, evalue = trace[-1].split(": ", 1) self.assertEqual(etype, "ZeroDivisionError") self.assertEqual(evalue, "division by zero") elog = Path(self.logpath).read_text().strip() elog.endswith("ZeroDivisionError: division by zero")
def get_function(self): ''' Fetches Lambda function configuration ''' try: if self.identity.acl.allowed("lambda:GetFunction"): function = self.client.get_function( FunctionName=self.arn.resource) config = function['Configuration'] self.codeURL = function['Code']['Location'] elif self.identity.acl.allowed("lambda:GetFunctionConfiguration"): config = self.client.get_function_configuration( FunctionName=self.arn.resource) self.codeURL = '' else: exit( "\nMissing both lambda:GetFunction and lambda:GetFunctionConfiguration" ) self.runtime = config['Runtime'] self.handler = config['Handler'] self.description = config['Description'] if 'KMSKeyArn' in config: self.kms = KMS(config['KMSKeyArn'], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) self.role = Role(config['Role'], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) self.layers = [] if 'Layers' in config: for layer in config['Layers']: layer = self.client.get_layer_version_by_arn( Arn=layer['Arn']) self.layers.append({ 'arn': layer['LayerVersionArn'], 'description': layer['Description'], 'codeURL': layer['Content']['Location'] }) except Exception: debug(self.arn.full)
def __init__(self, report, *args, **kwargs): self.report = report self.args = args[0] self.profile = args[0].profile self.access_key_id = args[0].keys[0] self.secret_access_key = args[0].keys[1] self.security = {'count': {}, 'items': []} self.item = None # item currently scanned if self.args.sonarqube: try: self.sonarqube = SonarQube(self.args.sonarqube, self.args.output) except Exception: debug('Invalid SonarQube configuration') self.args.sonarqube = None # disable SonarQube self.scan()