def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 1.5 - 1.11: incorrect custom action selection') APPLOGGER.add_message('CIS 1.5 - 1.11: incorrect custom action selection') return if (cis_data['ruleid'] not in ['1.5', '1.6', '1.7', '1.8', '1.9', '1.10', '1.11']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 1.5 - 1.11: incorrect custom action selection') APPLOGGER.add_message('CIS 1.5 - 1.11: incorrect custom action selection') return #========================================================================== message['AffectedObject'] = AFFECTED_OBJECT try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) iam = sess.client('iam') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) try: response = iam.update_account_password_policy( MinimumPasswordLength=14, RequireSymbols=True, RequireNumbers=True, RequireUppercaseCharacters=True, RequireLowercaseCharacters=True, AllowUsersToChangePassword=True, MaxPasswordAge=90, PasswordReusePrevention=24, HardExpiry=True ) LOGGER.debug(response) message['State'] = 'RESOLVED' message['Note'] = '' # use default notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) return except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 1.3 - 1.4: incorrect custom action selection') APPLOGGER.add_message( 'CIS 1.3 - 1.4: incorrect custom action selection') return if (cis_data['ruleid'] not in ['1.3', '1.4']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 1.3 - 1.4: incorrect custom action selection') APPLOGGER.add_message( 'CIS 1.3 - 1.4: incorrect custom action selection') return resource_type = str(finding.details['Resources'][0]['Type']) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # Import boto3 clients try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) iam = sess.client('iam') iam_resource = sess.resource('iam') except Exception as e: LOGGER.error(e) failed() return try: non_rotated_key_user = str(finding.details['Resources'][0]['Id'])[31:] except KeyError as key: LOGGER.error('Could not find ' + str(key) + ' in Resources data for the finding') failed() return except Exception as e: LOGGER.error(e) return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) try: todays_date_time = datetime.datetime.now(datetime.timezone.utc) paginator = iam.get_paginator('list_access_keys') for response in paginator.paginate(UserName=non_rotated_key_user): for key_metadata in response['AccessKeyMetadata']: access_key_id = str(key_metadata['AccessKeyId']) key_age_finder = todays_date_time - datetime.datetime.fromisoformat( str(key_metadata['CreateDate'])) if key_age_finder <= datetime.timedelta(days=90): LOGGER.debug("Access key: " + access_key_id + "is compliant.") # APPLOGGER.add_message("Access key: " + access_key_id + "is compliant.") else: message[ 'Note'] = "Access key over 90 days old found: " + access_key_id message['State'] = 'INFO' message[ 'AffectedObject'] = AFFECTED_OBJECT + ': ' + access_key_id notify(finding, message, LOGGER, cwlogs=APPLOGGER, sechub=False) access_key = iam_resource.AccessKey( non_rotated_key_user, access_key_id) # deactivate the key access_key.deactivate() get_key_status = iam.list_access_keys( UserName=non_rotated_key_user, MaxItems=20) for keys in get_key_status['AccessKeyMetadata']: this_access_key_id = str(keys['AccessKeyId']) this_key_status = str(keys['Status']) # find the key Id that matches the exposed key if this_access_key_id == access_key_id and this_key_status == 'Inactive': message['State'] = 'RESOLVED' message[ 'Note'] = 'Remediation completed successfully, create new access keys using IAM console.' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sechub=True, sns=AWS) except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'AffectedObject': AFFECTED_OBJECT, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.9: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.9: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.9']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.9: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.9: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]['Type']) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific security group, once the specific security group errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug( 'for finding resource type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # Grab non-logged VPC ID from Security Hub finding try: noncompliantVPC = str( finding.details['Resources'][0]['Id']).split(':')[5].split('/')[1] message[ 'AffectedObject'] = AFFECTED_OBJECT + ' for VPC: ' + noncompliantVPC except Exception as e: message['Note'] = str(e) + ' - Finding format is not as expected.' message['State'] = 'FAILED' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return lambdaFunctionSeshToken = os.getenv('AWS_SESSION_TOKEN', '') # Get Flow Logs Role ARN from env vars DeliverLogsPermissionArn = 'arn:aws:iam::' + finding.account_id + ':role/SO0111_CIS29_remediationRole' # Import boto3 clients try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) cwl = sess.client('logs') ec2 = sess.client('ec2') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # set dynamic variable for CW Log Group for VPC Flow Logs vpcFlowLogGroup = "VPCFlowLogs/" + noncompliantVPC + lambdaFunctionSeshToken[ 0:32] # create cloudwatch log group try: create_log_grp = cwl.create_log_group(logGroupName=vpcFlowLogGroup) except Exception as e: LOGGER.error(e) failed() return # wait for CWL creation to propagate time.sleep(3) # create VPC Flow Logging try: enableFlowlogs = ec2.create_flow_logs( DryRun=False, DeliverLogsPermissionArn=DeliverLogsPermissionArn, LogGroupName=vpcFlowLogGroup, ResourceIds=[noncompliantVPC], ResourceType='VPC', TrafficType='REJECT', LogDestinationType='cloud-watch-logs') LOGGER.debug(enableFlowlogs) except Exception as e: failed() LOGGER.error(e) return # wait for Flow Log creation to propogate time.sleep(2) # searches for flow log status, filtered on unique CW Log Group created earlier try: confirmFlowlogs = ec2.describe_flow_logs(DryRun=False, Filters=[ { 'Name': 'log-group-name', 'Values': [vpcFlowLogGroup] }, ]) LOGGER.debug(confirmFlowlogs) flowStatus = str(confirmFlowlogs['FlowLogs'][0]['FlowLogStatus']) if flowStatus == 'ACTIVE': message[ 'Note'] = '\"' + REMEDIATION + '\" remediation was successful' message['State'] = 'RESOLVED' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) else: failed() LOGGER.error(e) return except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.3: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.3: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.3']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.3: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.3: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]["Type"]) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # Parse ARN of non-compliant resource from Security Hub CWE try: raw_bucket_info = str(finding.details['Resources'][0]['Id']) # Remove ARN string, create new variable noncompliant_bucket = remove_arn_prefix(raw_bucket_info) except Exception as e: message['Note'] = str(e) + ' - Finding format is not as expected.' message['State'] = 'FAILED' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + noncompliant_bucket # Connect to APIs try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) s3 = sess.client('s3') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) try: remove_public = s3.put_public_access_block( Bucket=noncompliant_bucket, PublicAccessBlockConfiguration={ 'BlockPublicAcls': True, 'IgnorePublicAcls': True, 'BlockPublicPolicy': True, 'RestrictPublicBuckets': True }) LOGGER.debug(remove_public) # Do no use default wording: this lambda calls SSM to do the remediation. # All we know is whether it was invoked, not whether it was successful message[ 'Note'] = '\"' + REMEDIATION + '\" remediation was successfully completed' message['State'] = 'RESOLVED' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 4.1 - 4.2: incorrect custom action selection') APPLOGGER.add_message( 'CIS 4.1 - 4.2: incorrect custom action selection') return if (cis_data['ruleid'] not in ['4.1', '4.2']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 4.1 - 4.2: incorrect custom action selection') APPLOGGER.add_message( 'CIS 4.1 - 4.2: incorrect custom action selection') return #========================================================================== # parse Security Group ID from Security Hub CWE sg_type = str(finding.details['Resources'][0]['Type']) if sg_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific security group, once the specific security group errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug( 'for security group finding type AwsAccount, there is no resolution.' ) APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return try: non_compliant_sg = str( finding.details['Resources'][0]['Details'][sg_type]['GroupId']) except Exception as e: message['Note'] = str(e) + ' - Finding format is not as expected.' message['State'] = 'FAILED' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + non_compliant_sg #import boto3 clients try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) ssm = sess.client('ssm') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) try: response = ssm.start_automation_execution( # Launch SSM Doc via Automation DocumentName='AWS-DisablePublicAccessForSecurityGroup', DocumentVersion='1', Parameters={ 'GroupId': [non_compliant_sg], 'AutomationAssumeRole': [ 'arn:aws:iam::' + finding.account_id + ':role/' + LAMBDA_ROLE ] }) LOGGER.debug(response) message[ 'Note'] = '\"' + REMEDIATION + '\" remediation was successfully invoked via AWS Systems Manager' message['State'] = 'RESOLVED' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'AffectedObject': AFFECTED_OBJECT, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.4: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.4: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.4']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.4: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.4: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]["Type"]) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message('AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # parse non-compliant trail from Security Hub finding try: non_compliant_trail = str(finding.details['Resources'][0]['Id']).split(':')[5].split('/')[1] except KeyError as key: LOGGER.error('Could not find ' + str(key) + ' in Resources data for the finding') return except Exception as e: LOGGER.error(e) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + non_compliant_trail # Set name for Cloudwatch logs group cloudwatchLogGroup = 'CloudTrail/CIS2-4-' + non_compliant_trail # CloudTrail to CloudWatch logging IAM Role on for each account cloudtrailLoggingArn = 'arn:' + AWS_PARTITION + ':iam::' + \ finding.account_id + ':role/SO0111_CIS24_remediationRole_' + AWS_REGION # Connect to APIs try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) cwl = sess.client('logs') cloudtrail = sess.client('cloudtrail') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # create cloudwatch log group try: createGroup = cwl.create_log_group( logGroupName=cloudwatchLogGroup, ) print(createGroup) except Exception as e: LOGGER.error(e) failed() return # wait for CWL group to propagate time.sleep(2) # get CWL ARN try: describeGroup = cwl.describe_log_groups(logGroupNamePrefix=cloudwatchLogGroup) cloudwatchArn = str(describeGroup['logGroups'][0]['arn']) except Exception as e: LOGGER.error(e) message['State'] = 'FAILED' message['Note'] = '' # use default notify(finding, message, LOGGER, cwlogs=APPLOGGER) return # update non-compliant Trail try: updateCloudtrail = cloudtrail.update_trail( Name=non_compliant_trail, CloudWatchLogsLogGroupArn=cloudwatchArn, CloudWatchLogsRoleArn=cloudtrailLoggingArn ) LOGGER.debug(updateCloudtrail) message['State'] = 'RESOLVED' message['Note'] = '' # use default notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.8: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.8: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.8']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.8: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.8: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]["Type"]) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # Parse ARN of non-compliant resource from Security Hub CWE try: noncompliantCMK = str(finding.details['Resources'][0]['Id']) formattedCMK = noncompliantCMK.replace("AWS::KMS::Key:", "") except Exception as e: message['Note'] = str(e) + ' - Finding format is not as expected.' message['State'] = 'FAILED' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + formattedCMK try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) kms = sess.client('kms') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Rotate KMS Key try: rotate = kms.enable_key_rotation(KeyId=formattedCMK) time.sleep(3) except Exception as e: failed() return try: confirmRotate = kms.get_key_rotation_status(KeyId=formattedCMK) rotationStatus = str(confirmRotate['KeyRotationEnabled']) if rotationStatus == 'True': message['Note'] = '' message['State'] = 'RESOLVED' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) else: failed() except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.6: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.6: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.6']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.6: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.6: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]["Type"]) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # Parse ARN of non-compliant resource from Security Hub CWE try: ctBucket = str(finding.details['Resources'][0]['Id']) # Remove ARN string, create new variable formattedCTBucket = resource_from_arn(ctBucket) except Exception as e: message['Note'] = str(e) + ' - Finding format is not as expected.' message['State'] = 'FAILED' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + formattedCTBucket try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) ssm = sess.client('ssm') s3 = sess.client('s3') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Create a bucket for the access logs # The same bucket is used to log access for all CloudTrails in the same account accessLoggingBucket = LOGGING_BUCKET_PREFIX + "-" + finding.account_id + "-" + AWS_REGION accessLoggingBucket = accessLoggingBucket.lower() try: kwargs = { 'Bucket': accessLoggingBucket, 'GrantWrite': 'uri=http://acs.amazonaws.com/groups/s3/LogDelivery', 'GrantReadACP': 'uri=http://acs.amazonaws.com/groups/s3/LogDelivery' } if AWS_REGION != 'us-east-1': kwargs['CreateBucketConfiguration'] = { 'LocationConstraint': AWS_REGION } s3.create_bucket(**kwargs) s3.put_bucket_encryption(Bucket=accessLoggingBucket, ServerSideEncryptionConfiguration={ 'Rules': [{ 'ApplyServerSideEncryptionByDefault': { 'SSEAlgorithm': 'AES256' } }] }) except botocore.exceptions.ClientError as error: if error.response['Error']['Code'] == 'BucketAlreadyExists': pass elif error.response['Error']['Code'] == 'BucketAlreadyOwnedByYou': pass else: LOGGER.error(error) failed() return except Exception as e: LOGGER.error(e) failed() return # execute automation with ConfigureS3BucketLogging Document try: response = ssm.start_automation_execution( DocumentName='AWS-ConfigureS3BucketLogging', DocumentVersion='1', Parameters={ 'BucketName': [formattedCTBucket], 'GrantedPermission': ['READ'], 'GranteeType': ['Group'], 'GranteeUri': ['http://acs.amazonaws.com/groups/s3/LogDelivery'], ## Must Use URI, fails with Canonical Group Id 'TargetPrefix' : [formattedCTBucket + '/'], 'TargetBucket': [accessLoggingBucket], 'AutomationAssumeRole': ['arn:' + AWS_PARTITION + ':iam::' + \ finding.account_id + ':role/' + LAMBDA_ROLE] } ) LOGGER.debug(response) message[ 'Note'] = '\"' + REMEDIATION + '\" remediation was successfully invoked via AWS Systems Manager' message['State'] = 'RESOLVED' notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) except Exception as e: LOGGER.error(e) failed() return
def remediate(finding, metrics_data): message = { 'Note': '', 'State': 'INFO', 'Account': finding.account_id, 'Remediation': REMEDIATION, 'metrics_data': metrics_data } def failed(): """ Send Failed status message """ message['State'] = 'FAILED' message['Note'] = '' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # Make sure it matches - custom action can be initiated for any finding. # Ignore if the finding selected and the playbook do not match cis_data = finding.is_cis_ruleset() if not cis_data: # Not an applicable finding - does not match ruleset # send an error and exit LOGGER.debug('CIS 2.2: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.2: incorrect custom action selection.') return if (cis_data['ruleid'] not in ['2.2']): # Not an applicable finding - does not match rule # send an error and exit LOGGER.debug('CIS 2.2: incorrect custom action selection.') APPLOGGER.add_message('CIS 2.2: incorrect custom action selection.') return resource_type = str(finding.details['Resources'][0]["Type"]) if resource_type == 'AwsAccount': # This code snippet is invoked when the user selects a finding with type as AwsAccount # this finding in security hub is more referring to the account in general and doesn't provide # information of the specific remediation, once the specific Resource Type errors are resolved # this finding will be resolved as well, therefore there is no specific remediation for this finding. LOGGER.debug('for finding type AwsAccount, there is no resolution.') APPLOGGER.add_message( 'AwsAccount is a general finding for the entire account. Once the specific findings are resolved for resource type(s) other than AwsAccount, \ this will be marked as resolved.') message['State'] = 'INITIAL' message['Note'] = 'The finding is related to the AWS account.' notify(finding, message, LOGGER, cwlogs=APPLOGGER) return #========================================================================== # parse non-compliant trail from Security Hub finding try: non_compliant_trail = str( finding.details['Resources'][0]['Id']).split(':')[5].split('/')[1] except KeyError as key: LOGGER.error('Could not find ' + str(key) + ' in Resources data for the finding') return except Exception as e: LOGGER.error(e) return message['AffectedObject'] = AFFECTED_OBJECT + ': ' + non_compliant_trail # Connect to APIs try: sess = BotoSession(finding.account_id, LAMBDA_ROLE) cloudtrail = sess.client('cloudtrail') except Exception as e: LOGGER.error(e) failed() return # Mark the finding NOTIFIED while we remediate message['State'] = 'INITIAL' notify(finding, message, LOGGER, cwlogs=APPLOGGER) # turn on cloudtrail log file validation try: response = cloudtrail.update_trail(Name=non_compliant_trail, EnableLogFileValidation=True) LOGGER.debug(response) message['State'] = 'RESOLVED' message['Note'] = '' # use default notify(finding, message, LOGGER, cwlogs=APPLOGGER, sns=AWS) except Exception as e: LOGGER.error(e) failed() return