├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Code └── lambda_function.py ├── LICENSE ├── README.md └── Template └── cloudformation_template.yaml /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/aws-securityhub-config-integration/issues), or [recently closed](https://github.com/aws-samples/aws-securityhub-config-integration/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/aws-securityhub-config-integration/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/aws-securityhub-config-integration/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /Code/lambda_function.py: -------------------------------------------------------------------------------- 1 | """Script to integrate Config with Security Hub.""" 2 | import boto3 3 | import hashlib 4 | 5 | SECURITYHUB = boto3.client('securityhub') 6 | CONFIG = boto3.client('config') 7 | 8 | 9 | def get_description_of_rule(config_rule_name): 10 | """Gather description of config rule.""" 11 | description = "" 12 | try: 13 | response = CONFIG.describe_config_rules( 14 | ConfigRuleNames=[config_rule_name] 15 | ) 16 | if 'Description' in response['ConfigRules'][0]: 17 | description = response['ConfigRules'][0]['Description'] 18 | else: 19 | description = response['ConfigRules'][0]['ConfigRuleName'] 20 | return description 21 | except Exception as error: 22 | print("Error: ", error) 23 | raise 24 | 25 | 26 | def get_compliance_and_severity(new_status): 27 | """Return compliance status.""" 28 | status = ['FAILED', 3.0, 30] 29 | if new_status == 'COMPLIANT': 30 | status = ['PASSED', 0, 0] 31 | elif new_status == 'NOT_APPLICABLE': 32 | status = ['NOT_AVAILABLE', 0, 0] 33 | return status 34 | 35 | 36 | def map_config_findings_to_sh(event, old_recorded_time): 37 | """Create custom finding.""" 38 | new_findings = [] 39 | event_details = event['detail'] 40 | new_status = event_details['newEvaluationResult']['complianceType'] 41 | config_rule_name = event_details['configRuleName'] 42 | compliance_status = get_compliance_and_severity(new_status) 43 | description = get_description_of_rule(config_rule_name) 44 | remediation_url = (f"https://console.aws.amazon.com/config/home?region={event_details['awsRegion']}#/rules/details?configRuleName={config_rule_name}") 45 | finding_hash = hashlib.sha256(f"{event_details['configRuleARN']}-{event_details['resourceId']}".encode()).hexdigest() 46 | finding_id = (f"arn:aws:securityhub:{event_details['awsRegion']}:{event_details['awsAccountId']}:config/rules/{config_rule_name}/finding/{finding_hash}") 47 | new_findings.append({ 48 | "SchemaVersion": "2018-10-08", 49 | "Id": finding_id, 50 | "ProductArn": (f"arn:aws:securityhub:{event_details['awsRegion']}:" 51 | f"{event_details['awsAccountId']}:" 52 | f"product/{event_details['awsAccountId']}/default"), 53 | "GeneratorId": event_details['configRuleARN'], 54 | "AwsAccountId": event_details['awsAccountId'], 55 | 'ProductFields': { 56 | 'ProviderName': 'AWS Config' 57 | }, 58 | "Types": [ 59 | "Software and Configuration Checks/AWS Config Analysis" 60 | ], 61 | "CreatedAt": old_recorded_time, 62 | "UpdatedAt": (event_details['newEvaluationResult']['resultRecordedTime']), 63 | "Severity": { 64 | "Product": compliance_status[1], 65 | "Normalized": compliance_status[2], 66 | "Label": "MEDIUM" 67 | }, 68 | "Title": config_rule_name, 69 | "Description": description, 70 | 'Remediation': { 71 | 'Recommendation': { 72 | 'Text': 'For directions on how to fix this issue, see the remediation action on the rule details page in AWS Config console', 73 | 'Url': remediation_url 74 | } 75 | }, 76 | 'Resources': [ 77 | { 78 | 'Id': event_details['resourceId'], 79 | 'Type': event_details['resourceType'], 80 | 'Partition': "aws", 81 | 'Region': event_details['awsRegion'] 82 | } 83 | ], 84 | 'Compliance': {'Status': compliance_status[0]} 85 | }) 86 | 87 | if new_findings: 88 | try: 89 | response = SECURITYHUB.batch_import_findings(Findings=new_findings) 90 | if response['FailedCount'] > 0: 91 | print( 92 | "Failed to import {} findings".format( 93 | response['FailedCount'])) 94 | except Exception as error: 95 | print("Error: ", error) 96 | raise 97 | 98 | 99 | def parse_message(event): 100 | """Initialize event logic.""" 101 | event_details = event['detail'] 102 | if (event_details['messageType'] == 'ComplianceChangeNotification'): 103 | if 'oldEvaluationResult' not in event_details: 104 | old_recorded_time = (event_details['newEvaluationResult']['resultRecordedTime']) 105 | else: 106 | old_recorded_time = (event_details['oldEvaluationResult']['resultRecordedTime']) 107 | map_config_findings_to_sh(event, old_recorded_time) 108 | else: 109 | print("Other Notification") 110 | 111 | 112 | def lambda_handler(event, context): 113 | """Begin Lambda execution.""" 114 | print("Event Before Parsing: ", event) 115 | parse_message(event) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Import AWS Config Findings into AWS Security Hub 2 | 3 | This repository contains the following. 4 | 5 | - **Template** contains the cloudformation template to deploy the solution for integrating AWS Config with AWS Security Hub 6 | - **Code** contains the python lambda code file that integrates AWS Config with AWS Security Hub 7 | 8 | ## License Summary 9 | 10 | This sample code is made available under the MIT-0 license. See the LICENSE file. 11 | -------------------------------------------------------------------------------- /Template/cloudformation_template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: This CloudFormation template will automate the importing of aws config findings into aws security hub 3 | Parameters: 4 | LambdaSourceBucket: 5 | Description: S3 Bucket of the Lambda code 6 | Default: bucket-with-lambda-code 7 | Type: String 8 | LambdaSourceKey: 9 | Description: Lambda file name in the S3 Bucket 10 | Default: config-cwe-sh.zip 11 | Type: String 12 | Resources: 13 | LambdaServiceRole: 14 | Type: 'AWS::IAM::Role' 15 | Properties: 16 | RoleName: 'config-sechub-lambda-role' 17 | AssumeRolePolicyDocument: 18 | Version: 2012-10-17 19 | Statement: 20 | - Effect: Allow 21 | Principal: 22 | Service: 23 | - lambda.amazonaws.com 24 | Action: 25 | - 'sts:AssumeRole' 26 | Policies: 27 | - PolicyName: lambda-service-policy 28 | PolicyDocument: 29 | Statement: 30 | - Effect: Allow 31 | Action: 32 | - 'securityhub:BatchImportFindings' 33 | Resource: 34 | - '*' 35 | - Effect: Allow 36 | Action: 37 | - 'logs:CreateLogGroup' 38 | - 'logs:CreateLogStream' 39 | - 'logs:PutLogEvents' 40 | Resource: '*' 41 | - Effect: Allow 42 | Action: 43 | - 'config:DescribeConfigRules' 44 | Resource: '*' 45 | ConfigSecHubFunction: 46 | Type: AWS::Lambda::Function 47 | Properties: 48 | Code: 49 | S3Bucket: !Ref LambdaSourceBucket 50 | S3Key: !Ref LambdaSourceKey 51 | FunctionName : 'Config-SecHub-Lambda' 52 | Handler: 'lambda_function.lambda_handler' 53 | Role: 54 | Fn::GetAtt: 55 | - LambdaServiceRole 56 | - Arn 57 | Runtime: python3.8 58 | Timeout: 300 59 | ConfigSecHubCWRule: 60 | Type: AWS::Events::Rule 61 | Properties: 62 | Description: This CW rule integrates AWS Config Compliance events with AWS Lambda as a target 63 | Name: 'Config-Sechub-CW-Rule' 64 | EventPattern: 65 | source: 66 | - aws.config 67 | detail-type: 68 | - Config Rules Compliance Change 69 | detail: 70 | configRuleName: 71 | - anything-but: 72 | prefix: securityhub- 73 | messageType: 74 | - ComplianceChangeNotification 75 | State: ENABLED 76 | Targets: 77 | - 78 | Arn: 79 | Fn::GetAtt: 80 | - 'ConfigSecHubFunction' 81 | - 'Arn' 82 | Id: 'TargetFunctionV1' 83 | PermissionForEventsToInvokeLambda: 84 | Type: AWS::Lambda::Permission 85 | Properties: 86 | FunctionName: 87 | Ref: 'ConfigSecHubFunction' 88 | Action: 'lambda:InvokeFunction' 89 | Principal: 'events.amazonaws.com' 90 | SourceArn: 91 | Fn::GetAtt: 92 | - 'ConfigSecHubCWRule' 93 | - 'Arn' 94 | --------------------------------------------------------------------------------