feat(CloudFront): Service and Checks (#1470)

This commit is contained in:
Pepe Fagoaga
2022-11-16 10:21:43 +01:00
committed by GitHub
parent 30738d7810
commit 2c5320a0b0
34 changed files with 1769 additions and 248 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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

View File

@@ -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"
)