From 805837853f475f3e60751631f655fd80d582f461 Mon Sep 17 00:00:00 2001 From: k-rheinheimer <114611405+k-rheinheimer@users.noreply.github.com> Date: Thu, 28 Mar 2024 04:59:00 -0400 Subject: [PATCH] feat: Aws cis foundation v3 (#689) - removed macros 3.3 and 3.4 from 2.0.0 (only major change on version) - reworked macro 3.3 config enabled all regions (was 3.5 on v2.0.0) to better align with the remediation steps below and to exclude any regions not active in the account via aws_regions.opt_in_status > Evaluate the output to ensure that all recorders have a recordingGroup object > which includes "allSupported": true. Additionally, ensure that at least one > recorder has "includeGlobalResourceTypes": true --- transformations/aws/compliance-free/README.md | 10 ++ .../aws_compliance__cis_v3_0_0_free.sql | 20 +++ .../aws/compliance-free/requirements.txt | 1 + .../aws/compliance-premium/README.md | 61 +++++++++ .../models/aws_compliance__cis_v3_0_0.sql | 122 ++++++++++++++++++ .../aws/compliance-premium/requirements.txt | 1 + .../config/config_enabled_all_regions.sql | 118 +++++++++++------ 7 files changed, 296 insertions(+), 37 deletions(-) create mode 100644 transformations/aws/compliance-free/models/aws_compliance__cis_v3_0_0_free.sql create mode 100644 transformations/aws/compliance-premium/models/aws_compliance__cis_v3_0_0.sql diff --git a/transformations/aws/compliance-free/README.md b/transformations/aws/compliance-free/README.md index 6bbeaecca..d6cb81f4d 100644 --- a/transformations/aws/compliance-free/README.md +++ b/transformations/aws/compliance-free/README.md @@ -100,6 +100,16 @@ The free version contains 10% of the full pack's checks. - ✅ `1.8`: `password_policy_min_length` - ✅ `1.9`: `password_policy_prevent_reuse` +##### `cis_v3.0.0` + +- ✅ `1.2`: `security_account_information_provided` +- ✅ `1.4`: `iam_root_user_no_access_keys` +- ✅ `1.5`: `mfa_enabled_for_root` +- ✅ `1.6`: `hardware_mfa_enabled_for_root` +- ✅ `1.7`: `iam_root_last_used` +- ✅ `1.8`: `password_policy_min_length` +- ✅ `1.9`: `password_policy_prevent_reuse` + ##### `foundational_security` - ✅ `apigateway.1`: `api_gw_execution_logging_enabled` diff --git a/transformations/aws/compliance-free/models/aws_compliance__cis_v3_0_0_free.sql b/transformations/aws/compliance-free/models/aws_compliance__cis_v3_0_0_free.sql new file mode 100644 index 000000000..b1dcd03a6 --- /dev/null +++ b/transformations/aws/compliance-free/models/aws_compliance__cis_v3_0_0_free.sql @@ -0,0 +1,20 @@ +with + aggregated as ( + ({{ security_account_information_provided('cis_v3.0.0','1.2') }}) + {{ union() }} + ({{ iam_root_user_no_access_keys('cis_v3.0.0','1.4') }}) + {{ union() }} + ({{ mfa_enabled_for_root('cis_v3.0.0','1.5') }}) + {{ union() }} + ({{ hardware_mfa_enabled_for_root('cis_v3.0.0','1.6') }}) + {{ union() }} + ({{ iam_root_last_used('cis_v3.0.0','1.7') }}) + {{ union() }} + ({{ password_policy_min_length('cis_v3.0.0','1.8') }}) + {{ union() }} + ({{ password_policy_prevent_reuse('cis_v3.0.0','1.9') }}) + ) +select + {{ gen_timestamp() }}, + aggregated.* +from aggregated diff --git a/transformations/aws/compliance-free/requirements.txt b/transformations/aws/compliance-free/requirements.txt index 2ba5742d0..a1fb7560c 100644 --- a/transformations/aws/compliance-free/requirements.txt +++ b/transformations/aws/compliance-free/requirements.txt @@ -1,3 +1,4 @@ dbt-postgres==1.7.10 dbt-bigquery==1.7.6 dbt-snowflake==1.7.2 +pyarrow >= 3.0.0 \ No newline at end of file diff --git a/transformations/aws/compliance-premium/README.md b/transformations/aws/compliance-premium/README.md index 0b5876e86..6763ae6d7 100644 --- a/transformations/aws/compliance-premium/README.md +++ b/transformations/aws/compliance-premium/README.md @@ -164,6 +164,67 @@ The premium version contains all checks. - ✅ `5.4`: `default_sg_no_access` - ✅ `5.6`: `ec2_not_imdsv2_instances` +##### `cis_v3.0.0` + +- ✅ `1.2`: `security_account_information_provided` +- ✅ `1.4`: `iam_root_user_no_access_keys` +- ✅ `1.5`: `mfa_enabled_for_root` +- ✅ `1.6`: `hardware_mfa_enabled_for_root` +- ✅ `1.7`: `iam_root_last_used` +- ✅ `1.8`: `password_policy_min_length` +- ✅ `1.9`: `password_policy_prevent_reuse` +- ✅ `1.10`: `mfa_enabled_for_console_access` +- ✅ `1.11`: `iam_user_access_keys_and_password_at_setup` +- ✅ `1.12`: `unused_creds_disabled_45_days` +- ✅ `1.13`: `users_with_two_active_access_keys` +- ✅ `1.14`: `old_access_keys` +- ✅ `1.15`: `policies_attached_to_groups_roles` +- ✅ `1.16`: `no_star` +- ✅ `1.17`: `iam_support_role` +- ✅ `1.18`: `use_iam_roles_for_instances` +- ✅ `1.19`: `iam_server_certificate_not_expired` +- ✅ `1.20`: `regions_with_no_accessanalyzers` +- ✅ `1.21`: `iam_users_are_managed_centrally` +- ✅ `1.22`: `iam_user_group_role_cloudshell_fullaccess_restricted` +- ✅ `2.1.1`: `deny_http_requests` +- ✅ `2.1.2`: `mfa_delete` +- ✅ `2.1.4`: `account_level_public_access_blocks` +- ✅ `2.2.1`: `unencrypted_ebs_volumes` +- ✅ `2.3.1`: `instances_should_have_ecnryption_at_rest_enabled` +- ✅ `2.3.2`: `redis_clusters_have_autominorversionupgrade` +- ✅ `2.3.3`: `rds_db_instances_should_prohibit_public_access` +- ✅ `2.4.1`: `unencrypted_efs_filesystems` +- ✅ `3.1`: `cloudtrail_enabled_all_regions` +- ✅ `3.2`: `log_file_validation_enabled` +- ✅ `3.3`: `config_enabled_all_regions` +- ✅ `3.4`: `bucket_access_logging` +- ✅ `3.5`: `logs_encrypted` +- ✅ `3.6`: `rotation_enabled_for_customer_key` +- ✅ `3.7`: `flow_logs_enabled_in_all_vpcs` +- ✅ `3.8`: `cloudtrail_s3_object_write_events_audit_enabled` +- ✅ `3.9`: `cloudtrail_s3_object_read_events_audit_enabled` +- ✅ `4.1`: `alarm_unauthorized_api` +- ✅ `4.2`: `alarm_console_no_mfa` +- ✅ `4.3`: `alarm_root_account` +- ✅ `4.4`: `alarm_iam_policy_change` +- ✅ `4.5`: `alarm_cloudtrail_config_changes` +- ✅ `4.6`: `alarm_console_auth_failure` +- ✅ `4.7`: `alarm_delete_customer_cmk` +- ✅ `4.8`: `alarm_s3_bucket_policy_change` +- ✅ `4.9`: `alarm_aws_config_changes` +- ✅ `4.10`: `alarm_security_group_changes` +- ✅ `4.11`: `alarm_nacl_changes` +- ✅ `4.12`: `alarm_network_gateways` +- ✅ `4.13`: `alarm_route_table_changes` +- ✅ `4.14`: `alarm_vpc_changes` +- ✅ `4.15`: `alarm_organization_changes` +- ✅ `4.16`: `securityhub_enabled` +- ✅ `5.1`: `vpc_network_acl_remote_administration` +- ✅ `5.2`: `vpc_security_group_remote_administration_ipv4` +- ✅ `5.3`: `no_broad_public_ipv6_ingress_on_port_22_3389` +- ✅ `5.4`: `default_sg_no_access` +- ✅ `5.6`: `ec2_not_imdsv2_instances` + ##### `foundational_security` - ✅ `account.1`: `security_account_information_provided` diff --git a/transformations/aws/compliance-premium/models/aws_compliance__cis_v3_0_0.sql b/transformations/aws/compliance-premium/models/aws_compliance__cis_v3_0_0.sql new file mode 100644 index 000000000..4743ef3a0 --- /dev/null +++ b/transformations/aws/compliance-premium/models/aws_compliance__cis_v3_0_0.sql @@ -0,0 +1,122 @@ +with + aggregated as ( + ({{ security_account_information_provided('cis_v3.0.0','1.2') }}) + {{ union() }} + ({{ iam_root_user_no_access_keys('cis_v3.0.0','1.4') }}) + {{ union() }} + ({{ mfa_enabled_for_root('cis_v3.0.0','1.5') }}) + {{ union() }} + ({{ hardware_mfa_enabled_for_root('cis_v3.0.0','1.6') }}) + {{ union() }} + ({{ iam_root_last_used('cis_v3.0.0','1.7') }}) + {{ union() }} + ({{ password_policy_min_length('cis_v3.0.0','1.8') }}) + {{ union() }} + ({{ password_policy_prevent_reuse('cis_v3.0.0','1.9') }}) + {{ union() }} + ({{ mfa_enabled_for_console_access('cis_v3.0.0','1.10') }}) + {{ union() }} + ({{ iam_user_access_keys_and_password_at_setup('cis_v3.0.0','1.11') }}) + {{ union() }} + ({{ unused_creds_disabled_45_days('cis_v3.0.0','1.12') }}) + {{ union() }} + ({{ users_with_two_active_access_keys('cis_v3.0.0','1.13') }}) + {{ union() }} + ({{ old_access_keys('cis_v3.0.0','1.14') }}) + {{ union() }} + ({{ policies_attached_to_groups_roles('cis_v3.0.0','1.15') }}) + {{ union() }} + ({{ no_star('cis_v3.0.0','1.16') }}) + {{ union() }} + ({{ iam_support_role('cis_v3.0.0','1.17') }}) + {{ union() }} + ({{ use_iam_roles_for_instances('cis_v3.0.0','1.18') }}) + {{ union() }} + ({{ iam_server_certificate_not_expired('cis_v3.0.0','1.19') }}) + {{ union() }} + ({{ regions_with_no_accessanalyzers('cis_v3.0.0','1.20') }}) + {{ union() }} + ({{ iam_users_are_managed_centrally('cis_v3.0.0','1.21') }}) + {{ union() }} + ({{ iam_user_group_role_cloudshell_fullaccess_restricted('cis_v3.0.0','1.22') }}) + {{ union() }} + ({{ deny_http_requests('cis_v3.0.0','2.1.1') }}) + {{ union() }} + ({{ mfa_delete('cis_v3.0.0','2.1.2') }}) + {{ union() }} + ({{ account_level_public_access_blocks('cis_v3.0.0','2.1.4') }}) + {{ union() }} + ({{ unencrypted_ebs_volumes('cis_v3.0.0','2.2.1') }}) + {{ union() }} + ({{ instances_should_have_ecnryption_at_rest_enabled('cis_v3.0.0','2.3.1') }}) + {{ union() }} + ({{ redis_clusters_have_autominorversionupgrade('cis_v3.0.0','2.3.2') }}) + {{ union() }} + ({{ rds_db_instances_should_prohibit_public_access('cis_v3.0.0','2.3.3') }}) + {{ union() }} + ({{ unencrypted_efs_filesystems('cis_v3.0.0','2.4.1') }}) + {{ union() }} + ({{ cloudtrail_enabled_all_regions('cis_v3.0.0','3.1') }}) + {{ union() }} + ({{ log_file_validation_enabled('cis_v3.0.0','3.2') }}) + {{ union() }} + ({{ config_enabled_all_regions('cis_v3.0.0','3.3') }}) + {{ union() }} + ({{ bucket_access_logging('cis_v3.0.0','3.4') }}) + {{ union() }} + ({{ logs_encrypted('cis_v3.0.0','3.5') }}) + {{ union() }} + ({{ rotation_enabled_for_customer_key('cis_v3.0.0','3.6') }}) + {{ union() }} + ({{ flow_logs_enabled_in_all_vpcs('cis_v3.0.0','3.7') }}) + {{ union() }} + ({{ cloudtrail_s3_object_write_events_audit_enabled('cis_v3.0.0','3.8') }}) + {{ union() }} + ({{ cloudtrail_s3_object_read_events_audit_enabled('cis_v3.0.0','3.9') }}) + {{ union() }} + ({{ alarm_unauthorized_api('cis_v3.0.0','4.1') }}) + {{ union() }} + ({{ alarm_console_no_mfa('cis_v3.0.0','4.2') }}) + {{ union() }} + ({{ alarm_root_account('cis_v3.0.0','4.3') }}) + {{ union() }} + ({{ alarm_iam_policy_change('cis_v3.0.0','4.4') }}) + {{ union() }} + ({{ alarm_cloudtrail_config_changes('cis_v3.0.0','4.5') }}) + {{ union() }} + ({{ alarm_console_auth_failure('cis_v3.0.0','4.6') }}) + {{ union() }} + ({{ alarm_delete_customer_cmk('cis_v3.0.0','4.7') }}) + {{ union() }} + ({{ alarm_s3_bucket_policy_change('cis_v3.0.0','4.8') }}) + {{ union() }} + ({{ alarm_aws_config_changes('cis_v3.0.0','4.9') }}) + {{ union() }} + ({{ alarm_security_group_changes('cis_v3.0.0','4.10') }}) + {{ union() }} + ({{ alarm_nacl_changes('cis_v3.0.0','4.11') }}) + {{ union() }} + ({{ alarm_network_gateways('cis_v3.0.0','4.12') }}) + {{ union() }} + ({{ alarm_route_table_changes('cis_v3.0.0','4.13') }}) + {{ union() }} + ({{ alarm_vpc_changes('cis_v3.0.0','4.14') }}) + {{ union() }} + ({{ alarm_organization_changes('cis_v3.0.0','4.15') }}) + {{ union() }} + ({{ securityhub_enabled('cis_v3.0.0','4.16') }}) + {{ union() }} + ({{ vpc_network_acl_remote_administration('cis_v3.0.0','5.1') }}) + {{ union() }} + ({{ vpc_security_group_remote_administration_ipv4('cis_v3.0.0','5.2') }}) + {{ union() }} + ({{ no_broad_public_ipv6_ingress_on_port_22_3389('cis_v3.0.0','5.3') }}) + {{ union() }} + ({{ default_sg_no_access('cis_v3.0.0','5.4') }}) + {{ union() }} + ({{ ec2_not_imdsv2_instances('cis_v3.0.0','5.6') }}) + ) +select + {{ gen_timestamp() }}, + aggregated.* +from aggregated diff --git a/transformations/aws/compliance-premium/requirements.txt b/transformations/aws/compliance-premium/requirements.txt index 2ba5742d0..a1fb7560c 100644 --- a/transformations/aws/compliance-premium/requirements.txt +++ b/transformations/aws/compliance-premium/requirements.txt @@ -1,3 +1,4 @@ dbt-postgres==1.7.10 dbt-bigquery==1.7.6 dbt-snowflake==1.7.2 +pyarrow >= 3.0.0 \ No newline at end of file diff --git a/transformations/aws/macros/config/config_enabled_all_regions.sql b/transformations/aws/macros/config/config_enabled_all_regions.sql index 17455fcb5..56ec72d49 100644 --- a/transformations/aws/macros/config/config_enabled_all_regions.sql +++ b/transformations/aws/macros/config/config_enabled_all_regions.sql @@ -3,57 +3,101 @@ {% endmacro %} {% macro snowflake__config_enabled_all_regions(framework, check_id) %} +with global_recorders as ( + select + count(*) as global_config_recorders + from + aws_config_configuration_recorders + where + recording_group:IncludeGlobalResourceTypes::BOOLEAN = TRUE + and recording_group:AllSupported::BOOLEAN = TRUE + and status_recording = TRUE + and status_last_status = 'SUCCESS' +) select - '{{framework}}' As framework, - '{{check_id}}' As check_id, + '{{framework}}' As framework, + '{{check_id}}' As check_id, 'AWS Config should be enabled' as title, - account_id, - arn as resource_id, - CASE - WHEN ((recording_group:IncludeGlobalResourceTypes::BOOLEAN != TRUE) OR (recording_group:AllSupported::BOOLEAN != TRUE) OR (status_recording != TRUE OR status_last_status != 'SUCCESS')) - THEN 'fail' - ELSE 'pass' - END AS status -FROM - aws_config_configuration_recorders + r.account_id, + r.arn as resource_id, + case + when g.global_config_recorders >= 1 + and status_recording = TRUE + and status_last_status = 'SUCCESS' then 'pass' + else 'fail' + end as status +from + global_recorders g, + aws_regions a + inner join aws_config_configuration_recorders as r on r.account_id = a.account_id + and r.region = a.region_name + where a.opt_in_status != 'not-opted-in' {% endmacro %} {% macro postgres__config_enabled_all_regions(framework, check_id) %} +with global_recorders as ( + select + count(*) as global_config_recorders + from + aws_config_configuration_recorders + where + recording_group -> 'IncludeGlobalResourceTypes' = 'true' + and recording_group -> 'AllSupported' = 'true' + and status_recording is true + and status_last_status = 'SUCCESS' +) select - '{{framework}}' as framework, - '{{check_id}}' as check_id, + '{{framework}}' As framework, + '{{check_id}}' As check_id, 'AWS Config should be enabled' as title, - account_id, - arn as resource_id, - case when - (recording_group->>'IncludeGlobalResourceTypes')::boolean IS NOT TRUE - OR (recording_group->>'AllSupported')::boolean IS NOT TRUE - OR status_recording IS NOT TRUE - OR status_last_status IS DISTINCT FROM 'SUCCESS' - then 'fail' - else 'pass' - end as status -FROM - aws_config_configuration_recorders + r.account_id, + r.arn as resource_id, + case + when g.global_config_recorders >= 1 + and status_recording is true + and status_last_status = 'SUCCESS' then 'pass' + else 'fail' + end as status +from + global_recorders g, + aws_regions a + inner join aws_config_configuration_recorders r on r.account_id = a.account_id + and r.region = a.region_name + where a.opt_in_status != 'not-opted-in' {% endmacro %} {% macro default__config_enabled_all_regions(framework, check_id) %}{% endmacro %} {% macro bigquery__config_enabled_all_regions(framework, check_id) %} +with global_recorders as ( + select + count(*) as global_config_recorders + from + {{ full_table_name("aws_config_configuration_recorders") }} + where + CAST( JSON_VALUE(recording_group.IncludeGlobalResourceTypes) AS BOOL) IS TRUE + and CAST( JSON_VALUE(recording_group.AllSupported) AS BOOL) IS TRUE + and status_recording is true + and status_last_status = 'SUCCESS' +) select + distinct '{{framework}}' as framework, '{{check_id}}' as check_id, 'AWS Config should be enabled' as title, - account_id, - arn as resource_id, - case when - CAST( JSON_VALUE(recording_group.IncludeGlobalResourceTypes) AS BOOL) IS NOT TRUE - OR CAST( JSON_VALUE(recording_group.AllSupported) AS BOOL) IS NOT TRUE - OR status_recording IS NOT TRUE - OR status_last_status IS DISTINCT FROM 'SUCCESS' - then 'fail' - else 'pass' - end as status -FROM - {{ full_table_name("aws_config_configuration_recorders") }} + r.account_id, + r.arn as resource_id, + case + when a.opt_in_status = 'not-opted-in' then '' + when g.global_config_recorders >= 1 + and status_recording is true + and status_last_status = 'SUCCESS' then 'pass' + else 'fail' + end as status +from + global_recorders g, + {{ full_table_name("aws_regions") }} a + inner join {{ full_table_name("aws_config_configuration_recorders") }} r on r.account_id = a.account_id + and r.region = a.region_name + where a.opt_in_status != 'not-opted-in' {% endmacro %} \ No newline at end of file