├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── acm_dns_validation_check.guard ├── all_resources_reserved_tag_check.guard ├── all_resources_supplementaryConfiguration_tag_check.guard ├── all_resources_tagCheck_withparam.guard ├── appsync_api_xray_enabled.guard ├── cfn_failed_status.guard ├── check_alb_listener_policy.guard ├── check_alb_tls10.guard ├── check_application_layer_ddos_mitigation_enabled.guard ├── check_instance_placement_group.guard ├── check_instance_profile.guard ├── check_nacl_unrestricted_inbound.guard ├── check_nlb_listener_policy.guard ├── cloudfront_check_httpversion.guard ├── cloudfront_tls_latest_check.guard ├── cloudwatch_alarm_action_check.guard ├── deprecated_cfn_check.guard ├── deprecated_cfn_template_usage.guard ├── dynamodb_customer_managed_key.guard ├── dynamodb_streams_enabled.guard ├── ec2_instance_monitoring_enabled.guard ├── ec2_instance_multiple_enis.guard ├── ec2_src_dest_check.guard ├── ec2_subnet_Ipaddress_availabilityCheck.guard ├── ecr_public_repository.guard ├── ecs_task_definitions_latest_image.guard ├── efs_backup_check.guard ├── efs_encryption_check.guard ├── efs_lifecycle_check.guard ├── eip_amazon_owned.guard ├── elb_listner_non_std_port.guard ├── elb_logging_to_s3_bucket.guard ├── iam_role_deprecated_managed_policy.guard ├── iam_root_mfa_disabled_check.guard ├── lambda_deprecated_runtime.guard ├── lambda_latest_runtime.guard ├── opensearch_security.guard ├── rds_iam_authentication_enabled.guard ├── rds_version_check.guard ├── s3_bucket_kms_keys.guard ├── s3_versioning_enabled_check.guard ├── secrets_manager_secret_rotation_enabled_check.guard ├── secrets_manager_secret_rotation_schedule_days_check.guard ├── secrets_manager_tag_check.guard ├── security_group_internet_check_approval.guard ├── security_group_launch_wizard_check.guard ├── securitygroup_egress_allportsallow_check.guard ├── sns_encryption.guard ├── sqs_queues_not_encrypted.guard ├── sqs_queues_not_kms_encrypted.guard ├── tag_space_check.guard ├── transitGatewayAttachment_stno_tagCheck.guard ├── vpc_endpoint_gateway_in_use.guard ├── vpc_endpoint_interface_in_use.guard └── vpc_endpoint_not_in_use_check.guard /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /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, or recently closed, 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 *main* 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' 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](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 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 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AWS Config custom policy rule samples 2 | 3 | This repository contains a collection of sample custom policy rules for AWS Config. The samples are designed to educate AWS customers on how to build custom AWS Config rules written using Guard which is a domain specific language (DSL). They are organized by various resources for AWS Config to return compliance in line with the rule definition. We will continue to add more rules to this repository as they are developed and verified. 4 | 5 | ## Security 6 | 7 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 8 | 9 | ## License 10 | 11 | This library is licensed under the MIT-0 License. See the LICENSE file. 12 | 13 | -------------------------------------------------------------------------------- /acm_dns_validation_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks that Amazon issued ACM certificates have DNS based domain validation set 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when all certificate domains have DNS based validation 5 | # b) NONCOMPLIANT if any certificate domain does not have DNS based validation 6 | 7 | rule check_acm_certificate_domain_validation { 8 | when configuration.type == "AMAZON_ISSUED" { 9 | configuration.domainValidationOptions[*] { 10 | validationMethod == "DNS" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /all_resources_reserved_tag_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if Reserved Tags are only added on Resources created by SecurityAutomation 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a resource is using the reserved tag and is created by SecurityAutomation 5 | # b) NONCOMPLIANT when a resource is using reserved tags and not created by SecurityAutomation or missing the CreatedBy Tag 6 | # c) NOTAPPLICABLE if a resource is not using reserves tag 7 | 8 | let reserved_nametags = ['SEC_APPROVAL_ID','SEC_EXCEPTION_ID'] 9 | # Resource CreatedBy Tag 10 | let tags_created_by_key = "CreatedBy" 11 | # Resource created by SecurityAutomation 12 | let tags_created_by_value = "SecurityAutomation" 13 | 14 | rule checkreservedtags 15 | when tags.%tags_created_by_key empty or 16 | tags.%tags_created_by_key != %tags_created_by_value 17 | { 18 | configuration !empty 19 | configuration.tags.*.key !in %reserved_nametags 20 | } 21 | rule checkreservedtags_forcreator 22 | when tags.%tags_created_by_key == 23 | %tags_created_by_value 24 | { 25 | tags.* in %reserved_nametags or tags.* !in 26 | %reserved_nametags 27 | } -------------------------------------------------------------------------------- /all_resources_supplementaryConfiguration_tag_check.guard: -------------------------------------------------------------------------------- 1 | # Rule Intent: Check if a resource has a specific resource tag key/value pair in the supplementaryConfiguration. 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when the resource has a matching resource tag key/value pair 5 | # b) NONCOMPLIANT when the resource tag key/value pair does not match the parameters or if resource has no tags 6 | 7 | let tags = supplementaryConfiguration.Tags 8 | let keyParam = CONFIG_RULE_PARAMETERS.keyParam 9 | let valueParam = CONFIG_RULE_PARAMETERS.valueParam 10 | rule compliant { 11 | %tags exists 12 | %tags !empty 13 | let key = %tags.*[key == %keyParam] 14 | %key !empty 15 | when %key !empty { 16 | %key.value == %valueParam 17 | } 18 | } -------------------------------------------------------------------------------- /all_resources_tagCheck_withparam.guard: -------------------------------------------------------------------------------- 1 | # this example shows two key value pair parameters provided 2 | # each pair uses a rule block 3 | # replace the 3 variable with the tag key and value names for each block 4 | rule no_tag_check when CONFIG_RULE_PARAMETERS !empty { 5 | tags !empty 6 | } 7 | rule tag1_check when no_tag_check { 8 | let tagkey = CONFIG_RULE_PARAMETERS.tag1key 9 | let tagvalue = CONFIG_RULE_PARAMETERS.tag1value 10 | when %tagkey !empty { 11 | tags.%tagkey !empty 12 | when %tagvalue !empty tags.%tagkey !empty{ 13 | tags.%tagkey == %tagvalue 14 | } 15 | } 16 | } 17 | rule tag2_check when no_tag_check { 18 | let tagkey = CONFIG_RULE_PARAMETERS.tag2key 19 | let tagvalue = CONFIG_RULE_PARAMETERS.tag2value 20 | when %tagkey !empty { 21 | tags.%tagkey !empty 22 | when %tagvalue !empty tags.%tagkey !empty { 23 | tags.%tagkey == %tagvalue 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /appsync_api_xray_enabled.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if AWS AppSyn GraphQL API Xray is enabled 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if AWS AppSyn GraphQL API Xray is enabled 5 | # 2) NOCOMPLIANT if AWS AppSyn GraphQL API Xray is disabled 6 | # 3) NOTAPPLICABLE if there is no AWS AppSyn GraphQL API 7 | 8 | rule check_appsync_xray_compliance { 9 | configuration.XrayEnabled == true 10 | <> 11 | } -------------------------------------------------------------------------------- /cfn_failed_status.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if any Clouformation Stacks are in failed state. 3 | # 4 | # Expectations: 5 | # a) COMPLIANT when CFN stacks status does not end with FAILED state. 6 | # b) NONCOMPLIANT when CFN stack status is in FAILED state. 7 | # 8 | 9 | rule failed_stacks { 10 | configuration.stackStatus != /_FAILED$/ 11 | << Stacks with *_FAILED status should be deleted >> 12 | } 13 | 14 | -------------------------------------------------------------------------------- /check_alb_listener_policy.guard: -------------------------------------------------------------------------------- 1 | # Rule Intent: Checks if an ALB is configured with the SSL policy specified in parameter. Works only for 2 | ALB 3 | # Parameter: predefinedSslPolicy 4 | # 5 | # Expectations: 6 | # a) COMPLIANT if the ALB listener ssl policy is EQUAL to predefinedSslPolicy parameter value 7 | # b) NON_COMPLIANT if the ALB listener ssl policy is ANYTHING OTHER THAN predefinedSslPolicy parameter 8 | value 9 | # c) NOT_APPLICABLE if the ALB listener protocol is not HTTPS. This should skip both NLB and Gateway Load 10 | Balancer as well 11 | 12 | let alb_listener_ssl_policy = configuration.SslPolicy 13 | let alb_listener_protocol = configuration.Protocol 14 | rule alb_listener_ssl_policy_check{ 15 | when %alb_listener_protocol == 'HTTPS'{ 16 | %alb_listener_ssl_policy == CONFIG_RULE_PARAMETERS.predefinedSslPolicy << 17 | result: NON_COMPLIANT 18 | message: The ALB listener's policy does not match the desired policy. 19 | >> 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /check_alb_tls10.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rules checks if any ALB listeners use TLS protocol version 1.0 2 | # Expectations: 3 | # a) COMPLIANT if an SSL listener and SSL policy IS NOT 'ELBSecurityPolicy-TLS-1-0-2015-04' 4 | # b) NOT COMPLIANT if an SSL listener and SSL policy IS 'ELBSecurityPolicy-TLS-1-0-2015-04' 5 | # c) NOT_APPLICABLE if the ALB listener protocol is not HTTPS 6 | rule check_if_tls10 when configuration.Protocol == 'HTTPS' { 7 | configuration.SslPolicy != 'ELBSecurityPolicy-TLS-1-0-2015-04' << 8 | result: NON_COMPLIANT 9 | message: Application Load Balancer Listener is using TLS protocol version 1.0 10 | >> 11 | } 12 | -------------------------------------------------------------------------------- /check_application_layer_ddos_mitigation_enabled.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent: Rule checks if a resource eligible for automatic application layer DDoS protection is protected by Shield Advanced 2 | # 3 | # Expectations: 4 | # a) COMPLIANT if an application layer DDoS protection eligible resource 5 | has automatic application layer protection enabled. 6 | # b) NON_COMPLIANT if an application layer DDoS protection eligible 7 | resource does not have automatic application layer protection enabled. 8 | # c) NOT_APPLICABLE if the Shield protected resource is not eligible for 9 | automatic application layer protection. 10 | 11 | #First, confirm the protection being evaluated is for a resource type that 12 | is eligible for application layer DDOS mitigation. 13 | let protectedresourcetype = relationships.*[ 14 | resourceType in [ 'AWS::CloudFront::Distribution', 15 | 'AWS::ElasticLoadBalancingV2::LoadBalancer' ] 16 | ] 17 | 18 | # Then, Check whether application layer DDOS mitigation is enabled for the 19 | protection currently being evaluated. 20 | rule L7automitigationenabled { 21 | when %protectedresourcetype !empty { 22 | configuration.ApplicationLayerAutomaticResponseConfig.Status == 23 | "ENABLED" << 24 | result: NON_COMPLIANT 25 | message: This resource does not have automatic application layer protection enabled. 26 | >> 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /check_instance_placement_group.guard: -------------------------------------------------------------------------------- 1 | # Rule Intent: Checks if EC2 instances belong to the given Placement Group. 2 | # Expectations: 3 | # a) COMPLIANT when an instance belongs to the given Placement group 4 | # b) NONCOMPLIANT when an instance does not belong to the given Placement group 5 | 6 | rule checkPlcmntGrp { 7 | configuration.placement.groupName == CONFIG_RULE_PARAMETERS.groupName << 8 | result: NON_COMPLIANT 9 | message: The Instance does not belong to the given Placement group. 10 | >> 11 | } 12 | -------------------------------------------------------------------------------- /check_instance_profile.guard: -------------------------------------------------------------------------------- 1 | # Rule Intent: Rule checks if EC2 Instances have an IAM Instance Profile attached 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when an EC2 instance has an IAM Instance Profile attached 5 | # b) NONCOMPLIANT when an EC2 instance does not have an IAM Instance Profile attached 6 | 7 | rule check_ec2_instance_profile { 8 | when configuration.iamInstanceProfile exists { 9 | configuration.iamInstanceProfile.arn not empty << 10 | result: NON_COMPLIANT 11 | message: The Instance does not have an IAM Instance profiled attached 12 | >> 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /check_nacl_unrestricted_inbound.guard: -------------------------------------------------------------------------------- 1 | # Rule intent: Rule checks if Network ACLs contain rules allowing unrestricted inbound traffic. 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a Network ACL does not contain inbound rule(s) allowing unrestricted inbound traffic 5 | # b) NON_COMPLIANT when a Network ACL contains an inbound rule(s) allowing unrestricted inbound traffic 6 | 7 | rule check_nacl_allow_unrestricted_inbound { 8 | let unrestricted_inbound_rules = configuration.entries[ 9 | egress == false 10 | ruleAction == "allow" 11 | protocol == "-1" 12 | cidrBlock == "0.0.0.0/0" OR cidrBlock == "::/0" 13 | ] 14 | %unrestricted_inbound_rules empty << 15 | result: NON_COMPLIANT 16 | message: The Network ACL contains an inbound rule(s) allowing traffic from 0.0.0.0/0 or ::/0. 17 | >> 18 | } 19 | -------------------------------------------------------------------------------- /check_nlb_listener_policy.guard: -------------------------------------------------------------------------------- 1 | # Rule Intent: Rules checks if an NLB is configured with the SSL policy specified in parameter. Works only 2 | for NLB 3 | # Parameter: predefinedSslPolicy 4 | # 5 | # Expectations: 6 | # b) COMPLIANT if an NLB listener TLS policy is EQUAL to predefinedSslPolicy parameter value 7 | # c) NON_COMPLIANT if an NLB listener TLS policy is ANYTHING OTHER THAN predefinedSslPolicy parameter value 8 | # a) NOT_APPLICABLE if an NLB listener's protocol is not TLS. This should skip both ALB and Gateway Load 9 | Balancer as well 10 | 11 | # CONFIG_RULE_PARAMETERS.predefinedSslPolicy 12 | let nlb_listener_ssl_policy = configuration.SslPolicy 13 | let nlb_listener_protocol = configuration.Protocol 14 | rule nlb_listener_ssl_policy_check{ 15 | when %nlb_listener_protocol == 'TLS'{ 16 | %nlb_listener_ssl_policy == 17 | CONFIG_RULE_PARAMETERS.predefinedSslPolicy << 18 | result: NON_COMPLIANT 19 | message: The NLB listener's policy does not match the desired policy. 20 | >> 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cloudfront_check_httpversion.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if http versions enabled on a Cloudfront Distribution, by default it checks for http2 or http3 or http2and3. Can be further parameterized to enforce a particular version. 3 | 4 | # 5 | # Expectations: 6 | # a) COMPLIANT when 7 | # input parameter allowedHttpVersion is not provided and if Cloudfront distribution`s httpVersion is enabled witn http2 or http3 or http2and3 ; 8 | # inputallowedHttpVersion is provided and if httpVersion Cloudfront distribution`s httpVersion matches the input parameter allowedHttpVersion 9 | 10 | # b) NONCOMPLIANT when 11 | # input parameter allowedHttpVersion is not provided and if Cloudfront distribution`s httpVersion is not http2 or http3 or http2and3 ; 12 | 13 | # input parameter allowedHttpVersion is provided and if httpVersion is doesn’t match with allowedHttpVersion 14 | 15 | 16 | rule cloudfront_http_versions_enabled when resourceType == "AWS::CloudFront::Distribution" { 17 | 18 | # match the version to input parameter if version is provided as input 19 | when CONFIG_RULE_PARAMETERS.allowedHttpVersion !empty { 20 | configuration.distributionConfig.httpVersion == CONFIG_RULE_PARAMETERS.allowedHttpVersion 21 | } 22 | 23 | # check http2, 3 or both by default 24 | when CONFIG_RULE_PARAMETERS.allowedHttpVersion empty { 25 | configuration.distributionConfig.httpVersion in ["http2","http3","http2and3"] 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /cloudfront_tls_latest_check.guard: -------------------------------------------------------------------------------- 1 | # rule-intent : Check if any CloudFront distributions support TLS protocol version 1.0/SSLv3 2 | # Expectations: 3 | # a) COMPLIANT if a CloudFront distribution DOES NOT use any of the following security policies 4 | # c) NOT COMPLIANT if a CloudFront distribution USES any of the following security policies 5 | # List of policies: TLSv1_2016, TLSv1, SSLv3 6 | 7 | rule check_if_tls10_or_sslv3 { 8 | configuration.distributionConfig.viewerCertificate.minimumProtocolVersion !in ['TLSv1_2016', 'TLSv1', 'SSLv3'] << 9 | Cloudfront distribution supports TLS protocol version 1.0/SSLv3 10 | >> 11 | } 12 | -------------------------------------------------------------------------------- /cloudwatch_alarm_action_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if CloudWatch Alarms have an associated action 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a CloudWatch Alarm has actions associated with it 5 | # b) NONCOMPLIANT if there are no actions associated with it 6 | 7 | 8 | rule check_alarm_without_actions when resourceType == "AWS::CloudWatch::Alarm" { 9 | configuration.alarmActions !empty 10 | << 11 | No actions were found for 12 | alarm 13 | >> 14 | } -------------------------------------------------------------------------------- /deprecated_cfn_check.guard: -------------------------------------------------------------------------------- 1 | # rule-intent : Check if any CloudFront distributions support TLS protocol version 1.0/SSLv3 2 | # Expectations: 3 | # a) COMPLIANT if a CloudFront distribution DOES NOT use any of the following security policies 4 | # c) NOT COMPLIANT if a CloudFront distribution USES any of the following security policies 5 | rule DEPRECATED_TEMPLATES_NOT_USED when resourceType == "AWS::CloudFormation::Stack" { 6 | configuration.description != /(?i)deprecated/ 7 | } 8 | -------------------------------------------------------------------------------- /deprecated_cfn_template_usage.guard: -------------------------------------------------------------------------------- 1 | rule DEPRECATED_TEMPLATES_NOT_USED 2 | when resourceType =="AWS::CloudFormation::Stack" 3 | { 4 | configuration.description != /(?i)deprecated/ 5 | } -------------------------------------------------------------------------------- /dynamodb_customer_managed_key.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if DynamoDB tables are encrypted with AWS managed key or Customer managed key 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a DynamoDB table is encrypted with AWS managed key or Customer managed key 5 | # b) NONCOMPLIANT if the DynamoDB table is encrypted AWS owned key (default) 6 | 7 | 8 | rule check_sse_enabled when resourceType == "AWS::DynamoDB::Table"{ 9 | configuration.ssedescription.status == "ENABLED" 10 | } -------------------------------------------------------------------------------- /dynamodb_streams_enabled.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if dynamodb streams is enabled for the table 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when dynamodb streams is enabled for the table 5 | # b) NONCOMPLIANT when dynamodb streams is not enabled for the table 6 | # c) NOTAPPLICABLE if resource is any other than DynamoDB 7 | 8 | rule check_ddb_streams_enabled when resourceType == "AWS::DynamoDB::Table"{ 9 | configuration.streamSpecification.streamEnabled == true 10 | } -------------------------------------------------------------------------------- /ec2_instance_monitoring_enabled.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if detailed monitoring is enabled on EC2 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if EC2 detailed monitoring is enabled 5 | # 2) NOCOMPLIANT if EC2 detailed monitoring is disabled 6 | # 3) NOTAPPLICABLE if there is no EC2 instance 7 | 8 | rule checkEC2InstanceDetailedMonitoringEnabled { 9 | configuration.monitoring.state == 'enabled' 10 | <> 11 | } -------------------------------------------------------------------------------- /ec2_instance_multiple_enis.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if any EC2 instance is configured ENIs in different subnets. 3 | # 4 | # Expectations: 5 | # a) COMPLIANT when all ENIs attached to EC2 instance are in the same subnet 6 | # b) NONCOMPLIANT when ENIs attached to EC2 instance are in different subnets 7 | # 8 | 9 | let eniOne = configuration.networkInterfaces[0] 10 | rule check_eni_subnets { 11 | configuration.networkInterfaces[*] { 12 | subnetId == %eniOne.subnetId 13 | << Dual Homed EC2 are a security risk >> 14 | } 15 | } -------------------------------------------------------------------------------- /ec2_src_dest_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Guard rule to check if the source/destination check is disabled 2 | # 3 | # Expectations: 4 | # a) COMPLIANT if the source/destination check is turned off 5 | # b) NONCOMPLIANT if the source/destination check is turned on 6 | 7 | rule checkSrcDestChk when resourceType == "AWS::EC2::Instance" { 8 | configuration.sourceDestCheck != false 9 | << 10 | result: NON_COMPLIANT 11 | message: Source/destination check is NOT turned off. 12 | >> 13 | } -------------------------------------------------------------------------------- /ec2_subnet_Ipaddress_availabilityCheck.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks Check if subnets are running out of ipaddresses - flag when approx 5% are left 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when there are enough free ips 5 | # b) NONCOMPLIANT when number of free ips is < 5% of subnet size 6 | # c) NOTAPPLICABLE when subnet mask does not match 7 | 8 | rule checker16 when configuration.cidrBlock == /\/16/ { 9 | configuration.availableIpAddressCount >= 3276 10 | } 11 | rule checker17 when configuration.cidrBlock == /\/17/ { 12 | configuration.availableIpAddressCount >= 1638 13 | } 14 | rule checker18 when configuration.cidrBlock == /\/18/ { 15 | configuration.availableIpAddressCount >= 818 16 | } 17 | rule checker19 when configuration.cidrBlock == /\/19/ { 18 | configuration.availableIpAddressCount >= 409 19 | } 20 | rule checker20 when configuration.cidrBlock == /\/20/ { 21 | configuration.availableIpAddressCount >= 204 22 | } 23 | rule checker21 when configuration.cidrBlock == /\/21/ { 24 | configuration.availableIpAddressCount >= 102 25 | } 26 | rule checker22 when configuration.cidrBlock == /\/22/ { 27 | configuration.availableIpAddressCount >= 51 28 | } 29 | rule checker23 when configuration.cidrBlock == /\/23/ { 30 | configuration.availableIpAddressCount >= 26 31 | } 32 | rule checker24 when configuration.cidrBlock == /\/24/ { 33 | configuration.availableIpAddressCount >= 13 34 | } 35 | rule checker25 when configuration.cidrBlock == /\/25/ { 36 | configuration.availableIpAddressCount >= 6 37 | } 38 | rule checker26 when configuration.cidrBlock == /\/26/ { 39 | configuration.availableIpAddressCount >= 3 40 | } 41 | rule checker27 when configuration.cidrBlock == /\/27/ { 42 | configuration.availableIpAddressCount >= 2 43 | } 44 | rule checker28 when configuration.cidrBlock == /\/28/ { 45 | configuration.availableIpAddressCount >= 1 46 | } -------------------------------------------------------------------------------- /ecr_public_repository.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if ECR has any public repository 3 | # 4 | # Expectations: 5 | # a) NONCOMPLIANT when Public repository is detected in ECR 6 | # b) NOTAPPLICABLE when no Public repositoryrepository is detected in ECR 7 | # 8 | 9 | rule check_ecr_public_repository { 10 | configuration.RepositoryName empty 11 | << Detected public ECR repository >> 12 | } -------------------------------------------------------------------------------- /ecs_task_definitions_latest_image.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if any ECS task definitions are running on ':latest' # version of the container images. 3 | # 4 | # Expectations: 5 | # a) NONCOMPLIANT when the Image version is not "latest" 6 | # b) NOTAPPLICABLE when the Image version is "latest" 7 | 8 | rule check_container_image { 9 | configuration.ContainerDefinitions[*] { 10 | Image != /:latest$/ 11 | << Image must be of specific version >> 12 | } 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /efs_backup_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if backup is enabled for the EFS file systems 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when backup is enabled for a EFS file system 5 | # b) NONCOMPLIANT if backup is not enabled 6 | 7 | rule EFS_RESOURCES_PROTECTED_BY_BACKUP_PLAN when resourceType == "AWS::EFS::FileSystem" { 8 | configuration.BackupPolicy.Status == "ENABLED" 9 | } -------------------------------------------------------------------------------- /efs_encryption_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if encryption is enabled for the EFS file systems 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when encryption is enabled for a EFS file system 5 | # b) NONCOMPLIANT if encryption is not enabled 6 | 7 | rule efs_encrypted_check when resourceType == "AWS::EFS::FileSystem" { 8 | configuration.Encrypted == true 9 | } -------------------------------------------------------------------------------- /efs_lifecycle_check.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if EFS Lifecycle Management is turned on 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if Amazon EFS Lifecycle management is turned off 5 | # 2) NOCOMPLIANT if Amazon EFS Lifecycle management is turned on 6 | # 3) NOTAPPLICABLE if there is no Amazon EFS volume 7 | 8 | rule checkEfsLifeCycleMgmt when 9 | resourceType == "AWS::EFS::FileSystem" { 10 | configuration.LifeCyclePolicies !empty 11 | <> 12 | } -------------------------------------------------------------------------------- /eip_amazon_owned.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if EIP is owned by Amazon 2 | # 3 | # Expectations: 4 | # a) COMPLIANT if EIP belongs to Amazon 5 | # b) NONCOMPLIANT if EIP does not belongs to Amazon 6 | 7 | rule check_amazon_owned_eip { 8 | when configuration exists { 9 | when configuration.publicIpv4Pool exists { 10 | configuration.publicIpv4Pool == "amazon" 11 | <> 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /elb_listner_non_std_port.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if ELB listner with Protocol as HTTP or HTTPS 2 | # is configured with a non-standard (HTTP -> 80, HTTPS ->443) port. 3 | # 4 | # 5 | # Expectations: 6 | # a) SKIP when Protocol for listner is other than HTTP and HTTPS 7 | # b) PASS when Protocol (HTTP/HTTPS) match standard ports (80/443). 8 | # c) FAIL when Protocol (HTTP/HTTPS) does not match standard ports (80/443). 9 | 10 | 11 | rule check_http_port when configuration.Protocol == 'HTTP' 12 | { 13 | configuration.Port == 80 14 | << HTTP protocol must be exposed on standard port 80 >> 15 | } 16 | 17 | rule check_https_port when configuration.Protocol =='HTTPS' 18 | { 19 | configuration.Port == 443 20 | << HTTPS protocol must be exposed on standard port 443 >> 21 | } 22 | 23 | -------------------------------------------------------------------------------- /elb_logging_to_s3_bucket.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if ALB/NLB has logging enabled with s3 bucket 3 | # 4 | # Expectations: 5 | # a) COMPLIANT when ALB/NLB has logging enabled with s3 bucket 6 | # b) NONCOMPLIANT ALB/NLB has not logging enabled or with no s3 bucket as target 7 | # 8 | 9 | rule ELB_LOGGING_ENABLED when 10 | resourceType == "AWS::ElasticLoadBalancingV2::LoadBalancer" { 11 | let accessLogBucket = 12 | supplementaryConfiguration.LoadBalancerAttributes[ key == 13 | "access_logs.s3.bucket" ] 14 | %accessLogBucket.value == CONFIG_RULE_PARAMETERS.targetBucket 15 | } 16 | -------------------------------------------------------------------------------- /iam_role_deprecated_managed_policy.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Validate that an IAM role does not have any deprecated policies attached 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when no deprecated managed policy is found in a role 5 | # b) NONCOMPLIANT if there are any depricated policy is found in a role 6 | 7 | rule check_attached { 8 | when configuration.attachedManagedPolicies !empty { 9 | configuration.attachedManagedPolicies.*.policyArn !in 10 | ['arn:aws:iam::aws:policy/AWSCodePipelineFullAccess', 11 | 'arn:aws:iam::aws:policy/AWSCodePipelineReadOnlyAccess', 12 | 'arn:aws:iam::aws:policy/AWSElasticBeanstalkFullAccess', 13 | 'arn:aws:iam::aws:policy/AWSElasticBeanstalkReadOnlyAccess', 14 | 'arn:aws:iam::aws:policy/AWSLambdaFullAccess', 15 | 'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccesswithDataPipeline', 16 | 'arn:aws:iam::aws:policy/AmazonEC2ContainerServiceAutoscaleRole', 17 | 'arn:aws:iam::aws:policy/AmazonEC2ContainerServiceFullAccess', 18 | 'arn:aws:iam::aws:policy/AmazonElasticMapReduceFullAccess', 19 | 'arn:aws:iam::aws:policy/service-role/AWSConfigRole', 20 | 'arn:aws:iam::aws:policy/service-role/AWSDataPipelineRole', 21 | 'arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkService', 22 | 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole', 23 | 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM', 24 | 'arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole'] 25 | << 26 | Deprecated policy is attached to this role 27 | >> 28 | } 29 | } -------------------------------------------------------------------------------- /iam_root_mfa_disabled_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule to check if Root Account MFA is disabled but MFA for IAM User Console Access is enabled. 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when there are IAM users with MFA enabled and root account has no MFA enabled 5 | # b) NONCOMPLIANT if there are no IAM users with MFA enabled 6 | # c) NOTAPPLICABLE if there are MFA for IAM users and root account enabled 7 | 8 | let iam_users_and_root = Resources.*[Type == 'AWS::Config::ConfigRule'] 9 | 10 | rule check_mfa_enabled_for_iam_console_access when %iam_users_and_root!empty { 11 | %iam_users_and_root { 12 | Properties.Source.SourceIdentifier[ * ] == "MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS" 13 | } 14 | } 15 | 16 | rule check_mfa_enabled_for_iam_console_access_except_root when check_mfa_enabled_for_iam_console_access { 17 | %iam_users_and_root { 18 | Properties.Source.SourceIdentifier[*] != "ROOT_ACCOUNT_MFA_ENABLED" 19 | } 20 | } -------------------------------------------------------------------------------- /lambda_deprecated_runtime.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Guard rule to check that deprecated or soon to be deprecated Lambda function runtimes are used 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when deprecated or soon to be deprecated Lambda function runtimes are not used 5 | # b) NONCOMPLIANT when deprecated or soon to be deprecated Lambda function runtimes are used 6 | 7 | rule ALLOWED_LAMBDA_RUNTIMES_CHECK when resourceType == "AWS::Lambda::Function" { 8 | configuration.runtime != "python3.6" 9 | configuration.runtime != "python2.7" 10 | configuration.runtime != "dotnetcore2.1" 11 | configuration.runtime != "dotnetcore3.1" 12 | configuration.runtime != "ruby2.5" 13 | configuration.runtime != "nodejs12.x" 14 | configuration.runtime != "nodejs10.x" 15 | configuration.runtime != "nodejs8.10" 16 | configuration.runtime != "nodejs6.10" 17 | configuration.runtime != "nodejs4.3" 18 | configuration.runtime != "dotnetcore1.0" 19 | configuration.runtime != "dotnetcore2.0" 20 | configuration.runtime != "nodejs4.3-edge" 21 | configuration.runtime != "nodejs" 22 | } -------------------------------------------------------------------------------- /lambda_latest_runtime.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if Lambda fucntions have the latest runtime version 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when Lambda function is using the latest runtime 5 | # b) NONCOMPLIANT when Lambda function is not using the latest runtime 6 | 7 | let aws_lambda_function_resources = resourceType.* 8 | 9 | rule check_runtime_is_latest_version when %aws_lambda_function_resources !empty { 10 | configuration.runtime == /nodejs16.x|python3.9|java11|dotnet6|go1.x|ruby2.7/ 11 | } -------------------------------------------------------------------------------- /opensearch_security.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if OpenSearch Domains follow security best practice 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a OpenSearch doamin passes all the security check 5 | # b) NONCOMPLIANT if at least one of the rule failed 6 | 7 | rule node2node_encrypted when resourceType == "AWS::OpenSearch::Domain" { 8 | configuration.NodeToNodeEncryptionOptions.Enabled == true 9 | } 10 | 11 | rule advancedSecurityOptions when resourceType == "AWS::OpenSearch::Domain" { 12 | configuration.AdvancedSecurityOptions.Enabled == true 13 | } 14 | rule EncryptionAtRestOptions when resourceType == "AWS::OpenSearch::Domain" { 15 | configuration.EncryptionAtRestOptions.KmsKeyId exists 16 | configuration.EncryptionAtRestOptions.Enabled == true 17 | } 18 | rule DomainEndPointOptions when resourceType == "AWS::OpenSearch::Domain" { 19 | configuration.DomainEndpointOptions.EnforceHTTPS == true 20 | configuration.DomainEndpointOptions.TLSSecurityPolicy == "Policy-Min-TLS-1-2-2019-07" 21 | } 22 | rule AuditLogsEnabled when resourceType == "AWS::OpenSearch::Domain" { 23 | configuration.LogPublishingOptions.AUDIT_LOGS exists 24 | configuration.LogPublishingOptions.AUDIT_LOGS.Enabled == true 25 | configuration.LogPublishingOptions.AUDIT_LOGS.CloudWatchLogsLogGroupArn exists 26 | } 27 | 28 | rule nonPublicDomain when resourceType == "AWS::OpenSearch::Domain" { 29 | configuration.VPCOptions exists 30 | } -------------------------------------------------------------------------------- /rds_iam_authentication_enabled.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Check if IAM authentication is enabled for "AWS::RDS::DBCluster" and "AWS::RDS::DBInstance" 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when IAM Authentication is enabled for RDS DBCluster or RDS DBInstance 5 | # b) NONCOMPLIANT when IAM Authentication is not enabled for RDS DBCluster or RDS DBInstance 6 | 7 | let iamAuthenticationCluster = configuration.iamdatabaseAuthenticationEnabled 8 | let iamAuthenticationInstance = configuration.iAMDatabaseAuthenticationEnabled 9 | 10 | ##Check whether IAM authencitation is enabled for the "AWS::RDS::DBCluster" resource type. 11 | rule rds_cluster_iam_authentication_enabled 12 | when resourceType == "AWS::RDS::DBCluster" { 13 | when %iamAuthenticationCluster !empty { 14 | %iamAuthenticationCluster == true << database cluster does not have IAM authentication enabled >> 15 | } 16 | } 17 | 18 | ##Check whether IAM authentication is enabled for the "AWS::RDS::DBInstance" resource type. 19 | rule rds_instance_iam_authentication_enabled 20 | when resourceType == "AWS::RDS::DBInstance" { 21 | when %iamAuthenticationInstance !empty { 22 | %iamAuthenticationInstance == true << database instance does not have IAM authentication enabled >> 23 | } 24 | } -------------------------------------------------------------------------------- /rds_version_check.guard: -------------------------------------------------------------------------------- 1 | # Rule intent: This rule checks if RDS database instances and clusters are running approved minimum versions 2 | # for various database engines including MySQL, PostgreSQL, Oracle, SQL Server, 3 | # MariaDB, and Aurora variants. It requires the following parameters: mysql_version, postgres_version, oracle_version, sqlserver_version, mariadb_version, aurora_mysql_version, aurora_postgres_version. 4 | # 5 | # Parameters: 6 | # This rule has a parameter per DB engine. Each DB engine will have its own minimum version. 7 | # 8 | # mysql_version (example value 8.0.39) 9 | # postgres_version (example value 16.4) 10 | # oracle_version (example value 19.0.0) 11 | # sqlserver_version (example value 15.00.4198.2) 12 | # mariadb_version (example value 11.4.4) 13 | # aurora_mysql_version (example value 8.0.mysql_aurora.3.05.2) 14 | # aurora_postgres_version (example value 16.5) 15 | # 16 | # Expectations: 17 | # a) COMPLIANT when a DB engine version is greater or equal to the version value in the parameters. 18 | # b) NON_COMPLIANT when a DB engine version isn't greater or equal to the version value in the parameters. 19 | 20 | rule RDS_VERSION_CHECK_INSTANCE when 21 | resourceType == "AWS::RDS::DBInstance" { 22 | let engineType = this.configuration.engine 23 | let engineVersion = this.configuration.engineVersion 24 | 25 | when %engineType == "mysql" { 26 | %engineVersion >= CONFIG_RULE_PARAMETERS.mysql_version 27 | } 28 | when %engineType == "postgres" { 29 | %engineVersion >= CONFIG_RULE_PARAMETERS.postgres_version 30 | } 31 | when %engineType == "oracle" { 32 | %engineVersion >= CONFIG_RULE_PARAMETERS.oracle_version 33 | } 34 | when engineType == "sqlserver" { 35 | engineVersion >= CONFIG_RULE_PARAMETERS.sqlserver_version 36 | } 37 | when %engineType == "mariadb" { 38 | %engineVersion >= CONFIG_RULE_PARAMETERS.mariadb_version 39 | } 40 | when %engineType == "aurora-mysql" { 41 | %engineVersion >= CONFIG_RULE_PARAMETERS.aurora_mysql_version 42 | } 43 | when %engineType == "aurora-postgresql" { 44 | %engineVersion >= CONFIG_RULE_PARAMETERS.aurora_postgres_version 45 | } 46 | } 47 | 48 | rule RDS_VERSION_CHECK_CLUSTER when 49 | resourceType == "AWS::RDS::DBCluster" { 50 | let engineType = this.configuration.engine 51 | let engineVersion = this.configuration.engineVersion 52 | 53 | when %engineType == "aurora-mysql" { 54 | %engineVersion >= CONFIG_RULE_PARAMETERS.aurora_mysql_version 55 | } 56 | when %engineType == "aurora-postgresql" { 57 | %engineVersion >= CONFIG_RULE_PARAMETERS.aurora_postgres_version 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /s3_bucket_kms_keys.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks to see if KMS encrypted buckets are using bucket keys 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when bucket has KMS encryption and has bucket keys enabled 5 | # b) NONCOMPLIANT when bucket has KMS encryption and does not have bucket keys enabled 6 | # c) NOTAPPLICABLE when no encryption or SSE-S3 encryption is used 7 | 8 | 9 | rule check_bucket_keys 10 | when supplementaryConfiguration.ServerSideEncryptionConfiguration.rules.0.applyServerSideEncryptionByDefault.sseAlgorithm == "aws:kms" 11 | { supplementaryConfiguration.ServerSideEncryptionConfiguration.rules.0.bucketKeyEnabled == true 12 | } -------------------------------------------------------------------------------- /s3_versioning_enabled_check.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if versioning enabled for Amazon S3 bucket 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if S3 bucket versioning is enabled 5 | # 2) NOCOMPLIANT if S3 bucket versioning is disabled 6 | # 3) NOTAPPLICABLE if there is no S3 bucket 7 | 8 | rule checkBucketVersioningEnabled { 9 | supplementaryConfiguration.BucketVersioningConfiguration.status == "Enabled" 10 | <> 11 | } -------------------------------------------------------------------------------- /secrets_manager_secret_rotation_enabled_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if a secret created in AWS Secrets Manager has secret rotation enabled 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a secret created in AWS Secrets Manager has secret rotation enabled 5 | # b) NONCOMPLIANT when a secret created in AWS Secrets Manager does not have secret rotation enabled 6 | 7 | rule check_secretsmanager_secret_rotation_enabled { 8 | configuration.RotationEnabled exists 9 | configuration.RotationEnabled == true 10 | <> 11 | } -------------------------------------------------------------------------------- /secrets_manager_secret_rotation_schedule_days_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if the scheduled rotation of a secret created in AWS Secrets Manager is less than or equal to 30 days 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a scheduled rotation of a secret created in AWS Secrets Manager is less than or equal to 30 days 5 | # b) NONCOMPLIANT when a scheduled rotation of a secret created in AWS Secrets Manager is not less than or equal to 30 days 6 | 7 | rule check_secretsmanager_secret_scheduled_rotation_date_is_equal_or_less_than_thirty 8 | { 9 | configuration.RotationEnabled == true <> 11 | configuration.RotationRules.AutomaticallyAfterDays <= 30 <> 13 | } -------------------------------------------------------------------------------- /secrets_manager_tag_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if a secret created in AWS Secrets Manager has an associated tag 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a secret created in AWS Secrets Manager has an associated tag 5 | # b) NONCOMPLIANT when a secret created in AWS Secrets Manager does not have an associated tag 6 | 7 | rule check_secretsmanager_secret_has_at_least_one_tag { 8 | tags !empty <> 9 | } -------------------------------------------------------------------------------- /security_group_internet_check_approval.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if Security Group allows internet access without security approval 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if Security Group allows internet access and has 'SecurityApproval' resource tag and its value set 5 | # 2) NOCOMPLIANT if Security Group allows internet access and does not have 'SecurityApproval' resource tag and value 6 | # 3) NOTAPPLICABLE if Security Group does not allow internet access 7 | 8 | # CIDR for internet access 9 | let blocked_range = "0.0.0.0/0" 10 | 11 | # Resource Tag name 12 | let tags_security_approval_key = "SecurityApproval" 13 | 14 | rule checkcompliance when 15 | resourceType == "AWS::EC2::SecurityGroup" 16 | { 17 | when configuration.ipPermissions.* !empty 18 | { 19 | let ip_permissions - configuration.ipPermissions 20 | when %ip_permissions.*.ipRanges == $blocked_range 21 | { 22 | tags.%tags_security_approval_key !empty 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /security_group_launch_wizard_check.guard: -------------------------------------------------------------------------------- 1 | #Rule-intent: Rule checks if Security Group name contains 'launch-wizard' in the name 2 | # 3 | # Expectations: 4 | # 1) COMPLIANT if Security Group name does not contain 'launch-wizard' in the name 5 | # 2) NOCOMPLIANT if Security Group name starts with'launch-wizard' in the name 6 | # 3) NOTAPPLICABLE if there is no Security Group resource 7 | 8 | rule compliancecheck when resourceType == "AWS::EC2::SecurityGroup" { 9 | resourceName != /launch-wizard-(?i)/ 10 | } -------------------------------------------------------------------------------- /securitygroup_egress_allportsallow_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks egress rule of security group to identify rules which are allowing All TCP or UDP port for all IPv4 in one rule. 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when egress rule does not allow all ports for all ipv4 addresses 5 | # b) NONCOMPLIANT when egress rule allows all ports for all ipv4 addresses 6 | # c) NOTAPPLICABLE if egress rule does not have rules for TCP or UDP 7 | 8 | let targets = configuration.ipPermissionsEgress[ 9 | ipProtocol == 'tcp' or 10 | ipProtocol == 'udp' ] 11 | 12 | rule check_sg_egress_rule_all_tcp_or_udp_port_allowed_for_all_ipv4_in_one_rule 13 | when %targets !empty { 14 | %targets[*]{ 15 | ipProtocol == "tcp" or 16 | ipProtocol == "udp" 17 | when ipv4Ranges !empty{ 18 | ipv4Ranges[*]{ 19 | cidrIp == /(?i)0.0.0.0/ 20 | } 21 | } 22 | when fromPort is_string{ 23 | fromPort == "-1" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /sns_encryption.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if SNS Topics have encryption enabled 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when a SNS Topic has associated encryption enabled 5 | # b) NONCOMPLIANT if a SNS Topic does not have encryption enabled 6 | 7 | rule check_sns_topic_encrypted { 8 | configuration.KmsMasterKeyId exists 9 | <> 10 | } -------------------------------------------------------------------------------- /sqs_queues_not_encrypted.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if SQS Queues are encrypted or not. 3 | # 4 | # Expectations: 5 | # a) COMPLIANT when SQS Queues are encrypted 6 | # b) NONCOMPLIANT when SQS Queues are not encrypted 7 | # 8 | 9 | rule check_sqs_queues_encryption { 10 | configuration. SqsManagedSseEnabled== "true" 11 | << SQS Queues are not encrypted >> 12 | } -------------------------------------------------------------------------------- /sqs_queues_not_kms_encrypted.guard: -------------------------------------------------------------------------------- 1 | 2 | # Rule-intent : Rule checks if KMS encryption is enabled on SQS Queues 3 | # 4 | # Expectations: 5 | # a) COMPLIANT when SQS Queues are enabled with KMS Key 6 | # b) NONCOMPLIANT when SQS Queues are not enabled with KMS Key 7 | # 8 | 9 | rule check_kms_encryption { 10 | configuration.KmsMasterKeyId !empty 11 | << SQS Queues are not enabled with KMS Key encryption >> 12 | } -------------------------------------------------------------------------------- /tag_space_check.guard: -------------------------------------------------------------------------------- 1 | # 2 | # Rule Intent: Check tag value to ensure there is no space at the start or end 3 | # of the value. 4 | # 5 | # Expectations: 6 | # a) SKIP when no tags are defined for that resource. 7 | # b) COMPLIANT when tag value don't have space at start or end. 8 | # c) NON COMPLIANT when a tag value has space at start or end. 9 | # 10 | 11 | rule check_tag_space when tags !empty { 12 | tags.* != /^ / << Tag value must not start with space >> 13 | tags.* != / $/ << Tag value must not end with space >> 14 | } 15 | -------------------------------------------------------------------------------- /transitGatewayAttachment_stno_tagCheck.guard: -------------------------------------------------------------------------------- 1 | # Rule intent: Checks Transit Gateway (TGW) attachments to ensure compliance with defined network policies. 2 | # Depends on Serverless Transit Network Orchestrator (STNO) v3 or above to manage TGW attachments. STNO adds the required tags used by this rule. 3 | # Rule should only be applied with resource type AWS::EC2::TransitGatewayAttachment 4 | # 5 | # Expectations: 6 | # a) COMPLIANT when All the conditions are met 7 | # b) NONCOMPLIANT when 8 | # - Attachments associated with the "Isolated" route table propagate to "Isolated" (which breaks Isolation) 9 | # - Accounts in the Sandbox OU are associated with the "Infrastructure" route table (which could allow sandbox accounts to provide shared services) 10 | # - Attachments associated with the "Infrastructure" route table does not propagate to "Isolated" (which prevents Isolated VPCs from having access to shared services) 11 | # - Attachments associated with the "Isolated" route table does not propagate to "Infrastructure" (which prevents Isolated VPCs from having access to shared services) 12 | # 13 | # c) NOTAPPLICABLE if TGW attachments do not have the required STNO tags 14 | 15 | # Variables: 16 | let sandbox_ou = 'Root/Sandbox/' 17 | let tgw_attachment_association = tags[ keys == 'Associate-with' ] 18 | let tgw_attachment_propagations = tags[ keys == 'Propagate-to' ] 19 | let tgw_attachment_ou = tags[ keys == 'account-ou' ] 20 | # Check that Isolated VPCs cannot talk to each other: 21 | rule isolated_propagation_check when %tgw_attachment_association !empty { 22 | when %tgw_attachment_propagations !empty { 23 | when %tgw_attachment_association == /(?i)^Isolated$/ { 24 | %tgw_attachment_propagations != /(?i)(\w?|^)Isolated(\w?|$)/ << VPC attachment associated with the Isolated route table also propagating to Isolated, breaking isolation>> 25 | } 26 | } 27 | } 28 | # Check that Sandbox accounts do not provide shared Infrastructure services: 29 | rule sandbox_association_check when %tgw_attachment_ou !empty { 30 | when %tgw_attachment_association !empty { 31 | when %tgw_attachment_ou == %sandbox_ou { 32 | %tgw_attachment_association != /(?i)^Infrastructure$/ << Account in Sandbox OU associated with Infrastructure route table, providing shared services >> 33 | } 34 | } 35 | } 36 | # Check that Isolated VPCs have access to shared Infrastructure services,by checking both route directions: 37 | rule isolated_infrastructure_propagation_check when 38 | %tgw_attachment_association !empty { 39 | when %tgw_attachment_propagations !empty { 40 | when %tgw_attachment_association == /(?i)^Isolated$/ { 41 | %tgw_attachment_propagations == /(?i)(\w?|^)Infrastructure(\w?|$)/ << Isolated VPC attachment does not propagate to Infrastructure route table, and does not have access to shared services >> 42 | } 43 | } 44 | } 45 | rule infrastructure_isolated_propagation_check when 46 | %tgw_attachment_association !empty { 47 | when %tgw_attachment_propagations !empty { 48 | when %tgw_attachment_association == /(?i)^Infrastructure$/ { 49 | %tgw_attachment_propagations == /(?i)(\w?|^)Isolated(\w?|$)/ << Infrastructure VPC attachment does not propagated to Isolated route table, and cannot provide shared services >> 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /vpc_endpoint_gateway_in_use.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks when VPCendpointType is Gateway and GatewayEndpoint exists, it must be connected to one or more routetables 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when VPCendpointType is Gateway and GatewayEndpoint exists, it is associated to one or more routetables 5 | # b) NONCOMPLIANT when VPCendpointType is Gateway and GatewayEndpoint exists, it is not associated to any routetableIds 6 | # c) NOTAPPLICABLE if there is no GatewayVPCendpoint 7 | 8 | let TargetvpcEndpointType = 'Gateway' 9 | 10 | rule VPCEndpointIsRtbAssociated { 11 | when configuration.vpcEndpointType == %TargetvpcEndpointType { 12 | configuration.routeTableIds !empty << VPC Gateway Endpoint is Not in use >> 13 | } 14 | } -------------------------------------------------------------------------------- /vpc_endpoint_interface_in_use.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks when VPCendpointType is Interface and InterfaceEndpoint exists, it must be connected to one or more subnets 2 | # 3 | # Expectations: 4 | # a) COMPLIANT when VPCendpointType is Interface and InterfaceEndpoint exists, it is associated to one or more subnets 5 | # b) NONCOMPLIANT when VPCendpointType is Interface and InterfaceEndpoint exists, it is not associated to any SubnetIds 6 | # c) NOTAPPLICABLE if there is no InterfaceVPCendpoint 7 | 8 | let TargetvpcEndpointType = 'Interface' 9 | 10 | rule VPCEndpointIsSubnetIdsAssociated { 11 | when configuration.vpcEndpointType == %TargetvpcEndpointType { 12 | configuration.subnetIds !empty << VPC Interface Endpoint is Not in use >> 13 | } 14 | } -------------------------------------------------------------------------------- /vpc_endpoint_not_in_use_check.guard: -------------------------------------------------------------------------------- 1 | # Rule-intent : Rule checks if there is an unused VPCEndpoint regardless of VPCendpointType 2 | # 3 | # Expectations: 4 | # a) COMPLIANT When VPCendpointType is Interface and InterfaceEndpoint exists, it must be associated to one or more subnets or When VPCendpointType is Gateway and GatewayEndpoint exists, it must be associated to one or more routetables 5 | # b) NONCOMPLIANT When VPCendpointType is Interface and InterfaceEndpoint exists, it is not associated to any SubnetIds or When VPCendpointType is Gateway and GatewayEndpoint exists, it is not associated to any routetableIds 6 | 7 | rule VPCEndpointIsRtbAssociated { 8 | when configuration.vpcEndpointType == 'Gateway' { 9 | configuration.routeTableIds !empty << VPC Gateway Endpoint is Not in use >> 10 | } 11 | } 12 | 13 | rule VPCEndpointIsSubnetIdsAssociated { 14 | when configuration.vpcEndpointType == 'Interface' { 15 | configuration.subnetIds !empty << VPC Interface Endpoint is Not in use >> 16 | } 17 | } --------------------------------------------------------------------------------