From 2c5320a0b083e38b0c253a3d27d5f2157a21e043 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Wed, 16 Nov 2022 10:21:43 +0100 Subject: [PATCH] feat(CloudFront): Service and Checks (#1470) --- providers/aws/services/cloudfront/__init__.py | 0 .../aws/services/cloudfront/check_extra714 | 42 --- .../aws/services/cloudfront/check_extra732 | 41 --- .../aws/services/cloudfront/check_extra738 | 43 --- .../aws/services/cloudfront/check_extra767 | 40 --- .../aws/services/cloudfront/check_extra773 | 42 --- .../aws/services/cloudfront/check_extra791 | 40 --- .../services/cloudfront/cloudfront_client.py | 4 + .../__init__.py | 0 ...eld_level_encryption_enabled.metadata.json | 35 ++ ...ibutions_field_level_encryption_enabled.py | 22 ++ ...ons_field_level_encryption_enabled_test.py | 110 +++++++ .../__init__.py | 0 ...ons_geo_restrictions_enabled.metadata.json | 35 ++ ..._distributions_geo_restrictions_enabled.py | 23 ++ ...ributions_geo_restrictions_enabled_test.py | 135 ++++++++ .../__init__.py | 0 ..._distributions_https_enabled.metadata.json | 35 ++ .../cloudfront_distributions_https_enabled.py | 38 +++ ...dfront_distributions_https_enabled_test.py | 148 +++++++++ .../__init__.py | 0 ...istributions_logging_enabled.metadata.json | 35 ++ ...loudfront_distributions_logging_enabled.py | 28 ++ ...ront_distributions_logging_enabled_test.py | 185 +++++++++++ .../__init__.py | 0 ...ing_deprecated_ssl_protocols.metadata.json | 35 ++ ...ibutions_using_deprecated_ssl_protocols.py | 37 +++ ...ons_using_deprecated_ssl_protocols_test.py | 306 ++++++++++++++++++ .../__init__.py | 0 ...ront_distributions_using_waf.metadata.json | 35 ++ .../cloudfront_distributions_using_waf.py | 21 ++ ...cloudfront_distributions_using_waf_test.py | 98 ++++++ .../services/cloudfront/cloudfront_service.py | 157 +++++++++ .../cloudfront/cloudfront_service_test.py | 247 ++++++++++++++ 34 files changed, 1769 insertions(+), 248 deletions(-) create mode 100644 providers/aws/services/cloudfront/__init__.py delete mode 100644 providers/aws/services/cloudfront/check_extra714 delete mode 100644 providers/aws/services/cloudfront/check_extra732 delete mode 100644 providers/aws/services/cloudfront/check_extra738 delete mode 100644 providers/aws/services/cloudfront/check_extra767 delete mode 100644 providers/aws/services/cloudfront/check_extra773 delete mode 100644 providers/aws/services/cloudfront/check_extra791 create mode 100644 providers/aws/services/cloudfront/cloudfront_client.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_waf/__init__.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.metadata.json create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.py create mode 100644 providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf_test.py create mode 100644 providers/aws/services/cloudfront/cloudfront_service.py create mode 100644 providers/aws/services/cloudfront/cloudfront_service_test.py diff --git a/providers/aws/services/cloudfront/__init__.py b/providers/aws/services/cloudfront/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/check_extra714 b/providers/aws/services/cloudfront/check_extra714 deleted file mode 100644 index 5395bb5d..00000000 --- a/providers/aws/services/cloudfront/check_extra714 +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra714="7.14" -CHECK_TITLE_extra714="[extra714] Check if CloudFront distributions have logging enabled" -CHECK_SCORED_extra714="NOT_SCORED" -CHECK_CIS_LEVEL_extra714="EXTRA" -CHECK_SEVERITY_extra714="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra714="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check714="extra714" -CHECK_SERVICENAME_extra714="cloudfront" -CHECK_RISK_extra714='If not enabled monitoring of service use is not possible.' -CHECK_REMEDIATION_extra714='Real-time monitoring can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms. Enable logging for services with defined log rotation. This logs are useful for Incident Response and forensics investigation among other use cases.' -CHECK_DOC_extra714='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html' -CHECK_CAF_EPIC_extra714='Logging and Monitoring' - -extra714(){ - # "Check if CloudFront distributions have logging enabled " - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None") - if [[ $LIST_OF_DISTRIBUTIONS ]]; then - for dist in $LIST_OF_DISTRIBUTIONS; do - LOG_ENABLED=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.Logging.Enabled' --output text | grep True) - LOG_ENABLED_REALTIME=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.DefaultCacheBehavior.RealtimeLogConfigArn' --output text | grep -i arn) - if [[ $LOG_ENABLED || $LOG_ENABLED_REALTIME ]]; then - textPass "$REGION: CloudFront distribution $dist has logging enabled" "$REGION" "$dist" - else - textFail "$REGION: CloudFront distribution $dist has logging disabled" "$REGION" "$dist" - fi - done - else - textInfo "$REGION: No CloudFront distributions found" "$REGION" "$dist" - fi -} diff --git a/providers/aws/services/cloudfront/check_extra732 b/providers/aws/services/cloudfront/check_extra732 deleted file mode 100644 index 6cbb715d..00000000 --- a/providers/aws/services/cloudfront/check_extra732 +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. - -CHECK_ID_extra732="7.32" -CHECK_TITLE_extra732="[extra732] Check if Geo restrictions are enabled in CloudFront distributions" -CHECK_SCORED_extra732="NOT_SCORED" -CHECK_CIS_LEVEL_extra732="EXTRA" -CHECK_SEVERITY_extra732="Low" -CHECK_ASFF_RESOURCE_TYPE_extra732="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check732="extra732" -CHECK_SERVICENAME_extra732="cloudfront" -CHECK_RISK_extra732='Consider countries where service should not be accessed; by legal or compliance requirements. Additionally if not restricted the attack vector is increased.' -CHECK_REMEDIATION_extra732='If possible; define and enable Geo restrictions for this service.' -CHECK_DOC_extra732='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html' -CHECK_CAF_EPIC_extra732='Infrastructure Security' - -extra732(){ - LIST_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].Id' --output text |grep -v ^None) - if [[ $LIST_DISTRIBUTIONS ]]; then - for dist in $LIST_DISTRIBUTIONS; do - GEO_ENABLED=$($AWSCLI cloudfront get-distribution-config $PROFILE_OPT --id $dist --query DistributionConfig.Restrictions.GeoRestriction.RestrictionType --output text) - if [[ $GEO_ENABLED == "none" ]]; then - textFail "$REGION: CloudFront distribution $dist has not Geo restrictions" "$REGION" "$dist" - else - textPass "$REGION: CloudFront distribution $dist has Geo restrictions enabled" "$REGION" "$dist" - fi - done - else - textInfo "$REGION: No CloudFront distributions found" - fi -} diff --git a/providers/aws/services/cloudfront/check_extra738 b/providers/aws/services/cloudfront/check_extra738 deleted file mode 100644 index 32116eba..00000000 --- a/providers/aws/services/cloudfront/check_extra738 +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra738="7.38" -CHECK_TITLE_extra738="[extra738] Check if CloudFront distributions are set to HTTPS" -CHECK_SCORED_extra738="NOT_SCORED" -CHECK_CIS_LEVEL_extra738="EXTRA" -CHECK_SEVERITY_extra738="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra738="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check738="extra738" -CHECK_ASFF_COMPLIANCE_TYPE_extra738="ens-mp.com.2.aws.front.1" -CHECK_SERVICENAME_extra738="cloudfront" -CHECK_RISK_extra738='If not enabled sensitive information in transit is not protected. Surveillance and other threats are risks may exists.' -CHECK_REMEDIATION_extra738='Use HTTPS everywhere possible. It will enforce privacy and protect against account hijacking and other threats.' -CHECK_DOC_extra738='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-https.html' -CHECK_CAF_EPIC_extra738='Data Protection' - -extra738(){ - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None) - if [[ $LIST_OF_DISTRIBUTIONS ]];then - for dist in $LIST_OF_DISTRIBUTIONS; do - CHECK_HTTPS_STATUS=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.DefaultCacheBehavior.ViewerProtocolPolicy $PROFILE_OPT --output text) - if [[ $CHECK_HTTPS_STATUS == "allow-all" ]]; then - textFail "$REGION: CloudFront distribution $dist viewers can use HTTP or HTTPS!" "$REGION" "$dist" - elif [[ $CHECK_HTTPS_STATUS == "redirect-to-https" ]]; then - textPass "$REGION: CloudFront distribution $dist has redirect to HTTPS" "$REGION" "$dist" - else - textPass "$REGION: CloudFront distribution $dist has HTTPS only" "$REGION" "$dist" - fi - done - else - textInfo "$REGION: No CloudFront distributions found" "$REGION" - fi -} diff --git a/providers/aws/services/cloudfront/check_extra767 b/providers/aws/services/cloudfront/check_extra767 deleted file mode 100644 index 77bb0de4..00000000 --- a/providers/aws/services/cloudfront/check_extra767 +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra767="7.67" -CHECK_TITLE_extra767="[extra767] Check if CloudFront distributions have Field Level Encryption enabled " -CHECK_SCORED_extra767="NOT_SCORED" -CHECK_CIS_LEVEL_extra767="EXTRA" -CHECK_SEVERITY_extra767="Low" -CHECK_ASFF_RESOURCE_TYPE_extra767="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check767="extra767" -CHECK_SERVICENAME_extra767="cloudfront" -CHECK_RISK_extra767='Allows you protect specific data throughout system processing so that only certain applications can see it.' -CHECK_REMEDIATION_extra767='Check if applicable to any sensitive data. This encryption ensures that only applications that need the data—and have the credentials to decrypt it - are able to do so.' -CHECK_DOC_extra767='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/field-level-encryption.html' -CHECK_CAF_EPIC_extra767='Data Protection' - -extra767(){ - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None) - if [[ $LIST_OF_DISTRIBUTIONS ]];then - for dist in $LIST_OF_DISTRIBUTIONS; do - CHECK_FLE=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.DefaultCacheBehavior.FieldLevelEncryptionId $PROFILE_OPT --output text) - if [[ $CHECK_FLE ]]; then - textPass "CloudFront distribution $dist has Field Level Encryption enabled" "$regx" "$dist" - else - textFail "CloudFront distribution $dist has Field Level Encryption disabled!" "$regx" "$dist" - fi - done - else - textInfo "No CloudFront distributions found" "$regx" - fi -} diff --git a/providers/aws/services/cloudfront/check_extra773 b/providers/aws/services/cloudfront/check_extra773 deleted file mode 100644 index 12f0ccdf..00000000 --- a/providers/aws/services/cloudfront/check_extra773 +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra773="7.73" -CHECK_TITLE_extra773="[extra773] Check if CloudFront distributions are using WAF " -CHECK_SCORED_extra773="NOT_SCORED" -CHECK_CIS_LEVEL_extra773="EXTRA" -CHECK_SEVERITY_extra773="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra773="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check773="extra773" -CHECK_ASFF_COMPLIANCE_TYPE_extra773="ens-mp.s.2.aws.waf.1" -CHECK_SERVICENAME_extra773="cloudfront" -CHECK_RISK_extra773='Potential attacks and / or abuse of service; more even for even for internet reachable services.' -CHECK_REMEDIATION_extra773='Use AWS WAF to protect your service from common web exploits. These could affect availability and performance; compromise security; or consume excessive resources.' -CHECK_DOC_extra773='https://docs.aws.amazon.com/waf/latest/developerguide/cloudfront-features.html' -CHECK_CAF_EPIC_extra773='Infrastructure Security' - -extra773(){ - # "Check if CloudFront distributions have logging enabled " - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None") - if [[ $LIST_OF_DISTRIBUTIONS ]]; then - for dist in $LIST_OF_DISTRIBUTIONS; do - WEB_ACL_ID=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.WebACLId' --output text) - if [[ $WEB_ACL_ID ]]; then - textPass "CloudFront distribution $dist is using AWS WAF web ACL $WEB_ACL_ID" "us-east-1" "$dist" - else - textFail "CloudFront distribution $dist is not using AWS WAF web ACL" "us-east-1" "$dist" - fi - done - else - textInfo "No CloudFront distributions found" - fi -} diff --git a/providers/aws/services/cloudfront/check_extra791 b/providers/aws/services/cloudfront/check_extra791 deleted file mode 100644 index 22f1ee83..00000000 --- a/providers/aws/services/cloudfront/check_extra791 +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra791="7.91" -CHECK_TITLE_extra791="[extra791] Check if CloudFront distributions are using deprecated SSL protocols" -CHECK_SCORED_extra791="NOT_SCORED" -CHECK_CIS_LEVEL_extra791="EXTRA" -CHECK_SEVERITY_extra791="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra791="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check791="extra791" -CHECK_SERVICENAME_extra791="cloudfront" -CHECK_RISK_extra791='Using insecure ciphers could affect privacy of in transit information.' -CHECK_REMEDIATION_extra791='Use a Security policy with a ciphers that are stronger as possible. Drop legacy and unsecure ciphers.' -CHECK_DOC_extra791='https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html' -CHECK_CAF_EPIC_extra791='Data Protection' - -extra791(){ - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions --query 'DistributionList.Items[*].Id' $PROFILE_OPT --output text|grep -v ^None) - if [[ $LIST_OF_DISTRIBUTIONS ]];then - for dist in $LIST_OF_DISTRIBUTIONS; do - CHECK_ORIGINSSLPROTOCOL_STATUS=$($AWSCLI cloudfront get-distribution --id $dist --query Distribution.DistributionConfig.Origins.Items[].CustomOriginConfig.OriginSslProtocols.Items $PROFILE_OPT --output text) - if [[ $CHECK_ORIGINSSLPROTOCOL_STATUS == *"SSLv2"* ]] || [[ $CHECK_ORIGINSSLPROTOCOL_STATUS == *"SSLv3"* ]]; then - textFail "CloudFront distribution $dist is using a deprecated SSL protocol!" "$regx" "$dist" - else - textPass "CloudFront distribution $dist is not using a deprecated SSL protocol" "$regx" "$dist" - fi - done - else - textInfo "No CloudFront distributions found" "$regx" - fi -} diff --git a/providers/aws/services/cloudfront/cloudfront_client.py b/providers/aws/services/cloudfront/cloudfront_client.py new file mode 100644 index 00000000..cba3c58e --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_client.py @@ -0,0 +1,4 @@ +from providers.aws.lib.audit_info.audit_info import current_audit_info +from providers.aws.services.cloudfront.cloudfront_service import CloudFront + +cloudfront_client = CloudFront(current_audit_info) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.metadata.json new file mode 100644 index 00000000..6dfa1824 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_field_level_encryption_enabled", + "CheckTitle": "Check if CloudFront distributions have Field Level Encryption enabled.", + "CheckType": [""], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "low", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if CloudFront distributions have Field Level Encryption enabled.", + "Risk": "Allows you protect specific data throughout system processing so that only certain applications can see it.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/field-level-encryption.html", + "Remediation": { + "Code": { + "CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/CloudFront/field-level-encryption-enabled.html", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Check if applicable to any sensitive data. This encryption ensures that only applications that need the data—and have the credentials to decrypt it - are able to do so.", + "Url": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/field-level-encryption.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "Data Protection", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.py b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.py new file mode 100644 index 00000000..8abc1378 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled.py @@ -0,0 +1,22 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client + + +class cloudfront_distributions_field_level_encryption_enabled(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + if distribution.default_cache_config.field_level_encryption_id: + report.status = "PASS" + report.status_extended = f"CloudFront Distribution {distribution.id} has Field Level Encryption enabled" + else: + report.status = "FAIL" + report.status_extended = f"CloudFront Distribution {distribution.id} has Field Level Encryption disabled" + + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled_test.py new file mode 100644 index 00000000..5f228fd4 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_field_level_encryption_enabled/cloudfront_distributions_field_level_encryption_enabled_test.py @@ -0,0 +1,110 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import ( + DefaultCacheConfigBehaviour, + Distribution, + ViewerProtocolPolicy, +) + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_field_level_encryption_enabled: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_field_level_encryption_enabled.cloudfront_distributions_field_level_encryption_enabled import ( + cloudfront_distributions_field_level_encryption_enabled, + ) + + check = cloudfront_distributions_field_level_encryption_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_field_level_encryption_enabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="AAAAAAAA", + ), + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_field_level_encryption_enabled.cloudfront_distributions_field_level_encryption_enabled import ( + cloudfront_distributions_field_level_encryption_enabled, + ) + + check = cloudfront_distributions_field_level_encryption_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has Field Level Encryption enabled" + ) + + def test_one_distribution_field_level_encryption_disabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="", + ), + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_field_level_encryption_enabled.cloudfront_distributions_field_level_encryption_enabled import ( + cloudfront_distributions_field_level_encryption_enabled, + ) + + check = cloudfront_distributions_field_level_encryption_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has Field Level Encryption disabled" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.metadata.json new file mode 100644 index 00000000..330f8321 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_geo_restrictions_enabled", + "CheckTitle": "Check if Geo restrictions are enabled in CloudFront distributions.", + "CheckType": [""], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "low", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if Geo restrictions are enabled in CloudFront distributions.", + "Risk": "Consider countries where service should not be accessed; by legal or compliance requirements. Additionally if not restricted the attack vector is increased.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html", + "Remediation": { + "Code": { + "CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/CloudFront/geo-restriction.html", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "If possible; define and enable Geo restrictions for this service.", + "Url": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "Infrastructure Security", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.py b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.py new file mode 100644 index 00000000..2d229d0d --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled.py @@ -0,0 +1,23 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client +from providers.aws.services.cloudfront.cloudfront_service import GeoRestrictionType + + +class cloudfront_distributions_geo_restrictions_enabled(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + if distribution.geo_restriction_type == GeoRestrictionType.none: + report.status = "FAIL" + report.status_extended = f"CloudFront Distribution {distribution.id} has Geo restrictions disabled" + else: + report.status = "PASS" + report.status_extended = f"CloudFront Distribution {distribution.id} has Geo restrictions enabled" + + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled_test.py new file mode 100644 index 00000000..3b71e4e1 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_geo_restrictions_enabled/cloudfront_distributions_geo_restrictions_enabled_test.py @@ -0,0 +1,135 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import ( + Distribution, + GeoRestrictionType, +) + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_geo_restrictions_enabled: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_geo_restrictions_enabled.cloudfront_distributions_geo_restrictions_enabled import ( + cloudfront_distributions_geo_restrictions_enabled, + ) + + check = cloudfront_distributions_geo_restrictions_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_geo_restriction_disabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + geo_restriction_type=GeoRestrictionType.none, + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_geo_restrictions_enabled.cloudfront_distributions_geo_restrictions_enabled import ( + cloudfront_distributions_geo_restrictions_enabled, + ) + + check = cloudfront_distributions_geo_restrictions_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has Geo restrictions disabled" + ) + + def test_one_distribution_geo_restriction_enabled_whitelist(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + geo_restriction_type=GeoRestrictionType.whitelist, + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_geo_restrictions_enabled.cloudfront_distributions_geo_restrictions_enabled import ( + cloudfront_distributions_geo_restrictions_enabled, + ) + + check = cloudfront_distributions_geo_restrictions_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has Geo restrictions enabled" + ) + + def test_one_distribution_geo_restriction_enabled_blacklist(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + geo_restriction_type=GeoRestrictionType.blacklist, + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_geo_restrictions_enabled.cloudfront_distributions_geo_restrictions_enabled import ( + cloudfront_distributions_geo_restrictions_enabled, + ) + + check = cloudfront_distributions_geo_restrictions_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has Geo restrictions enabled" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.metadata.json new file mode 100644 index 00000000..7beb5759 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_https_enabled", + "CheckTitle": "Check if CloudFront distributions are set to HTTPS.", + "CheckType": [""], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "medium", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if CloudFront distributions are set to HTTPS.", + "Risk": "If not enabled sensitive information in transit is not protected. Surveillance and other threats are risks may exists.", + "RelatedUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html", + "Remediation": { + "Code": { + "CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/CloudFront/security-policy.html", + "NativeIaC": "https://docs.bridgecrew.io/docs/networking_32#cloudformation", + "Other": "", + "Terraform": "https://docs.bridgecrew.io/docs/networking_32#terraform" + }, + "Recommendation": { + "Text": "Use HTTPS everywhere possible. It will enforce privacy and protect against account hijacking and other threats.", + "Url": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-https.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.py b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.py new file mode 100644 index 00000000..e66a813e --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled.py @@ -0,0 +1,38 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client +from providers.aws.services.cloudfront.cloudfront_service import ViewerProtocolPolicy + + +class cloudfront_distributions_https_enabled(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + if ( + distribution.default_cache_config.viewer_protocol_policy + == ViewerProtocolPolicy.allow_all + ): + report.status = "FAIL" + report.status_extended = f"CloudFront Distribution {distribution.id} viewers can use HTTP or HTTPS" + elif ( + distribution.default_cache_config.viewer_protocol_policy + == ViewerProtocolPolicy.redirect_to_https + ): + report.status = "PASS" + report.status_extended = ( + f"CloudFront Distribution {distribution.id} has redirect to HTTPS" + ) + elif ( + distribution.default_cache_config.viewer_protocol_policy + == ViewerProtocolPolicy.https_only + ): + report.status = "PASS" + report.status_extended = ( + f"CloudFront Distribution {distribution.id} has HTTPS only" + ) + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled_test.py new file mode 100644 index 00000000..a2e5ccba --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_https_enabled/cloudfront_distributions_https_enabled_test.py @@ -0,0 +1,148 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import ( + DefaultCacheConfigBehaviour, + Distribution, + ViewerProtocolPolicy, +) + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_https_enabled: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_https_enabled.cloudfront_distributions_https_enabled import ( + cloudfront_distributions_https_enabled, + ) + + check = cloudfront_distributions_https_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_https_disabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.allow_all, + field_level_encryption_id="", + ), + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_https_enabled.cloudfront_distributions_https_enabled import ( + cloudfront_distributions_https_enabled, + ) + + check = cloudfront_distributions_https_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} viewers can use HTTP or HTTPS" + ) + + def test_one_distribution_https_redirect(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.redirect_to_https, + field_level_encryption_id="", + ), + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_https_enabled.cloudfront_distributions_https_enabled import ( + cloudfront_distributions_https_enabled, + ) + + check = cloudfront_distributions_https_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has redirect to HTTPS" + ) + + def test_one_distribution_https_only(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="", + ), + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_https_enabled.cloudfront_distributions_https_enabled import ( + cloudfront_distributions_https_enabled, + ) + + check = cloudfront_distributions_https_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has HTTPS only" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json new file mode 100644 index 00000000..3e9a5085 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_logging_enabled", + "CheckTitle": "Check if CloudFront distributions have logging enabled.", + "CheckType": [""], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "medium", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if CloudFront distributions have logging enabled.", + "Risk": "If not enabled monitoring of service use is not possible.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html", + "Remediation": { + "Code": { + "CLI": "https://docs.bridgecrew.io/docs/logging_20#cli-command", + "NativeIaC": "https://docs.bridgecrew.io/docs/logging_20#cloudformation", + "Other": "", + "Terraform": "https://docs.bridgecrew.io/docs/logging_20#terraform" + }, + "Recommendation": { + "Text": "Real-time monitoring can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms. Enable logging for services with defined log rotation. This logs are useful for Incident Response and forensics investigation among other use cases.", + "Url": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "Logging and Monitoring", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.py b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.py new file mode 100644 index 00000000..f4589aac --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled.py @@ -0,0 +1,28 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client + + +class cloudfront_distributions_logging_enabled(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + if ( + distribution.logging_enabled + or distribution.default_cache_config.realtime_log_config_arn + ): + report.status = "PASS" + report.status_extended = ( + f"CloudFront Distribution {distribution.id} has logging enabled" + ) + else: + report.status = "FAIL" + report.status_extended = ( + f"CloudFront Distribution {distribution.id} has logging disabled" + ) + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled_test.py new file mode 100644 index 00000000..95bfd8f2 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_logging_enabled/cloudfront_distributions_logging_enabled_test.py @@ -0,0 +1,185 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import ( + DefaultCacheConfigBehaviour, + Distribution, + ViewerProtocolPolicy, +) + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_logging_enabled: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_logging_enabled.cloudfront_distributions_logging_enabled import ( + cloudfront_distributions_logging_enabled, + ) + + check = cloudfront_distributions_logging_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_logging_enabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + logging_enabled=True, + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_logging_enabled.cloudfront_distributions_logging_enabled import ( + cloudfront_distributions_logging_enabled, + ) + + check = cloudfront_distributions_logging_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has logging enabled" + ) + + def test_one_distribution_logging_disabled_realtime_disabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + logging_enabled=False, + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn="", + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="", + ), + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_logging_enabled.cloudfront_distributions_logging_enabled import ( + cloudfront_distributions_logging_enabled, + ) + + check = cloudfront_distributions_logging_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has logging disabled" + ) + + def test_one_distribution_logging_disabled_realtime_enabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + logging_enabled=False, + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn=DISTRIBUTION_ARN, + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="", + ), + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_logging_enabled.cloudfront_distributions_logging_enabled import ( + cloudfront_distributions_logging_enabled, + ) + + check = cloudfront_distributions_logging_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has logging enabled" + ) + + def test_one_distribution_logging_enabled_realtime_enabled(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + logging_enabled=True, + default_cache_config=DefaultCacheConfigBehaviour( + realtime_log_config_arn=DISTRIBUTION_ARN, + viewer_protocol_policy=ViewerProtocolPolicy.https_only, + field_level_encryption_id="", + ), + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_logging_enabled.cloudfront_distributions_logging_enabled import ( + cloudfront_distributions_logging_enabled, + ) + + check = cloudfront_distributions_logging_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} has logging enabled" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.metadata.json new file mode 100644 index 00000000..55456f1b --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_using_deprecated_ssl_protocols", + "CheckTitle": "Check if CloudFront distributions are using deprecated SSL protocols.", + "CheckType": [""], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "low", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if CloudFront distributions are using deprecated SSL protocols.", + "Risk": "Using insecure ciphers could affect privacy of in transit information.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html", + "Remediation": { + "Code": { + "CLI": "https://docs.bridgecrew.io/docs/networking_33#cli-command", + "NativeIaC": "", + "Other": "https://docs.bridgecrew.io/docs/networking_33#aws-cloudfront-console", + "Terraform": "" + }, + "Recommendation": { + "Text": "Use a Security policy with a ciphers that are stronger as possible. Drop legacy and unsecure ciphers.", + "Url": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.py new file mode 100644 index 00000000..b7f8a65a --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols.py @@ -0,0 +1,37 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client +from providers.aws.services.cloudfront.cloudfront_service import OriginsSSLProtocols + + +class cloudfront_distributions_using_deprecated_ssl_protocols(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + report.status = "PASS" + report.status_extended = f"CloudFront Distribution {distribution.id} is not using a deprecated SSL protocol" + + bad_ssl_protocol = False + for origin in distribution.origins: + if "CustomOriginConfig" in origin: + for ssl_protocol in origin["CustomOriginConfig"][ + "OriginSslProtocols" + ]["Items"]: + if ssl_protocol in ( + OriginsSSLProtocols.SSLv3.value, + OriginsSSLProtocols.TLSv1.value, + OriginsSSLProtocols.TLSv1_1.value, + ): + bad_ssl_protocol = True + break + if bad_ssl_protocol: + report.status = "FAIL" + report.status_extended = f"CloudFront Distribution {distribution.id} is using a deprecated SSL protocol" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols_test.py new file mode 100644 index 00000000..eabffdcf --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_deprecated_ssl_protocols/cloudfront_distributions_using_deprecated_ssl_protocols_test.py @@ -0,0 +1,306 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import Distribution + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_using_deprecated_ssl_protocols: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_deprecated_ssl_protocols.cloudfront_distributions_using_deprecated_ssl_protocols import ( + cloudfront_distributions_using_deprecated_ssl_protocols, + ) + + check = cloudfront_distributions_using_deprecated_ssl_protocols() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_using_deprecated_ssl_protocols(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[ + { + "Id": "string", + "DomainName": "string", + "OriginPath": "string", + "CustomHeaders": { + "Quantity": 123, + "Items": [ + { + "HeaderName": "string", + "HeaderValue": "string", + }, + ], + }, + "S3OriginConfig": {"OriginAccessIdentity": "string"}, + "CustomOriginConfig": { + "HTTPPort": 123, + "HTTPSPort": 123, + "OriginProtocolPolicy": "https-only", + "OriginSslProtocols": { + "Quantity": 123, + "Items": [ + "SSLv3", + ], + }, + "OriginReadTimeout": 123, + "OriginKeepaliveTimeout": 123, + }, + "ConnectionAttempts": 123, + "ConnectionTimeout": 123, + "OriginShield": { + "Enabled": False, + "OriginShieldRegion": "string", + }, + "OriginAccessControlId": "string", + }, + ], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_deprecated_ssl_protocols.cloudfront_distributions_using_deprecated_ssl_protocols import ( + cloudfront_distributions_using_deprecated_ssl_protocols, + ) + + check = cloudfront_distributions_using_deprecated_ssl_protocols() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is using a deprecated SSL protocol" + ) + + def test_one_distribution_using_SSL_and_TLS(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[ + { + "Id": "string", + "DomainName": "string", + "OriginPath": "string", + "CustomHeaders": { + "Quantity": 123, + "Items": [ + { + "HeaderName": "string", + "HeaderValue": "string", + }, + ], + }, + "S3OriginConfig": {"OriginAccessIdentity": "string"}, + "CustomOriginConfig": { + "HTTPPort": 123, + "HTTPSPort": 123, + "OriginProtocolPolicy": "https-only", + "OriginSslProtocols": { + "Quantity": 123, + "Items": [ + "SSLv3", + "TLSv1.2", + ], + }, + "OriginReadTimeout": 123, + "OriginKeepaliveTimeout": 123, + }, + "ConnectionAttempts": 123, + "ConnectionTimeout": 123, + "OriginShield": { + "Enabled": False, + "OriginShieldRegion": "string", + }, + "OriginAccessControlId": "string", + }, + ], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_deprecated_ssl_protocols.cloudfront_distributions_using_deprecated_ssl_protocols import ( + cloudfront_distributions_using_deprecated_ssl_protocols, + ) + + check = cloudfront_distributions_using_deprecated_ssl_protocols() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is using a deprecated SSL protocol" + ) + + def test_one_distribution_using_SSL_and_bad_TLS(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[ + { + "Id": "string", + "DomainName": "string", + "OriginPath": "string", + "CustomHeaders": { + "Quantity": 123, + "Items": [ + { + "HeaderName": "string", + "HeaderValue": "string", + }, + ], + }, + "S3OriginConfig": {"OriginAccessIdentity": "string"}, + "CustomOriginConfig": { + "HTTPPort": 123, + "HTTPSPort": 123, + "OriginProtocolPolicy": "https-only", + "OriginSslProtocols": { + "Quantity": 123, + "Items": [ + "SSLv3", + "TLSv1.1", + ], + }, + "OriginReadTimeout": 123, + "OriginKeepaliveTimeout": 123, + }, + "ConnectionAttempts": 123, + "ConnectionTimeout": 123, + "OriginShield": { + "Enabled": False, + "OriginShieldRegion": "string", + }, + "OriginAccessControlId": "string", + }, + ], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_deprecated_ssl_protocols.cloudfront_distributions_using_deprecated_ssl_protocols import ( + cloudfront_distributions_using_deprecated_ssl_protocols, + ) + + check = cloudfront_distributions_using_deprecated_ssl_protocols() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is using a deprecated SSL protocol" + ) + + def test_one_distribution_not_using_deprecated_ssl_protocols(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[ + { + "Id": "string", + "DomainName": "string", + "OriginPath": "string", + "CustomHeaders": { + "Quantity": 123, + "Items": [ + { + "HeaderName": "string", + "HeaderValue": "string", + }, + ], + }, + "S3OriginConfig": {"OriginAccessIdentity": "string"}, + "CustomOriginConfig": { + "HTTPPort": 123, + "HTTPSPort": 123, + "OriginProtocolPolicy": "https-only", + "OriginSslProtocols": { + "Quantity": 123, + "Items": ["TLSv1.2"], + }, + "OriginReadTimeout": 123, + "OriginKeepaliveTimeout": 123, + }, + "ConnectionAttempts": 123, + "ConnectionTimeout": 123, + "OriginShield": { + "Enabled": False, + "OriginShieldRegion": "string", + }, + "OriginAccessControlId": "string", + }, + ], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_deprecated_ssl_protocols.cloudfront_distributions_using_deprecated_ssl_protocols import ( + cloudfront_distributions_using_deprecated_ssl_protocols, + ) + + check = cloudfront_distributions_using_deprecated_ssl_protocols() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is not using a deprecated SSL protocol" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/__init__.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.metadata.json b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.metadata.json new file mode 100644 index 00000000..95375455 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "cloudfront_distributions_using_waf", + "CheckTitle": "Check if CloudFront distributions are using WAF.", + "CheckType": ["IAM"], + "ServiceName": "cloudfront", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id", + "Severity": "medium", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if CloudFront distributions are using WAF.", + "Risk": "Potential attacks and / or abuse of service; more even for even for internet reachable services.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/cloudfront-features.html", + "Remediation": { + "Code": { + "CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/CloudFront/cloudfront-integrated-with-waf.html", + "NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_general_27#cloudformation", + "Other": "https://docs.bridgecrew.io/docs/bc_aws_general_27#cloudfront-console", + "Terraform": "https://docs.bridgecrew.io/docs/bc_aws_general_27#terraform" + }, + "Recommendation": { + "Text": "Use AWS WAF to protect your service from common web exploits. These could affect availability and performance; compromise security; or consume excessive resources.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/cloudfront-features.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.py new file mode 100644 index 00000000..4192350f --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf.py @@ -0,0 +1,21 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client + + +class cloudfront_distributions_using_waf(Check): + def execute(self): + findings = [] + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = distribution.region + report.resource_arn = distribution.arn + report.resource_id = distribution.id + if distribution.web_acl_id: + report.status = "PASS" + report.status_extended = f"CloudFront Distribution {distribution.id} is using AWS WAF web ACL {distribution.web_acl_id}" + else: + report.status = "FAIL" + report.status_extended = f"CloudFront Distribution {distribution.id} is not using AWS WAF web ACL" + findings.append(report) + + return findings diff --git a/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf_test.py b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf_test.py new file mode 100644 index 00000000..a2fd0a83 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_distributions_using_waf/cloudfront_distributions_using_waf_test.py @@ -0,0 +1,98 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.cloudfront.cloudfront_service import Distribution + +DISTRIBUTION_ID = "E27LVI50CSW06W" +DISTRIBUTION_ARN = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{DISTRIBUTION_ID}" +) +REGION = "eu-west-1" + + +class Test_cloudfront_distributions_using_waf: + def test_no_distributions(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = {} + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_waf.cloudfront_distributions_using_waf import ( + cloudfront_distributions_using_waf, + ) + + check = cloudfront_distributions_using_waf() + result = check.execute() + + assert len(result) == 0 + + def test_one_distribution_waf(self): + wef_acl_id = "TEST-WAF-ACL" + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + web_acl_id=wef_acl_id, + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_waf.cloudfront_distributions_using_waf import ( + cloudfront_distributions_using_waf, + ) + + check = cloudfront_distributions_using_waf() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is using AWS WAF web ACL {wef_acl_id}" + ) + + def test_one_distribution_no_waf(self): + cloudfront_client = mock.MagicMock + cloudfront_client.distributions = { + "DISTRIBUTION_ID": Distribution( + arn=DISTRIBUTION_ARN, + id=DISTRIBUTION_ID, + region=REGION, + origins=[], + ) + } + + with mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.cloudfront.cloudfront_distributions_using_waf.cloudfront_distributions_using_waf import ( + cloudfront_distributions_using_waf, + ) + + check = cloudfront_distributions_using_waf() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == REGION + assert result[0].resource_arn == DISTRIBUTION_ARN + assert result[0].resource_id == DISTRIBUTION_ID + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront Distribution {DISTRIBUTION_ID} is not using AWS WAF web ACL" + ) diff --git a/providers/aws/services/cloudfront/cloudfront_service.py b/providers/aws/services/cloudfront/cloudfront_service.py new file mode 100644 index 00000000..a4e7ff65 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_service.py @@ -0,0 +1,157 @@ +from dataclasses import dataclass +from enum import Enum + +from lib.logger import logger +from providers.aws.aws_provider import get_region_global_service + + +################## CloudFront +class CloudFront: + def __init__(self, audit_info): + self.service = "cloudfront" + self.session = audit_info.audit_session + self.audited_account = audit_info.audited_account + self.client = self.session.client(self.service) + self.region = get_region_global_service(audit_info) + self.distributions = self.__list_distributions__(self.client, self.region) + self.distributions = self.__get_distribution_config__( + self.client, self.distributions, self.region + ) + + def __get_session__(self): + return self.session + + def __list_distributions__(self, client, region) -> dict: + logger.info("CloudFront - Listing Distributions...") + distributions = {} + try: + list_ditributions_paginator = client.get_paginator("list_distributions") + for page in list_ditributions_paginator.paginate(): + if "Items" in page["DistributionList"]: + for item in page["DistributionList"]["Items"]: + distribution_id = item["Id"] + distribution_arn = item["ARN"] + origins = item["Origins"]["Items"] + distribution = Distribution( + arn=distribution_arn, + id=distribution_id, + origins=origins, + region=region, + ) + distributions[distribution_id] = distribution + + return distributions + + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __get_distribution_config__(self, client, distributions, region) -> dict: + logger.info("CloudFront - Getting Distributions...") + try: + for distribution_id in distributions.keys(): + distribution_config = client.get_distribution_config(Id=distribution_id) + # Global Config + distributions[distribution_id].logging_enabled = distribution_config[ + "DistributionConfig" + ]["Logging"]["Enabled"] + distributions[ + distribution_id + ].geo_restriction_type = distribution_config["DistributionConfig"][ + "Restrictions" + ][ + "GeoRestriction" + ][ + "RestrictionType" + ] + distributions[distribution_id].web_acl_id = distribution_config[ + "DistributionConfig" + ]["WebACLId"] + + # Default Cache Config + default_chache_config = DefaultCacheConfigBehaviour( + realtime_log_config_arn=distribution_config["DistributionConfig"][ + "DefaultCacheBehavior" + ]["RealtimeLogConfigArn"], + viewer_protocol_policy=distribution_config["DistributionConfig"][ + "DefaultCacheBehavior" + ]["ViewerProtocolPolicy"], + field_level_encryption_id=distribution_config["DistributionConfig"][ + "DefaultCacheBehavior" + ]["FieldLevelEncryptionId"], + ) + distributions[ + distribution_id + ].default_cache_config = default_chache_config + + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + finally: + return distributions + + +class OriginsSSLProtocols(Enum): + SSLv3 = "SSLv3" + TLSv1 = "TLSv1" + TLSv1_1 = "TLSv1.1" + TLSv1_2 = "TLSv1.2" + + +class ViewerProtocolPolicy(Enum): + """The protocol that viewers can use to access the files in the origin specified by TargetOriginId when a request matches the path pattern in PathPattern""" + + allow_all = "allow-all" + redirect_to_https = "redirect-to-https" + https_only = "https-only" + + +class GeoRestrictionType(Enum): + """Method types that you want to use to restrict distribution of your content by country""" + + none = "none" + blacklist = "blacklist" + whitelist = "whitelist" + + +@dataclass +class DefaultCacheConfigBehaviour: + realtime_log_config_arn: str + viewer_protocol_policy: ViewerProtocolPolicy + field_level_encryption_id: str + + +@dataclass +class Distribution: + """Distribution holds a CloudFront Distribution with the required information to run the rela""" + + arn: str + id: str + region: str + logging_enabled: bool + default_cache_config: DefaultCacheConfigBehaviour + geo_restriction_type: GeoRestrictionType + origins: list + web_acl_id: str + + def __init__( + self, + arn, + id, + region, + origins, + logging_enabled=False, + default_cache_config=None, + geo_restriction_type=None, + web_acl_id="", + ): + self.arn = arn + self.id = id + self.region = region + self.logging_enabled = logging_enabled + self.default_cache_config = default_cache_config + self.geo_restriction_type = geo_restriction_type + self.origins = origins + self.web_acl_id = web_acl_id diff --git a/providers/aws/services/cloudfront/cloudfront_service_test.py b/providers/aws/services/cloudfront/cloudfront_service_test.py new file mode 100644 index 00000000..4c941120 --- /dev/null +++ b/providers/aws/services/cloudfront/cloudfront_service_test.py @@ -0,0 +1,247 @@ +from unittest.mock import patch + +import botocore +from boto3 import client, session +from moto import mock_cloudfront +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.cloudfront.cloudfront_service import CloudFront + +# Mock Test Region +AWS_REGION = "eu-west-1" + + +def example_distribution_config(ref): + """Return a basic example distribution config for use in tests.""" + return { + "CallerReference": ref, + "Origins": { + "Quantity": 1, + "Items": [ + { + "Id": "origin1", + "DomainName": "asdf.s3.us-east-1.amazonaws.com", + "S3OriginConfig": {"OriginAccessIdentity": ""}, + } + ], + }, + "DefaultCacheBehavior": { + "TargetOriginId": "origin1", + "ViewerProtocolPolicy": "allow-all", + "MinTTL": 10, + "ForwardedValues": { + "QueryString": False, + "Cookies": {"Forward": "none"}, + }, + }, + "Comment": "an optional comment that's not actually optional", + "Enabled": False, + } + + +# Mocking Access Analyzer Calls +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + """ + As you can see the operation_name has the list_analyzers snake_case form but + we are using the ListAnalyzers form. + Rationale -> https://github.com/boto/botocore/blob/develop/botocore/client.py#L810:L816 + + We have to mock every AWS API call using Boto3 + """ + if operation_name == "GetDistributionConfig": + if kwarg["Id"]: + return { + "DistributionConfig": { + "Origins": {"Quantity": 123, "Items": []}, + "OriginGroups": {"Quantity": 123, "Items": []}, + "DefaultCacheBehavior": { + "TargetOriginId": "", + "TrustedSigners": { + "Enabled": False, + "Quantity": 123, + "Items": [ + "", + ], + }, + "TrustedKeyGroups": { + "Enabled": False, + "Quantity": 123, + "Items": [ + "", + ], + }, + "ViewerProtocolPolicy": "https-only", + "AllowedMethods": { + "Quantity": 123, + "Items": [ + "GET", + ], + "CachedMethods": { + "Quantity": 123, + "Items": [ + "GET", + ], + }, + }, + "SmoothStreaming": False, + "Compress": False, + "LambdaFunctionAssociations": {}, + "FunctionAssociations": {}, + "FieldLevelEncryptionId": "enabled", + "RealtimeLogConfigArn": "test-log-arn", + "CachePolicyId": "", + "OriginRequestPolicyId": "", + "ResponseHeadersPolicyId": "", + "ForwardedValues": { + "QueryString": False, + "Cookies": {}, + "Headers": {}, + "QueryStringCacheKeys": {}, + }, + "MinTTL": 123, + "DefaultTTL": 123, + "MaxTTL": 123, + }, + "CacheBehaviors": {}, + "CustomErrorResponses": {}, + "Comment": "", + "Logging": { + "Enabled": True, + "IncludeCookies": False, + "Bucket": "", + "Prefix": "", + }, + "PriceClass": "PriceClass_All", + "Enabled": False, + "ViewerCertificate": {}, + "Restrictions": { + "GeoRestriction": { + "RestrictionType": "blacklist", + "Quantity": 123, + "Items": [ + "", + ], + } + }, + "WebACLId": "test-web-acl", + "HttpVersion": "http2and3", + "IsIPV6Enabled": False, + }, + "ETag": "", + } + return make_api_call(self, operation_name, kwarg) + + +# PENDING PR TO GET THE PARAMETERS USING MOTO + + +# Patch every AWS call using Boto3 +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_CloudFront_Service: + # Mocked Audit Info + def set_mocked_audit_info(self): + audit_info = AWS_Audit_Info( + original_session=None, + audit_session=session.Session( + profile_name=None, + botocore_session=None, + region_name=AWS_REGION, + ), + audited_account=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + # Test CloudFront Client + @mock_cloudfront + def test__get_client__(self): + cloudfront = CloudFront(self.set_mocked_audit_info()) + assert cloudfront.client.__class__.__name__ == "CloudFront" + + # Test CloudFront Session + @mock_cloudfront + def test__get_session__(self): + cloudfront = CloudFront(self.set_mocked_audit_info()) + assert cloudfront.session.__class__.__name__ == "Session" + + # Test CloudFront Service + @mock_cloudfront + def test__get_service__(self): + cloudfront = CloudFront(self.set_mocked_audit_info()) + assert cloudfront.service == "cloudfront" + + @mock_cloudfront + def test__list_distributions__zero(self): + cloudfront = CloudFront(self.set_mocked_audit_info()) + + assert len(cloudfront.distributions) == 0 + + @mock_cloudfront + def test__list_distributions__complete(self): + cloudfront_client = client("cloudfront") + config = example_distribution_config("ref") + response = cloudfront_client.create_distribution(DistributionConfig=config) + cloudfront_distribution_id = response["Distribution"]["Id"] + cloudfront_distribution_arn = response["Distribution"]["ARN"] + cloudfront = CloudFront(self.set_mocked_audit_info()) + + assert len(cloudfront.distributions) == 1 + assert ( + cloudfront.distributions[cloudfront_distribution_id].arn + == cloudfront_distribution_arn + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].id + == cloudfront_distribution_id + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].region + == self.set_mocked_audit_info().audit_session.region_name + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].logging_enabled is True + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].origins + == cloudfront_client.get_distribution(Id=cloudfront_distribution_id)[ + "Distribution" + ]["DistributionConfig"]["Origins"]["Items"] + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].geo_restriction_type + == "blacklist" + ) + assert ( + cloudfront.distributions[cloudfront_distribution_id].web_acl_id + == "test-web-acl" + ) + assert ( + cloudfront.distributions[ + cloudfront_distribution_id + ].default_cache_config.realtime_log_config_arn + == "test-log-arn" + ) + assert ( + cloudfront.distributions[ + cloudfront_distribution_id + ].default_cache_config.viewer_protocol_policy + == "https-only" + ) + assert ( + cloudfront.distributions[ + cloudfront_distribution_id + ].default_cache_config.field_level_encryption_id + == "enabled" + )