feat(ELB): add ELB and ELBv2 tests and checks (#1489)

Co-authored-by: sergargar <sergio@verica.io>
This commit is contained in:
Sergio Garcia
2022-11-17 20:30:27 +01:00
committed by GitHub
parent 12896cceaa
commit 3370475fe9
81 changed files with 3803 additions and 711 deletions

View File

@@ -309,6 +309,10 @@ def generate_regional_clients(service: str, audit_info: AWS_Audit_Info) -> dict:
json_regions = data["services"]["dynamodb"]["regions"][
audit_info.audited_partition
]
elif service == "elbv2":
json_regions = data["services"]["elb"]["regions"][audit_info.audited_partition]
elif service == "wafv2" or service == "waf-regional":
json_regions = data["services"]["waf"]["regions"][audit_info.audited_partition]
else:
json_regions = data["services"][service]["regions"][
audit_info.audited_partition

View File

View File

@@ -1,81 +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_extra7129="7.129"
CHECK_TITLE_extra7129="[extra7129] Check if Application Load Balancer has a WAF ACL attached"
CHECK_SCORED_extra7129="NOT_SCORED"
CHECK_CIS_LEVEL_extra7129="EXTRA"
CHECK_SEVERITY_extra7129="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7129="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7129="extra7129"
CHECK_ASFF_COMPLIANCE_TYPE_extra7129="ens-mp.s.2.aws.waf.3"
CHECK_SERVICENAME_extra7129="elb"
CHECK_RISK_extra7129='If not WAF ACL is attached risk of web attacks increases.'
CHECK_REMEDIATION_extra7129='Using the AWS Management Console open the AWS WAF console to attach an ACL.'
CHECK_DOC_extra7129='https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html'
CHECK_CAF_EPIC_extra7129='Infrastructure Security'
PARALLEL_REGIONS="50"
extra7129(){
for regx in $REGIONS; do
# (
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Scheme == `internet-facing` && Type == `application`].[LoadBalancerName]' --output text 2>&1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
LIST_OF_WAFV2_WEBACL_ARN=$($AWSCLI wafv2 list-web-acls $PROFILE_OPT --region=$regx --scope=REGIONAL --query WebACLs[*].ARN --output text)
LIST_OF_WAFV1_WEBACL_WEBACLID=$($AWSCLI waf-regional list-web-acls $PROFILE_OPT --region $regx --query WebACLs[*].[WebACLId] --output text)
if [[ $LIST_OF_ELBSV2 ]]; then
for alb in $LIST_OF_ELBSV2; do
if [[ ${#LIST_OF_WAFV2_WEBACL_ARN[@]} -gt 0 || ${#LIST_OF_WAFV1_WEBACL_WEBACLID[@]} -gt 0 ]]; then
WAF_PROTECTED_ALBS=()
for wafaclarn in $LIST_OF_WAFV2_WEBACL_ARN; do
ALB_RESOURCES_IN_WEBACL=$($AWSCLI wafv2 list-resources-for-web-acl $PROFILE_OPT --web-acl-arn $wafaclarn --region=$regx --resource-type APPLICATION_LOAD_BALANCER --query ResourceArns --output text | xargs -n1 | awk -F'/' '{ print $3 }'| grep $alb)
if [[ $ALB_RESOURCES_IN_WEBACL ]]; then
WAF_PROTECTED_ALBS+=($wafaclarn)
fi
done
for wafv1aclid in $LIST_OF_WAFV1_WEBACL_WEBACLID; do
ALB_RESOURCES_IN_WEBACL=$($AWSCLI waf-regional list-resources-for-web-acl $PROFILE_OPT --web-acl-id $wafv1aclid --region=$regx --resource-type APPLICATION_LOAD_BALANCER --output text --query "[ResourceArns]"| grep $alb)
if [[ $ALB_RESOURCES_IN_WEBACL ]]; then
WAFv1_PROTECTED_ALBS+=($wafv1aclid)
fi
done
if [[ ${#WAF_PROTECTED_ALBS[@]} -gt 0 || ${#WAFv1_PROTECTED_ALBS[@]} -gt 0 ]]; then
if [[ ${#WAF_PROTECTED_ALBS[@]} -gt 0 ]]; then
for wafaclarn in "${WAF_PROTECTED_ALBS[@]}"; do
WAFV2_WEBACL_ARN_SHORT=$(echo $wafaclarn | awk -F'/' '{ print $3 }')
textPass "$regx: Application Load Balancer $alb is protected by WAFv2 ACL $WAFV2_WEBACL_ARN_SHORT" "$regx" "$alb"
done
fi
if [[ ${#WAFv1_PROTECTED_ALBS[@]} -gt 0 ]]; then
for wafv1aclid in "${WAFv1_PROTECTED_ALBS[@]}"; do
textPass "$regx: Application Load Balancer $alb is protected by WAFv1 ACL $wafv1aclid" "$regx" "$alb"
done
fi
else
textFail "$regx: Application Load Balancer $alb is not protected by WAF ACL" "$regx" "$alb"
fi
else
textFail "$regx: Application Load Balancer $alb is not protected no WAF ACL found" "$regx" "$alb"
fi
done
else
textInfo "$regx: No Application Load Balancers found" "$regx"
fi
# ) &
done
# wait
}

View File

@@ -1,48 +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_extra7142="7.142"
CHECK_TITLE_extra7142="[extra7142] Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling"
CHECK_SCORED_extra7142="NOT_SCORED"
CHECK_CIS_LEVEL_extra7142="EXTRA"
CHECK_SEVERITY_extra7142="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7142="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7142="extra7142"
CHECK_ASFF_COMPLIANCE_TYPE_extra7142=""
CHECK_SERVICENAME_extra7142="elb"
CHECK_RISK_extra7142='ALB can be target of actors sending bad HTTP headers'
CHECK_REMEDIATION_extra7142='Ensure Application Load Balancer is configured for HTTP headers with header fields that are not valid are removed by the load balancer (true)'
CHECK_DOC_extra7142='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#desync-mitigation-mode'
CHECK_CAF_EPIC_extra7142='Data Protection'
extra7142(){
for regx in $REGIONS; do
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application`].[LoadBalancerArn]' --output text 2>&1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBSV2 ]];then
for alb in $LIST_OF_ELBSV2;do
CHECK_IF_DROP_INVALID_HEADER_FIELDS=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $alb --query 'Attributes[6]' --output text|grep -i true)
if [[ $CHECK_IF_DROP_INVALID_HEADER_FIELDS ]];then
textPass "$regx: Application Load Balancer $alb is dropping invalid header fields." "$regx" "$alb"
else
textFail "$regx: Application Load Balancer $alb is not dropping invalid header fields" "$regx" "$alb"
fi
done
else
textInfo "$regx: no ALBs found"
fi
done
}

View File

@@ -1,48 +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_extra7150="7.150"
CHECK_TITLE_extra7150="[extra7150] Check if Elastic Load Balancers have deletion protection enabled"
CHECK_SCORED_extra7150="NOT_SCORED"
CHECK_CIS_LEVEL_extra7150="EXTRA"
CHECK_SEVERITY_extra7150="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7150="AwsElbLoadBalancer"
CHECK_ALTERNATE_check7150="extra7150"
CHECK_SERVICENAME_extra7150="elb"
CHECK_RISK_extra7150='If deletion protection is not enabled; the resource is not protected against deletion.'
CHECK_REMEDIATION_extra7150='Enable deletion protection attribute; this is not enabled by default.'
CHECK_DOC_extra7150='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection'
CHECK_CAF_EPIC_extra7150='Data Protection'
extra7150(){
# "Check if Elastic Load Balancers have delete protection enabled."
for regx in $REGIONS; do
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1|xargs -n1 )
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBSV2 ]]; then
for elb in $LIST_OF_ELBSV2; do
CHECK_DELETION_PROTECTION_ENABLED=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $elb --query Attributes[*] --output text|grep "deletion_protection.enabled"|grep true )
ELBV2_NAME=$(echo $elb|cut -d\/ -f3)
if [[ $CHECK_DELETION_PROTECTION_ENABLED ]]; then
textPass "$regx: $ELBV2_NAME has the attribute deletion protection enabled" "$regx" "$elb"
else
textFail "$regx: $ELBV2_NAME does not have deletion protection enabled." "$regx" "$elb"
fi
done
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) 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.
# Remediation:
#
# https://docs.aws.amazon.com/cli/latest/reference/elbv2/modify-load-balancer-attributes.html
#
# aws elbv2 modify-load-balancer-attributes
# --load-balancer-arn <alb arn>\
# --attributes Key=routing.http.desync_mitigation_mode,Value=<defensive/strictest>
CHECK_ID_extra7155="7.155"
CHECK_TITLE_extra7155="[extra7155] Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode"
CHECK_SCORED_extra7155="NOT_SCORED"
CHECK_CIS_LEVEL_extra7155="EXTRA"
CHECK_SEVERITY_extra7155="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7155="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7155="extra7155"
CHECK_SERVICENAME_extra7155="elb"
CHECK_RISK_extra7155='HTTP Desync issues can lead to request smuggling and make your applications vulnerable to request queue or cache poisoning; which could lead to credential hijacking or execution of unauthorized commands.'
CHECK_REMEDIATION_extra7155='Ensure Application Load Balancer is configured with defensive or strictest desync mitigation mode'
CHECK_DOC_extra7155='https://aws.amazon.com/about-aws/whats-new/2020/08/application-and-classic-load-balancers-adding-defense-in-depth-with-introduction-of-desync-mitigation-mode/'
CHECK_CAF_EPIC_extra7155='Data Protection'
extra7155() {
for regx in $REGIONS; do
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application`].[LoadBalancerArn]' --output text 2>&1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBSV2 ]];then
for alb in $LIST_OF_ELBSV2;do
CHECK_DESYNC_MITIGATION_MODE=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $alb --query 'Attributes[8]' --output json | jq -r '.Value')
if [[ $CHECK_DESYNC_MITIGATION_MODE == "monitor" ]]; then
textFail "$regx: Application load balancer $alb does not have desync mitigation mode set as defensive or strictest." "$regx" "$alb"
else
textPass "$regx: Application load balancer $alb is configured with correct desync mitigation mode." "$regx" "$alb"
fi
done
else
textInfo "$regx: No Application Load Balancers found" "$regx"
fi
done
}

View File

@@ -1,47 +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_extra7158="7.158"
CHECK_TITLE_extra7158="[extra7158] Check if ELBV2 has listeners underneath"
CHECK_SCORED_extra7158="NOT_SCORED"
CHECK_CIS_LEVEL_extra7158="EXTRA"
CHECK_SEVERITY_extra7158="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7158="AwsElbv2LoadBalancer"
CHECK_ALTERNATE_check7158="extra7158"
CHECK_SERVICENAME_extra7158="elb"
CHECK_RISK_extra7158='The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.'
CHECK_REMEDIATION_extra7158='Add listeners to Elastic Load Balancers V2.'
CHECK_DOC_extra7158='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html'
CHECK_CAF_EPIC_extra7158='Data Protection'
extra7158(){
for regx in $REGIONS; do
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers --query 'LoadBalancers[*].LoadBalancerArn' $PROFILE_OPT --region $regx --output text 2>&1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBSV2 ]]; then
for elb in $LIST_OF_ELBSV2; do
LIST_OF_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elb --query 'Listeners[*]' --output text)
ELBV2_NAME=$(echo $elb|cut -d\/ -f3)
if [[ $LIST_OF_LISTENERS ]]; then
textPass "$regx: $ELBV2_NAME has listeners underneath" "$regx" "$elb"
else
textFail "$regx: $ELBV2_NAME has no listeners underneath" "$regx" "$elb"
fi
done
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}

View File

@@ -1,46 +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_extra7159="7.159"
CHECK_TITLE_extra7159="[extra7159] Check if ELB has listeners underneath"
CHECK_SCORED_extra7159="NOT_SCORED"
CHECK_CIS_LEVEL_extra7159="EXTRA"
CHECK_SEVERITY_extra7159="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7159="AwsElbLoadBalancer"
CHECK_ALTERNATE_check7159="extra7159"
CHECK_SERVICENAME_extra7159="elb"
CHECK_RISK_extra7159='The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.'
CHECK_REMEDIATION_extra7159='Add listeners to Elastic Load Balancers.'
CHECK_DOC_extra7159='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html'
CHECK_CAF_EPIC_extra7159='Data Protection'
extra7159(){
for regx in $REGIONS; do
LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers --query 'LoadBalancerDescriptions[*].LoadBalancerName' $PROFILE_OPT --region $regx --output text 2>&1)
if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBS ]]; then
for elb in $LIST_OF_ELBS; do
LIST_OF_LISTENERS=$($AWSCLI elb describe-load-balancers --load-balancer-name $elb --query 'LoadBalancerDescriptions[*].ListenerDescriptions' $PROFILE_OPT --region $regx --output text)
if [[ $LIST_OF_LISTENERS ]]; then
textPass "$regx: $elb has listeners underneath" "$regx" "$elb"
else
textFail "$regx: $elb has no listeners underneath" "$regx" "$elb"
fi
done
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}

View File

@@ -1,65 +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_extra717="7.17"
CHECK_TITLE_extra717="[extra717] Check if Elastic Load Balancers have logging enabled"
CHECK_SCORED_extra717="NOT_SCORED"
CHECK_CIS_LEVEL_extra717="EXTRA"
CHECK_SEVERITY_extra717="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra717="AwsElbLoadBalancer"
CHECK_ALTERNATE_check717="extra717"
CHECK_SERVICENAME_extra717="elb"
CHECK_RISK_extra717='If logs are not enabled monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra717='Enable ELB logging; create la log lifecycle and define use cases.'
CHECK_DOC_extra717='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html'
CHECK_CAF_EPIC_extra717='Logging and Monitoring'
extra717(){
# "Check if Elastic Load Balancers have logging enabled "
for regx in $REGIONS; do
LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1 |xargs -n1)
if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to list load balancers v1" "$regx"
continue
fi
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1 |xargs -n1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to list load balancers v2" "$regx"
continue
fi
if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
if [[ $LIST_OF_ELBS ]]; then
for elb in $LIST_OF_ELBS; do
CHECK_ELBS_LOG_ENABLED=$($AWSCLI elb describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-name $elb --query 'LoadBalancerAttributes.AccessLog.Enabled' |grep "^true")
if [[ $CHECK_ELBS_LOG_ENABLED ]]; then
textPass "$regx: $elb has access logs to S3 configured" "$regx" "$elb"
else
textFail "$regx: $elb has not configured access logs" "$regx" "$elb"
fi
done
fi
if [[ $LIST_OF_ELBSV2 ]]; then
for elbarn in $LIST_OF_ELBSV2; do
CHECK_ELBSV2_LOG_ENABLED=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query Attributes[*] --output text |grep "^access_logs.s3.enabled"|cut -f2|grep true)
ELBV2_NAME=$(echo $elbarn|cut -d\/ -f3)
if [[ $CHECK_ELBSV2_LOG_ENABLED ]]; then
textPass "$regx: $ELBV2_NAME has access logs to S3 configured" "$regx" "$elb"
else
textFail "$regx: $ELBV2_NAME has not configured access logs" "$regx" "$elb"
fi
done
fi
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}

View File

@@ -1,53 +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_extra79="7.9"
CHECK_TITLE_extra79="[extra79] Check for internet facing Elastic Load Balancers"
CHECK_SCORED_extra79="NOT_SCORED"
CHECK_CIS_LEVEL_extra79="EXTRA"
CHECK_SEVERITY_extra79="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra79="AwsElbLoadBalancer"
CHECK_ALTERNATE_extra709="extra79"
CHECK_ALTERNATE_check79="extra79"
CHECK_ALTERNATE_check709="extra79"
CHECK_SERVICENAME_extra79="elb"
CHECK_RISK_extra79='Publicly accessible load balancers could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra79='Ensure the load balancer should be publicly accessible. If publicly exposed ensure a WAF ACL is implemented.'
CHECK_DOC_extra79='https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html'
CHECK_CAF_EPIC_extra79='Data Protection'
extra79(){
# "Check for internet facing Elastic Load Balancers "
for regx in $REGIONS; do
LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text 2>&1)
if [[ $(echo "$LIST_OF_PUBLIC_ELBS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied Trying to describe load balancers" "$regx"
continue
fi
LIST_OF_PUBLIC_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text 2>&1)
if [[ $(echo "$LIST_OF_PUBLIC_ELBSV2" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied Trying to describe load balancers" "$regx"
continue
fi
LIST_OF_ALL_ELBS=$( echo $LIST_OF_PUBLIC_ELBS; echo $LIST_OF_PUBLIC_ELBSV2)
LIST_OF_ALL_ELBS_PER_LINE=$( echo $LIST_OF_ALL_ELBS| xargs -n2 )
if [[ $LIST_OF_ALL_ELBS ]];then
while read -r elb;do
ELB_NAME=$(echo $elb | awk '{ print $1; }')
ELB_DNSNAME=$(echo $elb | awk '{ print $2; }')
textFail "$regx: ELB: $ELB_NAME at DNS: $ELB_DNSNAME is internet-facing!" "$regx" "$ELB_NAME"
done <<< "$LIST_OF_ALL_ELBS_PER_LINE"
else
textPass "$regx: no Internet Facing ELBs found" "$regx" "$ELB_NAME"
fi
done
}

View File

@@ -1,143 +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_extra792="7.92"
CHECK_TITLE_extra792="[extra792] Check if Elastic Load Balancers have insecure SSL ciphers "
CHECK_SCORED_extra792="NOT_SCORED"
CHECK_CIS_LEVEL_extra792="EXTRA"
CHECK_SEVERITY_extra792="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra792="AwsElbLoadBalancer"
CHECK_ALTERNATE_check792="extra792"
CHECK_ASFF_COMPLIANCE_TYPE_extra792="ens-mp.com.2.aws.elb.2"
CHECK_SERVICENAME_extra792="elb"
CHECK_RISK_extra792='Using insecure ciphers could affect privacy of in transit information.'
CHECK_REMEDIATION_extra792='Use a Security policy with a ciphers that are stronger as possible. Drop legacy and unsecure ciphers.'
CHECK_DOC_extra792='https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-ssl-security-policy.html'
CHECK_CAF_EPIC_extra792='Data Protection'
extra792(){
# "Check if Elastic Load Balancers have insecure SSL ciphers "
for regx in $REGIONS; do
LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1|xargs -n1 )
if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text 2>&1|xargs -n1 )
if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
if [[ $LIST_OF_ELBS ]]; then
# https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-ssl-security-policy.html#ssl-ciphers
# https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html
ELBSECUREPOLICIES=("ELBSecurityPolicy-TLS-1-2-2017-01")
ELBSECURECIPHERS=("Protocol-TLSv1.2" "ECDHE-ECDSA-AES128-GCM-SHA256" "ECDHE-RSA-AES128-GCM-SHA256" "ECDHE-ECDSA-AES128-SHA256" "ECDHE-RSA-AES128-SHA256" "ECDHE-ECDSA-AES128-SHA" "ECDHE-RSA-AES128-SHA" "ECDHE-ECDSA-AES256-GCM-SHA384" "ECDHE-RSA-AES256-GCM-SHA384" "ECDHE-ECDSA-AES256-SHA384" "ECDHE-RSA-AES256-SHA384" "ECDHE-RSA-AES256-SHA" "ECDHE-ECDSA-AES256-SHA" "AES128-GCM-SHA256" "AES128-SHA256" "AES128-SHA" "AES256-GCM-SHA384" "AES256-SHA256" "AES256-SHA" "Server-Defined-Cipher-Order")
for elb in $LIST_OF_ELBS; do
ELB_LISTENERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --load-balancer-name $elb --query "LoadBalancerDescriptions[0]" --output json)
ELB_PROTOCOLS=$(echo $ELB_LISTENERS | jq -r '.ListenerDescriptions[].Listener.Protocol')
if [[ $(echo $ELB_PROTOCOLS | grep HTTPS) || $(echo $ELB_PROTOCOLS | grep SSL) ]]; then
ELB_POLICIES=$(echo $ELB_LISTENERS | jq -r '.ListenerDescriptions[].PolicyNames | .[]')
passed=true
for policy in $ELB_POLICIES; do
# Check for secure default policy
REFPOLICY=$($AWSCLI elb describe-load-balancer-policies $PROFILE_OPT --region $regx --load-balancer-name $elb --policy-name $policy --query "PolicyDescriptions[0].PolicyAttributeDescriptions[?(AttributeName == 'Reference-Security-Policy')].AttributeValue" --output text)
if [[ -n "$REFPOLICY" ]]; then
if array_contains ELBSECUREPOLICIES "$REFPOLICY"; then
continue # Passed for this listener/policy
else
passed=false
fi
else
# A custom policy is in use. Check Ciphers
CIPHERS=$($AWSCLI elb describe-load-balancer-policies $PROFILE_OPT --region $regx --load-balancer-name $elb --policy-name $policy --query "PolicyDescriptions[0].PolicyAttributeDescriptions[?(AttributeValue == 'true')].AttributeName" --output text)
for cipher in $CIPHERS; do
if array_contains ELBSECURECIPHERS "$cipher"; then
continue
else
passed=false
fi
done
fi
done
if $passed; then
textPass "$regx: $elb has no insecure SSL ciphers" "$regx" "$elb"
else
textFail "$regx: $elb has insecure SSL ciphers" "$regx" "$elb"
fi
else
textInfo "$regx: $elb does not have an HTTPS or SSL listener" "$regx"
fi
done
fi
if [[ $LIST_OF_ELBSV2 ]]; then
# NOTE - ALBs do NOT support custom security policies
# https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html
ELBV2SECUREPOLICIES=("ELBSecurityPolicy-TLS-1-2-2017-01" "ELBSecurityPolicy-TLS-1-2-Ext-2018-06" "ELBSecurityPolicy-FS-1-2-2019-08" "ELBSecurityPolicy-FS-1-2-Res-2019-08" "ELBSecurityPolicy-FS-1-2-Res-2020-10" "ELBSecurityPolicy-TLS13-1-2-2021-06" "ELBSecurityPolicy-TLS13-1-3-2021-06" "ELBSecurityPolicy-TLS13-1-2-Res-2021-06" "ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06" "ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06")
for elbarn in $LIST_OF_ELBSV2; do
passed=true
if [[ $(echo $elbarn | grep "loadbalancer/app/") ]]; then
elbname=$(echo $elbarn | awk -F 'loadbalancer/app/' '{print $2}' | awk -F '/' '{print $1}')
elif [[ $(echo $elbarn | grep "loadbalancer/net/") ]]; then
elbname=$(echo $elbarn | awk -F 'loadbalancer/net/' '{print $2}' | awk -F '/' '{print $1}')
else
elbname=$elbarn
fi
ELBV2_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query "Listeners[*]" --output json)
ELBV2_PROTOCOLS=$(echo $ELBV2_LISTENERS | jq -r '.[].Protocol')
if [[ $(echo $ELBV2_PROTOCOLS | grep HTTPS) || $(echo $ELBV2_PROTOCOLS | grep TLS) ]]; then
ELBV2_SSL_POLICIES=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query 'Listeners[*].SslPolicy' --output text)
for policy in $ELBV2_SSL_POLICIES; do
if array_contains ELBV2SECUREPOLICIES "$policy"; then
continue # Passed for this listener/policy
else
passed=false
fi
done
if $passed; then
textPass "$regx: $elbname has no insecure SSL ciphers" "$regx" "$elbname"
else
textFail "$regx: $elbname has insecure SSL ciphers" "$regx" "$elbname"
fi
else
textInfo "$regx: $elbname does not have an HTTPS or TLS listener" "$regx"
fi
done
fi
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}
array_contains () {
local array="$1[@]"
local seeking=$2
local in=1
for element in "${!array}"; do
if [[ $element == "$seeking" ]]; then
in=0
break
fi
done
return $in
}

View File

@@ -1,124 +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_extra793="7.93"
CHECK_TITLE_extra793="[extra793] Check if Elastic Load Balancers have SSL listeners "
CHECK_SCORED_extra793="NOT_SCORED"
CHECK_CIS_LEVEL_extra793="EXTRA"
CHECK_SEVERITY_extra793="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra793="AwsElbLoadBalancer"
CHECK_ALTERNATE_check793="extra793"
CHECK_ASFF_COMPLIANCE_TYPE_extra793="ens-mp.com.2.aws.elb.1"
CHECK_SERVICENAME_extra793="elb"
CHECK_RISK_extra793='Clear text communication could affect privacy of information in transit.'
CHECK_REMEDIATION_extra793='Scan for Load Balancers with HTTP or TCP listeners and understand the reason for each of them. Check if the listener can be implemented as TLS instead.'
CHECK_DOC_extra793='https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html'
CHECK_CAF_EPIC_extra793='Data Protection'
extra793(){
# "Check if Elastic Load Balancers have encrypted listeners "
for regx in $REGIONS; do
LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text 2>&1|xargs -n1)
if [[ $(echo "$LIST_OF_ELBS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?(Type == `application`)].LoadBalancerArn' --output text 2>&1|xargs -n1)
if [[ $(echo "$LIST_OF_ELBSV2" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe load balancers" "$regx"
continue
fi
if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then
if [[ $LIST_OF_ELBS ]]; then
ENCRYPTEDPROTOCOLS=("HTTPS" "SSL")
for elb in $LIST_OF_ELBS; do
ELB_PROTOCOLS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --load-balancer-name $elb --query "LoadBalancerDescriptions[0].ListenerDescriptions[*].Listener.Protocol" --output text)
passed=true
potential_redirect=false
for protocol in $ELB_PROTOCOLS; do
if array_contains ENCRYPTEDPROTOCOLS "$protocol"; then
continue
else
# Check if both HTTP and HTTPS in use
if [[ $(echo $ELB_PROTOCOLS | grep HTTPS) ]]; then
potential_redirect=true
fi
passed=false
fi
done
if $passed; then
textPass "$regx: $elb has encrypted listeners" "$regx"
else
if $potential_redirect; then
textInfo "$regx: $elb has both encrypted and non-encrypted listeners" "$regx"
else
textFail "$regx: $elb has non-encrypted listeners" "$regx" "$elb"
fi
fi
done
fi
if [[ $LIST_OF_ELBSV2 ]]; then
for elbarn in $LIST_OF_ELBSV2; do
https_only=true
redirect_rule=false
elbname=$(echo $elbarn | awk -F 'loadbalancer/app/' '{print $2}' | awk -F '/' '{print $1}')
ELBV2_LISTENERS=$($AWSCLI elbv2 describe-listeners $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query "Listeners[*]")
ELBV2_PROTOCOLS=$(echo $ELBV2_LISTENERS | jq -r '.[].Protocol')
if [[ $(echo $ELBV2_PROTOCOLS | grep HTTPS) ]]; then
for line in $(echo $ELBV2_LISTENERS | jq -r '.[] | .Protocol + "," + .ListenerArn'); do
protocol=$(echo $line | awk -F ',' '{print $1}')
listenerArn=$(echo $line | awk -F ',' '{print $2}')
if [[ $protocol == "HTTP" ]]; then
https_only=false
# Check for redirect rule
ELBV2_RULES=$($AWSCLI elbv2 describe-rules $PROFILE_OPT --region $regx --listener-arn $listenerArn --query 'Rules[]')
if [[ $(echo $ELBV2_RULES | jq -r '.[].Actions[].RedirectConfig.Protocol' | grep HTTPS) ]]; then
redirect_rule=true
fi
fi
done
if $https_only; then
textPass "$regx: $elbname has HTTPS listeners only" "$regx"
else
if $redirect_rule; then
textInfo "$regx: $elbname has HTTP listener that redirects to HTTPS" "$regx"
else
textFail "$regx: $elbname has non-encrypted listeners" "$regx" "$elbname"
fi
fi
else
textFail "$regx: $elbname has non-encrypted listeners" "$regx" "$elbname"
fi
done
fi
else
textInfo "$regx: No ELBs found" "$regx"
fi
done
}
array_contains () {
local array="$1[@]"
local seeking=$2
local in=1
for element in "${!array}"; do
if [[ $element == "$seeking" ]]; then
in=0
break
fi
done
return $in
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
elb_client = ELB(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elb_insecure_ssl_ciphers",
"CheckTitle": "Check if Elastic Load Balancers have insecure SSL ciphers.",
"CheckType": ["Data Protection"],
"ServiceName": "elb",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElbLoadBalancer",
"Description": "Check if Elastic Load Balancers have insecure SSL ciphers.",
"Risk": "Using insecure ciphers could affect privacy of in transit information.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elb set-load-balancer-policies-of-listener --load-balancer-name <lb_name> --load-balancer-port 443 --policy-names ELBSecurityPolicy-TLS-1-2-2017-01",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELB/elb-security-policy.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_general_43#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/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,28 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elb.elb_client import elb_client
class elb_insecure_ssl_ciphers(Check):
def execute(self):
findings = []
secure_ssl_policies = [
"ELBSecurityPolicy-TLS-1-2-2017-01",
]
for lb in elb_client.loadbalancers:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.status = "PASS"
report.status_extended = (
f"ELB {lb.name} has not insecure SSL protocols or ciphers."
)
for listener in lb.listeners:
if listener.protocol == "HTTPS" and not any(
check in listener.policies for check in secure_ssl_policies
):
report.status = "FAIL"
report.status_extended = f"ELB {lb.name} has listeners with insecure SSL protocols or ciphers."
findings.append(report)
return findings

View File

@@ -0,0 +1,129 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elb
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elb_insecure_ssl_ciphers:
@mock_elb
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers.elb_client",
new=ELB(current_audit_info),
):
# Test Check
from providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers import (
elb_insecure_ssl_ciphers,
)
check = elb_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elb
def test_elb_listener_with_secure_policy(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "https", "LoadBalancerPort": 443, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
elb.set_load_balancer_policies_of_listener(
LoadBalancerName="my-lb",
LoadBalancerPort=443,
PolicyNames=["ELBSecurityPolicy-TLS-1-2-2017-01"],
)
elb.describe_load_balancer_policies(LoadBalancerName="my-lb")
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers import (
elb_insecure_ssl_ciphers,
)
check = elb_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has not insecure SSL protocols or ciphers",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
@mock_ec2
@mock_elb
def test_elb_with_HTTPS_listener(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "https", "LoadBalancerPort": 443, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_insecure_ssl_ciphers.elb_insecure_ssl_ciphers import (
elb_insecure_ssl_ciphers,
)
check = elb_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has listeners with insecure SSL protocols or ciphers",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elb_internet_facing",
"CheckTitle": "Check for internet facing Elastic Load Balancers.",
"CheckType": ["Data Protection"],
"ServiceName": "elb",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElbLoadBalancer",
"Description": "Check for internet facing Elastic Load Balancers.",
"Risk": "Publicly accessible load balancers could expose sensitive data to bad actors.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELB/internet-facing-load-balancers.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure the load balancer should be publicly accessible. If publicly exposed ensure a WAF ACL is implemented.",
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,22 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elb.elb_client import elb_client
class elb_internet_facing(Check):
def execute(self):
findings = []
for lb in elb_client.loadbalancers:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.status = "PASS"
report.status_extended = f"ELB {lb.name} is not internet facing."
if lb.scheme == "internet-facing":
report.status = "FAIL"
report.status_extended = (
f"ELB {lb.name} is internet facing in {lb.dns}."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,122 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elb
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elb_request_smugling:
@mock_elb
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_internet_facing.elb_internet_facing.elb_client",
new=ELB(current_audit_info),
):
# Test Check
from providers.aws.services.elb.elb_internet_facing.elb_internet_facing import (
elb_internet_facing,
)
check = elb_internet_facing()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elb
def test_elb_private(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_internet_facing.elb_internet_facing.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_internet_facing.elb_internet_facing import (
elb_internet_facing,
)
check = elb_internet_facing()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is not internet facing",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
@mock_ec2
@mock_elb
def test_elb_with_deletion_protection(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internet-facing",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_internet_facing.elb_internet_facing.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_internet_facing.elb_internet_facing import (
elb_internet_facing,
)
check = elb_internet_facing()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is internet facing",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elb_logging_enabled",
"CheckTitle": "Check if Elastic Load Balancers have logging enabled.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "elb",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElbLoadBalancer",
"Description": "Check if Elastic Load Balancers have logging enabled.",
"Risk": "If logs are not enabled monitoring of service use and threat analysis is not possible.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elb modify-load-balancer-attributes --load-balancer-name <lb_name> --load-balancer-attributes '{AccessLog:{Enabled:true,EmitInterval:60,S3BucketName:<bucket_name>}}'",
"NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_logging_23#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELB/elb-access-log.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_logging_23#terraform"
},
"Recommendation": {
"Text": "Enable ELB logging, create la log lifecycle and define use cases.",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/access-log-collection.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,22 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elb.elb_client import elb_client
class elb_logging_enabled(Check):
def execute(self):
findings = []
for lb in elb_client.loadbalancers:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.status = "FAIL"
report.status_extended = f"ELB {lb.name} has not configured access logs."
if lb.access_logs:
report.status = "PASS"
report.status_extended = (
f"ELB {lb.name} has access logs to S3 configured."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,134 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elb
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elb_logging_enabled:
@mock_elb
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled.elb_client",
new=ELB(current_audit_info),
):
# Test Check
from providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled import (
elb_logging_enabled,
)
check = elb_logging_enabled()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elb
def test_elb_without_access_log(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled import (
elb_logging_enabled,
)
check = elb_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has not configured access logs",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
@mock_ec2
@mock_elb
def test_elb_with_deletion_protection(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
elb.modify_load_balancer_attributes(
LoadBalancerName="my-lb",
LoadBalancerAttributes={
"AccessLog": {
"Enabled": True,
"S3BucketName": "mb",
"EmitInterval": 42,
"S3BucketPrefix": "s3bf",
}
},
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_logging_enabled.elb_logging_enabled import (
elb_logging_enabled,
)
check = elb_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has access logs to S3 configured",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"

View File

@@ -0,0 +1,91 @@
import threading
from typing import Optional
from pydantic import BaseModel
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################### ELB
class ELB:
def __init__(self, audit_info):
self.service = "elb"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.loadbalancers = []
self.__threading_call__(self.__describe_load_balancers__)
self.__threading_call__(self.__describe_load_balancer_attributes__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_load_balancers__(self, regional_client):
logger.info("ELB - Describing load balancers...")
try:
describe_elb_paginator = regional_client.get_paginator(
"describe_load_balancers"
)
for page in describe_elb_paginator.paginate():
for elb in page["LoadBalancerDescriptions"]:
listeners = []
for listener in elb["ListenerDescriptions"]:
listeners.append(
Listener(
protocol=listener["Listener"]["Protocol"],
policies=listener["PolicyNames"],
)
)
self.loadbalancers.append(
LoadBalancer(
name=elb["LoadBalancerName"],
dns=elb["DNSName"],
region=regional_client.region,
scheme=elb["Scheme"],
listeners=listeners,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_load_balancer_attributes__(self, regional_client):
logger.info("ELB - Describing attributes...")
try:
for lb in self.loadbalancers:
if lb.region == regional_client.region:
attributes = regional_client.describe_load_balancer_attributes(
LoadBalancerName=lb.name
)["LoadBalancerAttributes"]
if "AccessLog" in attributes:
lb.access_logs = attributes["AccessLog"]["Enabled"]
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class Listener(BaseModel):
protocol: str
policies: list[str]
class LoadBalancer(BaseModel):
name: str
dns: str
region: str
scheme: str
access_logs: Optional[bool]
listeners: list[Listener]

View File

@@ -0,0 +1,126 @@
from boto3 import client, resource, session
from moto import mock_ec2, mock_elb
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.elb.elb_service import ELB
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
class Test_ELB_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,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test ELB Service
@mock_elb
def test_service(self):
# ELB client for this test class
audit_info = self.set_mocked_audit_info()
elb = ELB(audit_info)
assert elb.service == "elb"
# Test ELB Client
@mock_elb
def test_client(self):
# ELB client for this test class
audit_info = self.set_mocked_audit_info()
elb = ELB(audit_info)
for regional_client in elb.regional_clients.values():
assert regional_client.__class__.__name__ == "ElasticLoadBalancing"
# Test ELB Session
@mock_elb
def test__get_session__(self):
# ELB client for this test class
audit_info = self.set_mocked_audit_info()
elb = ELB(audit_info)
assert elb.session.__class__.__name__ == "Session"
# Test ELB Describe Load Balancers
@mock_ec2
@mock_elb
def test__describe_load_balancers__(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
# ELB client for this test class
audit_info = self.set_mocked_audit_info()
elb = ELB(audit_info)
assert len(elb.loadbalancers) == 1
assert elb.loadbalancers[0].name == "my-lb"
assert elb.loadbalancers[0].region == AWS_REGION
assert elb.loadbalancers[0].scheme == "internal"
# Test ELB Describe Load Balancers Attributes
@mock_ec2
@mock_elb
def test__describe_load_balancer_attributes__(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
elb.modify_load_balancer_attributes(
LoadBalancerName="my-lb",
LoadBalancerAttributes={
"AccessLog": {
"Enabled": True,
"S3BucketName": "mb",
"EmitInterval": 42,
"S3BucketPrefix": "s3bf",
}
},
)
# ELB client for this test class
audit_info = self.set_mocked_audit_info()
elb = ELB(audit_info)
assert elb.loadbalancers[0].name == "my-lb"
assert elb.loadbalancers[0].region == AWS_REGION
assert elb.loadbalancers[0].scheme == "internal"
assert elb.loadbalancers[0].access_logs

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elb_ssl_listeners",
"CheckTitle": "Check if Elastic Load Balancers have SSL listeners.",
"CheckType": ["Data Protection"],
"ServiceName": "elb",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElbLoadBalancer",
"Description": "Check if Elastic Load Balancers have SSL listeners.",
"Risk": "Clear text communication could affect privacy of information in transit.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elb create-load-balancer-listeners --load-balancer-name <lb_name> --listeners Protocol=HTTPS, LoadBalancerPort=443, InstanceProtocol=HTTP, InstancePort=80, SSLCertificateId=<certificate_arn>",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELB/elb-listener-security.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Scan for Load Balancers with HTTP or TCP listeners and understand the reason for each of them. Check if the listener can be implemented as TLS instead..",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,24 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elb.elb_client import elb_client
class elb_ssl_listeners(Check):
def execute(self):
findings = []
secure_protocols = ["SSL", "HTTPS"]
for lb in elb_client.loadbalancers:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.status = "PASS"
report.status_extended = f"ELB {lb.name} has HTTPS listeners only."
for listener in lb.listeners:
if listener.protocol not in secure_protocols:
report.status = "FAIL"
report.status_extended = (
f"ELB {lb.name} has non-encrypted listeners."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,120 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elb
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elb_ssl_listeners:
@mock_elb
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners.elb_client",
new=ELB(current_audit_info),
):
# Test Check
from providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners import (
elb_ssl_listeners,
)
check = elb_ssl_listeners()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elb
def test_elb_with_HTTP_listener(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners import (
elb_ssl_listeners,
)
check = elb_ssl_listeners()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has non-encrypted listeners",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
@mock_ec2
@mock_elb
def test_elb_with_HTTPS_listener(self):
elb = client("elb", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "https", "LoadBalancerPort": 443, "InstancePort": 9000},
],
AvailabilityZones=[f"{AWS_REGION}a"],
Scheme="internal",
SecurityGroups=[security_group.id],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elb.elb_service import ELB
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners.elb_client",
new=ELB(current_audit_info),
):
from providers.aws.services.elb.elb_ssl_listeners.elb_ssl_listeners import (
elb_ssl_listeners,
)
check = elb_ssl_listeners()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has HTTPS listeners only",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"

View File

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
elbv2_client = ELBv2(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_deletion_protection",
"CheckTitle": "Check if Elastic Load Balancers have deletion protection enabled.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Elastic Load Balancers have deletion protection enabled.",
"Risk": "If deletion protection is not enabled, the resource is not protected against deletion.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=deletion_protection.enabled,Value=true",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/deletion-protection.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_networking_62#terraform"
},
"Recommendation": {
"Text": "Enable deletion protection attribute, this is not enabled by default.",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_deletion_protection(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "FAIL"
report.status_extended = f"ELBv2 {lb.name} has not deletion protection."
if lb.deletion_protection == "true":
report.status = "PASS"
report.status_extended = (
f"ELBv2 {lb.name} has deletion protection enabled."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,145 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_deletion_protection:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection import (
elbv2_deletion_protection,
)
check = elbv2_deletion_protection()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_deletion_protection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "deletion_protection.enabled", "Value": "false"},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection import (
elbv2_deletion_protection,
)
check = elbv2_deletion_protection()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has not deletion protection",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_deletion_protection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "deletion_protection.enabled", "Value": "true"},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_deletion_protection.elbv2_deletion_protection import (
elbv2_deletion_protection,
)
check = elbv2_deletion_protection()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has deletion protection enabled",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_desync_mitigation_mode",
"CheckTitle": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"Risk": "HTTP Desync issues can lead to request smuggling and make your applications vulnerable to request queue or cache poisoning; which could lead to credential hijacking or execution of unauthorized commands.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <alb arn> --attributes Key=routing.http.desync_mitigation_mode,Value=<defensive/strictest>",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"Url": "https://aws.amazon.com/about-aws/whats-new/2020/08/application-and-classic-load-balancers-adding-defense-in-depth-with-introduction-of-desync-mitigation-mode/"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,22 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_desync_mitigation_mode(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
if lb.type == "application":
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is configured with correct desync mitigation mode."
if lb.desync_mitigation_mode == "monitor":
report.status = "FAIL"
report.status_extended = f"ELBv2 ALB {lb.name} does not have desync mitigation mode set as defensive or strictest."
findings.append(report)
return findings

View File

@@ -0,0 +1,145 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_desync_mitigation_mode:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode import (
elbv2_desync_mitigation_mode,
)
check = elbv2_desync_mitigation_mode()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_desync_mitigation_mode(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "routing.http.desync_mitigation_mode", "Value": "monitor"},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode import (
elbv2_desync_mitigation_mode,
)
check = elbv2_desync_mitigation_mode()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have desync mitigation mode set as defensive or strictest",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_desync_mitigation_mode(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "routing.http.desync_mitigation_mode", "Value": "defensive"},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode import (
elbv2_desync_mitigation_mode,
)
check = elbv2_desync_mitigation_mode()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is configured with correct desync mitigation mode",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_insecure_ssl_ciphers",
"CheckTitle": "Check if Elastic Load Balancers have insecure SSL ciphers.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Elastic Load Balancers have insecure SSL ciphers.",
"Risk": "Using insecure ciphers could affect privacy of in transit information.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-listener --listener-arn <lb_arn> --ssl-policy ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/security-policy.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_general_43#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/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,39 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_insecure_ssl_ciphers(Check):
def execute(self):
findings = []
secure_ssl_policies = [
"ELBSecurityPolicy-TLS-1-2-2017-01",
"ELBSecurityPolicy-TLS-1-2-Ext-2018-06",
"ELBSecurityPolicy-FS-1-2-2019-08",
"ELBSecurityPolicy-FS-1-2-Res-2019-08",
"ELBSecurityPolicy-FS-1-2-Res-2020-10",
"ELBSecurityPolicy-TLS13-1-2-2021-06",
"ELBSecurityPolicy-TLS13-1-3-2021-06",
"ELBSecurityPolicy-TLS13-1-2-Res-2021-06",
"ELBSecurityPolicy-TLS13-1-2-Ext1-2021-06",
"ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06",
]
for lb in elbv2_client.loadbalancersv2:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "PASS"
report.status_extended = (
f"ELBv2 {lb.name} has not insecure SSL protocols or ciphers."
)
for listener in lb.listeners:
if (
listener.protocol == "HTTPS"
and listener.ssl_policy not in secure_ssl_policies
):
report.status = "FAIL"
report.status_extended = f"ELBv2 {lb.name} has listeners with insecure SSL protocols or ciphers."
findings.append(report)
return findings

View File

@@ -0,0 +1,178 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_insecure_ssl_ciphers:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers import (
elbv2_insecure_ssl_ciphers,
)
check = elbv2_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_listener_with_secure_policy(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
response = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=8080,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPort="8080",
HealthCheckPath="/",
HealthCheckIntervalSeconds=5,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
Matcher={"HttpCode": "200"},
)
target_group = response.get("TargetGroups")[0]
target_group_arn = target_group["TargetGroupArn"]
response = conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTPS",
Port=443,
SslPolicy="ELBSecurityPolicy-TLS-1-2-2017-01",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers import (
elbv2_insecure_ssl_ciphers,
)
check = elbv2_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has not insecure SSL protocols or ciphers",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_HTTPS_listener(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
response = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=8080,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPort="8080",
HealthCheckPath="/",
HealthCheckIntervalSeconds=5,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
Matcher={"HttpCode": "200"},
)
target_group = response.get("TargetGroups")[0]
target_group_arn = target_group["TargetGroupArn"]
response = conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTPS",
SslPolicy="ELBSecurityPolicy-TLS-1-1-2017-01",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers import (
elbv2_insecure_ssl_ciphers,
)
check = elbv2_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has listeners with insecure SSL protocols or ciphers",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_internet_facing",
"CheckTitle": "Check for internet facing Elastic Load Balancers.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check for internet facing Elastic Load Balancers.",
"Risk": "Publicly accessible load balancers could expose sensitive data to bad actors.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/internet-facing-load-balancers.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure the load balancer should be publicly accessible. If publicly exposed ensure a WAF ACL is implemented.",
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_internet_facing(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is not internet facing."
if lb.scheme == "internet-facing":
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} is internet facing in {lb.dns}."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,131 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_request_smugling:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing import (
elbv2_internet_facing,
)
check = elbv2_internet_facing()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_private(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing import (
elbv2_internet_facing,
)
check = elbv2_internet_facing()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is not internet facing",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_deletion_protection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internet-facing",
)["LoadBalancers"][0]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_internet_facing.elbv2_internet_facing import (
elbv2_internet_facing,
)
check = elbv2_internet_facing()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is internet facing",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_listeners_underneath",
"CheckTitle": "Check if ELBV2 has listeners underneath.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if ELBV2 has listeners underneath.",
"Risk": "The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Add listeners to Elastic Load Balancers V2.",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.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.elbv2.elbv2_client import elbv2_client
class elbv2_listeners_underneath(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "PASS"
report.status_extended = f"ELBv2 {lb.name} has listeners underneath."
if len(lb.listeners) == 0:
report.status = "FAIL"
report.status_extended = f"ELBv2 {lb.name} has no listeners underneath."
findings.append(report)
return findings

View File

@@ -0,0 +1,150 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_listeners_underneath:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath import (
elbv2_listeners_underneath,
)
check = elbv2_listeners_underneath()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_listeners(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath import (
elbv2_listeners_underneath,
)
check = elbv2_listeners_underneath()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has no listeners underneath",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_listeners(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
response = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=8080,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPort="8080",
HealthCheckPath="/",
HealthCheckIntervalSeconds=5,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
Matcher={"HttpCode": "200"},
)
target_group = response.get("TargetGroups")[0]
target_group_arn = target_group["TargetGroupArn"]
response = conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_listeners_underneath.elbv2_listeners_underneath import (
elbv2_listeners_underneath,
)
check = elbv2_listeners_underneath()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search("has listeners underneath", result[0].status_extended)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_logging_enabled",
"CheckTitle": "Check if Elastic Load Balancers have logging enabled.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Elastic Load Balancers have logging enabled.",
"Risk": "If logs are not enabled monitoring of service use and threat analysis is not possible.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=access_logs.s3.enabled,Value=true Key=access_logs.s3.bucket,Value=<bucket_name> Key=access_logs.s3.prefix,Value=<prefix>",
"NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_logging_22#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/access-log.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_logging_22#terraform"
},
"Recommendation": {
"Text": "Enable ELB logging, create la log lifecycle and define use cases.",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_logging_enabled(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} has not configured access logs."
)
if lb.access_logs == "true":
report.status = "PASS"
report.status_extended = (
f"ELBv2 ALB {lb.name} has access logs to S3 configured."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,151 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_logging_enabled:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled import (
elbv2_logging_enabled,
)
check = elbv2_logging_enabled()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_deletion_protection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "access_logs.s3.enabled",
"Value": "false",
},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled import (
elbv2_logging_enabled,
)
check = elbv2_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has not configured access logs",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_deletion_protection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "access_logs.s3.enabled",
"Value": "true",
},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_logging_enabled.elbv2_logging_enabled import (
elbv2_logging_enabled,
)
check = elbv2_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has access logs to S3 configured",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_request_smugling",
"CheckTitle": "Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling.",
"Risk": "ALB can be target of actors sending bad HTTP headers.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=routing.http.drop_invalid_header_fields.enabled,Value=true",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/drop-invalid-header-fields-enabled.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-alb-drops-http-headers#terraform"
},
"Recommendation": {
"Text": "Ensure Application Load Balancer is configured for HTTP headers with header fields that are not valid are removed by the load balancer (true).",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,26 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_request_smugling(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
if lb.type == "application":
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} is not dropping invalid header fields."
)
if lb.drop_invalid_header_fields == "true":
report.status = "PASS"
report.status_extended = (
f"ELBv2 ALB {lb.name} is dropping invalid header fields."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,151 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_request_smugling:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_dropping(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "false",
},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is not dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_dropping(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "true",
},
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,162 @@
import threading
from typing import Optional
from pydantic import BaseModel
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################### ELBv2
class ELBv2:
def __init__(self, audit_info):
self.service = "elbv2"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.loadbalancersv2 = []
self.__threading_call__(self.__describe_load_balancers__)
self.listeners = []
self.__threading_call__(self.__describe_listeners__)
self.__threading_call__(self.__describe_load_balancer_attributes__)
self.__threading_call__(self.__describe_rules__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_load_balancers__(self, regional_client):
logger.info("ELBv2 - Describing load balancers...")
try:
describe_elbv2_paginator = regional_client.get_paginator(
"describe_load_balancers"
)
for page in describe_elbv2_paginator.paginate():
for elbv2 in page["LoadBalancers"]:
self.loadbalancersv2.append(
LoadBalancerv2(
name=elbv2["LoadBalancerName"],
dns=elbv2["DNSName"],
region=regional_client.region,
arn=elbv2["LoadBalancerArn"],
scheme=elbv2["Scheme"],
type=elbv2["Type"],
listeners=[],
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_listeners__(self, regional_client):
logger.info("ELBv2 - Describing listeners...")
try:
for lb in self.loadbalancersv2:
if lb.region == regional_client.region:
describe_elbv2_paginator = regional_client.get_paginator(
"describe_listeners"
)
for page in describe_elbv2_paginator.paginate(
LoadBalancerArn=lb.arn
):
for listener in page["Listeners"]:
port = 0
if "Port" in listener:
port = listener["Port"]
lb.listeners.append(
Listenerv2(
region=regional_client.region,
arn=listener["ListenerArn"],
port=port,
protocol=listener["Protocol"],
ssl_policy=listener.get("SslPolicy"),
rules=[],
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_load_balancer_attributes__(self, regional_client):
logger.info("ELBv2 - Describing attributes...")
try:
for lb in self.loadbalancersv2:
if lb.region == regional_client.region:
for attribute in regional_client.describe_load_balancer_attributes(
LoadBalancerArn=lb.arn
)["Attributes"]:
if attribute["Key"] == "routing.http.desync_mitigation_mode":
lb.desync_mitigation_mode = attribute["Value"]
if attribute["Key"] == "deletion_protection.enabled":
lb.deletion_protection = attribute["Value"]
if attribute["Key"] == "access_logs.s3.enabled":
lb.access_logs = attribute["Value"]
if (
attribute["Key"]
== "routing.http.drop_invalid_header_fields.enabled"
):
lb.drop_invalid_header_fields = attribute["Value"]
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_rules__(self, regional_client):
logger.info("ELBv2 - Describing Rules...")
try:
for lb in self.loadbalancersv2:
if lb.region == regional_client.region:
for listener in lb.listeners:
for rule in regional_client.describe_rules(
ListenerArn=listener.arn
)["Rules"]:
listener.rules.append(
ListenerRule(
arn=rule["RuleArn"],
actions=rule["Actions"],
conditions=rule["Conditions"],
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class ListenerRule(BaseModel):
arn: str
actions: list[dict]
conditions: list[dict]
class Listenerv2(BaseModel):
arn: str
region: str
port: int
protocol: str
ssl_policy: Optional[str]
rules: list[ListenerRule]
class LoadBalancerv2(BaseModel):
name: str
dns: str
arn: str
region: str
scheme: str
type: str
access_logs: Optional[str]
desync_mitigation_mode: Optional[str]
deletion_protection: Optional[str]
drop_invalid_header_fields: Optional[str]
listeners: list[Listenerv2]

View File

@@ -0,0 +1,228 @@
from boto3 import client, resource, session
from moto import mock_ec2, mock_elbv2
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.elbv2.elbv2_service import ELBv2
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
class Test_ELBv2_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,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test ELBv2 Service
@mock_elbv2
def test_service(self):
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert elbv2.service == "elbv2"
# Test ELBv2 Client
@mock_elbv2
def test_client(self):
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
for regional_client in elbv2.regional_clients.values():
assert regional_client.__class__.__name__ == "ElasticLoadBalancingv2"
# Test ELBv2 Session
@mock_elbv2
def test__get_session__(self):
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert elbv2.session.__class__.__name__ == "Session"
# Test ELBv2 Describe Load Balancers
@mock_ec2
@mock_elbv2
def test__describe_load_balancers__(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert len(elbv2.loadbalancersv2) == 1
assert elbv2.loadbalancersv2[0].name == "my-lb"
assert elbv2.loadbalancersv2[0].region == AWS_REGION
assert elbv2.loadbalancersv2[0].scheme == "internal"
assert elbv2.loadbalancersv2[0].arn == lb["LoadBalancerArn"]
# Test ELBv2 Describe Listeners
@mock_ec2
@mock_elbv2
def test__describe_listeners__(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
Port=443,
DefaultActions=[
{
"Type": "redirect",
"RedirectConfig": {
"Protocol": "HTTPS",
"Port": "443",
"StatusCode": "HTTP_301",
},
}
],
)
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert len(elbv2.loadbalancersv2[0].listeners) == 1
assert elbv2.loadbalancersv2[0].listeners[0].protocol == "HTTP"
assert elbv2.loadbalancersv2[0].listeners[0].port == 443
# Test ELBv2 Describe Load Balancers Attributes
@mock_ec2
@mock_elbv2
def test__describe_load_balancer_attributes__(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "routing.http.desync_mitigation_mode", "Value": "defensive"},
{"Key": "access_logs.s3.enabled", "Value": "true"},
{"Key": "deletion_protection.enabled", "Value": "true"},
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "false",
},
],
)
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert len(elbv2.loadbalancersv2) == 1
assert elbv2.loadbalancersv2[0].desync_mitigation_mode == "defensive"
assert elbv2.loadbalancersv2[0].access_logs == "true"
assert elbv2.loadbalancersv2[0].deletion_protection == "true"
assert elbv2.loadbalancersv2[0].drop_invalid_header_fields == "false"
# Test ELBv2 Describe Load Balancers Attributes
@mock_ec2
@mock_elbv2
def test__describe_rules__(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
actions = [
{
"Type": "redirect",
"RedirectConfig": {
"Protocol": "HTTPS",
"Port": "443",
"StatusCode": "HTTP_301",
},
}
]
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=actions,
)
# ELBv2 client for this test class
audit_info = self.set_mocked_audit_info()
elbv2 = ELBv2(audit_info)
assert len(elbv2.loadbalancersv2) == 1
assert elbv2.loadbalancersv2[0].listeners[0].rules[0].actions == actions

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_ssl_listeners",
"CheckTitle": "Check if Elastic Load Balancers have SSL listeners.",
"CheckType": ["Data Protection"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Elastic Load Balancers have SSL listeners.",
"Risk": "Clear text communication could affect privacy of information in transit.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 create-listener --load-balancer-arn <lb_arn> --protocol HTTPS --port 443 --ssl-policy <ssl_policy> --certificates CertificateArn=<certificate_arn>,IsDefault=true",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/networking_36#aws-ec2-console",
"Terraform": ""
},
"Recommendation": {
"Text": "Scan for Load Balancers with HTTP or TCP listeners and understand the reason for each of them. Check if the listener can be implemented as TLS instead.",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,36 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_ssl_listeners(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
if lb.type == "application":
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "PASS"
report.status_extended = (
f"ELBv2 ALB {lb.name} has HTTPS listeners only."
)
for listener in lb.listeners:
if listener.protocol == "HTTP":
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} has non-encrypted listeners."
)
# Check if it redirects HTTP to HTTPS
for rule in listener.rules:
for action in rule.actions:
if (
action["Type"] == "redirect"
and action["RedirectConfig"]["Protocol"] == "HTTPS"
):
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} has HTTP listener but it redirects to HTTPS."
findings.append(report)
return findings

View File

@@ -0,0 +1,239 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_ssl_listeners:
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners import (
elbv2_ssl_listeners,
)
check = elbv2_ssl_listeners()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_with_HTTP_listener(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
response = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=8080,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPort="8080",
HealthCheckPath="/",
HealthCheckIntervalSeconds=5,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
Matcher={"HttpCode": "200"},
)
target_group = response.get("TargetGroups")[0]
target_group_arn = target_group["TargetGroupArn"]
response = conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners import (
elbv2_ssl_listeners,
)
check = elbv2_ssl_listeners()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has non-encrypted listeners",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_HTTPS_listener(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
response = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=8080,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPort="8080",
HealthCheckPath="/",
HealthCheckIntervalSeconds=5,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
Matcher={"HttpCode": "200"},
)
target_group = response.get("TargetGroups")[0]
target_group_arn = target_group["TargetGroupArn"]
response = conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTPS",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners import (
elbv2_ssl_listeners,
)
check = elbv2_ssl_listeners()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has HTTPS listeners only",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_HTTPS_redirection(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=[
{
"Type": "redirect",
"RedirectConfig": {
"Protocol": "HTTPS",
"Port": "443",
"StatusCode": "HTTP_301",
},
}
],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners.elbv2_client",
new=ELBv2(current_audit_info),
):
from providers.aws.services.elbv2.elbv2_ssl_listeners.elbv2_ssl_listeners import (
elbv2_ssl_listeners,
)
check = elbv2_ssl_listeners()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has HTTP listener but it redirects to HTTPS",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "elbv2_waf_acl_attached",
"CheckTitle": "Check if Application Load Balancer has a WAF ACL attached.",
"CheckType": ["Infrastructure Security"],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Application Load Balancer has a WAF ACL attached.",
"Risk": "If not WAF ACL is attached risk of web attacks increases.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Using the AWS Management Console open the AWS WAF console to attach an ACL.",
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,31 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.elbv2.elbv2_client import elbv2_client
from providers.aws.services.waf.waf_client import waf_client
from providers.aws.services.wafv2.wafv2_client import wafv2_client
class elbv2_waf_acl_attached(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
if lb.type == "application":
report = Check_Report(self.metadata)
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} is not protected by WAF Web ACL."
)
for acl in wafv2_client.web_acls:
if lb.arn in acl.albs:
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is protected by WAFv2 Web ACL {acl.name}."
for acl in waf_client.web_acls:
if lb.arn in acl.albs:
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is protected by WAFv1 Web ACL {acl.name}."
findings.append(report)
return findings

View File

@@ -0,0 +1,213 @@
from re import search
from unittest import mock
import botocore
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2, mock_wafv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
# Mocking WAF-Regional Calls
make_api_call = botocore.client.BaseClient._make_api_call
def mock_make_api_call(self, operation_name, kwarg):
"""We have to mock every AWS API call using Boto3"""
if operation_name == "ListWebACLs":
return {
"WebACLs": [
{"WebACLId": "my-web-acl-id", "Name": "my-web-acl"},
]
}
if operation_name == "ListResourcesForWebACL":
return {
"ResourceArns": [
"alb-arn",
]
}
return make_api_call(self, operation_name, kwarg)
class Test_elbv2_waf_acl_attached:
@mock_wafv2
@mock_elbv2
def test_elb_no_balancers(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
from providers.aws.services.waf.waf_service import WAF
from providers.aws.services.wafv2.wafv2_service import WAFv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.elbv2_client",
new=ELBv2(current_audit_info),
):
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.wafv2_client",
new=WAFv2(current_audit_info),
):
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.waf_client",
new=WAF(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached import (
elbv2_waf_acl_attached,
)
check = elbv2_waf_acl_attached()
result = check.execute()
assert len(result) == 0
@mock_wafv2
@mock_ec2
@mock_elbv2
def test_elbv2_without_WAF(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
wafv2 = client("wafv2", region_name="us-east-1")
_ = wafv2.create_web_acl(
Scope="REGIONAL",
Name="my-web-acl",
DefaultAction={"Allow": {}},
VisibilityConfig={
"SampledRequestsEnabled": False,
"CloudWatchMetricsEnabled": False,
"MetricName": "idk",
},
)["Summary"]
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
from providers.aws.services.waf.waf_service import WAF
from providers.aws.services.wafv2.wafv2_service import WAFv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.elbv2_client",
new=ELBv2(current_audit_info),
):
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.wafv2_client",
new=WAFv2(current_audit_info),
):
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.waf_client",
new=WAF(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached import (
elbv2_waf_acl_attached,
)
check = elbv2_waf_acl_attached()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is not protected by WAF Web ACL",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_wafv2
@mock_ec2
@mock_elbv2
def test_elbv2_with_WAF(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
wafv2 = client("wafv2", region_name="us-east-1")
waf = wafv2.create_web_acl(
Scope="REGIONAL",
Name="my-web-acl",
DefaultAction={"Allow": {}},
VisibilityConfig={
"SampledRequestsEnabled": False,
"CloudWatchMetricsEnabled": False,
"MetricName": "idk",
},
)["Summary"]
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
wafv2.associate_web_acl(WebACLArn=waf["ARN"], ResourceArn=lb["LoadBalancerArn"])
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.elbv2.elbv2_service import ELBv2
from providers.aws.services.waf.waf_service import WAF
from providers.aws.services.wafv2.wafv2_service import WAFv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.elbv2_client",
new=ELBv2(current_audit_info),
):
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.wafv2_client",
new=WAFv2(current_audit_info),
) as service_client:
with mock.patch(
"providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached.waf_client",
new=WAF(current_audit_info),
):
# Test Check
from providers.aws.services.elbv2.elbv2_waf_acl_attached.elbv2_waf_acl_attached import (
elbv2_waf_acl_attached,
)
service_client.web_acls[0].albs.append(lb["LoadBalancerArn"])
check = elbv2_waf_acl_attached()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is protected by WAFv2 Web ACL",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]

View File

@@ -84,7 +84,6 @@ class Test_iam_disable_45_days_credentials_test:
)
service_client.users[0].password_last_used = ""
print(service_client.users)
# raise Exception
check = iam_disable_45_days_credentials()
result = check.execute()

View File

@@ -14,7 +14,6 @@ class iam_no_expired_server_certificates_stored(Check):
report.resource_id = certificate.id
report.resource_arn = certificate.arn
expiration_days = (datetime.now(timezone.utc) - certificate.expiration).days
print(certificate.expiration)
if expiration_days >= 0:
report.status = "FAIL"
report.status_extended = f"IAM Certificate {certificate.name} has expired {expiration_days} days ago."

View File

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.waf.waf_service import WAF
waf_client = WAF(current_audit_info)

View File

@@ -0,0 +1,68 @@
import threading
from pydantic import BaseModel
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################### WAF
class WAF:
def __init__(self, audit_info):
self.service = "waf-regional"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.web_acls = []
self.__threading_call__(self.__list_web_acls__)
self.__threading_call__(self.__list_resources_for_web_acl__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __list_web_acls__(self, regional_client):
logger.info("WAF - Listing Regional Web ACLs...")
try:
for waf in regional_client.list_web_acls()["WebACLs"]:
self.web_acls.append(
WebAcl(
name=waf["Name"],
id=waf["WebACLId"],
albs=[],
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __list_resources_for_web_acl__(self, regional_client):
logger.info("WAF - Describing resources...")
try:
for acl in self.web_acls:
if acl.region == regional_client.region:
for resource in regional_client.list_resources_for_web_acl(
WebACLId=acl.id, ResourceType="APPLICATION_LOAD_BALANCER"
)["ResourceArns"]:
acl.albs.append(resource)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class WebAcl(BaseModel):
name: str
id: str
albs: list[str]
region: str

View File

@@ -0,0 +1,108 @@
from unittest.mock import patch
import botocore
from boto3 import session
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.waf.waf_service import WAF
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
# Mocking WAF-Regional Calls
make_api_call = botocore.client.BaseClient._make_api_call
def mock_make_api_call(self, operation_name, kwarg):
"""We have to mock every AWS API call using Boto3"""
if operation_name == "ListWebACLs":
return {
"WebACLs": [
{"WebACLId": "my-web-acl-id", "Name": "my-web-acl"},
]
}
if operation_name == "ListResourcesForWebACL":
return {
"ResourceArns": [
"alb-arn",
]
}
return make_api_call(self, operation_name, kwarg)
# Mock generate_regional_clients()
def mock_generate_regional_clients(service, audit_info):
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
regional_client.region = AWS_REGION
return {AWS_REGION: regional_client}
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"providers.aws.services.waf.waf_service.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_WAF_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,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test WAF Service
def test_service(self):
# WAF client for this test class
audit_info = self.set_mocked_audit_info()
waf = WAF(audit_info)
assert waf.service == "waf-regional"
# Test WAF Client
def test_client(self):
# WAF client for this test class
audit_info = self.set_mocked_audit_info()
waf = WAF(audit_info)
for regional_client in waf.regional_clients.values():
assert regional_client.__class__.__name__ == "WAFRegional"
# Test WAF Session
def test__get_session__(self):
# WAF client for this test class
audit_info = self.set_mocked_audit_info()
waf = WAF(audit_info)
assert waf.session.__class__.__name__ == "Session"
# Test WAF Describe Web ACLs
def test__list_web_acls__(self):
# WAF client for this test class
audit_info = self.set_mocked_audit_info()
waf = WAF(audit_info)
assert len(waf.web_acls) == 1
assert waf.web_acls[0].name == "my-web-acl"
assert waf.web_acls[0].region == AWS_REGION
assert waf.web_acls[0].id == "my-web-acl-id"
# Test WAF Describe Web ACLs Resources
def test__list_resources_for_web_acl__(self):
# WAF client for this test class
audit_info = self.set_mocked_audit_info()
waf = WAF(audit_info)
assert len(waf.web_acls) == 1
assert len(waf.web_acls[0].albs) == 1
assert "alb-arn" in waf.web_acls[0].albs

View File

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.wafv2.wafv2_service import WAFv2
wafv2_client = WAFv2(current_audit_info)

View File

@@ -0,0 +1,70 @@
import threading
from pydantic import BaseModel
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################### WAFv2
class WAFv2:
def __init__(self, audit_info):
self.service = "wafv2"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.web_acls = []
self.__threading_call__(self.__list_web_acls__)
self.__threading_call__(self.__list_resources_for_web_acl__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __list_web_acls__(self, regional_client):
logger.info("WAFv2 - Listing Regional Web ACLs...")
try:
for wafv2 in regional_client.list_web_acls(Scope="REGIONAL")["WebACLs"]:
self.web_acls.append(
WebAclv2(
arn=wafv2["ARN"],
name=wafv2["Name"],
id=wafv2["Id"],
albs=[],
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __list_resources_for_web_acl__(self, regional_client):
logger.info("WAFv2 - Describing resources...")
try:
for acl in self.web_acls:
if acl.region == regional_client.region:
for resource in regional_client.list_resources_for_web_acl(
WebACLArn=acl.arn, ResourceType="APPLICATION_LOAD_BALANCER"
)["ResourceArns"]:
acl.albs.append(resource)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class WebAclv2(BaseModel):
arn: str
name: str
id: str
albs: list[str]
region: str

View File

@@ -0,0 +1,125 @@
from boto3 import client, resource, session
from moto import mock_ec2, mock_elbv2, mock_wafv2
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.wafv2.wafv2_service import WAFv2
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
class Test_WAFv2_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,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test WAFv2 Service
@mock_wafv2
def test_service(self):
# WAFv2 client for this test class
audit_info = self.set_mocked_audit_info()
wafv2 = WAFv2(audit_info)
assert wafv2.service == "wafv2"
# Test WAFv2 Client
@mock_wafv2
def test_client(self):
# WAFv2 client for this test class
audit_info = self.set_mocked_audit_info()
wafv2 = WAFv2(audit_info)
for regional_client in wafv2.regional_clients.values():
assert regional_client.__class__.__name__ == "WAFV2"
# Test WAFv2 Session
@mock_wafv2
def test__get_session__(self):
# WAFv2 client for this test class
audit_info = self.set_mocked_audit_info()
wafv2 = WAFv2(audit_info)
assert wafv2.session.__class__.__name__ == "Session"
# Test WAFv2 Describe Web ACLs
@mock_wafv2
def test__list_web_acls__(self):
wafv2 = client("wafv2", region_name="us-east-1")
waf = wafv2.create_web_acl(
Scope="REGIONAL",
Name="my-web-acl",
DefaultAction={"Allow": {}},
VisibilityConfig={
"SampledRequestsEnabled": False,
"CloudWatchMetricsEnabled": False,
"MetricName": "idk",
},
)["Summary"]
# WAFv2 client for this test class
audit_info = self.set_mocked_audit_info()
wafv2 = WAFv2(audit_info)
assert len(wafv2.web_acls) == 1
assert wafv2.web_acls[0].name == waf["Name"]
assert wafv2.web_acls[0].region == AWS_REGION
assert wafv2.web_acls[0].arn == waf["ARN"]
assert wafv2.web_acls[0].id == waf["Id"]
# Test WAFv2 Describe Web ACLs Resources
@mock_ec2
@mock_elbv2
@mock_wafv2
def test__list_resources_for_web_acl__(self):
wafv2 = client("wafv2", region_name="us-east-1")
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
waf = wafv2.create_web_acl(
Scope="REGIONAL",
Name="my-web-acl",
DefaultAction={"Allow": {}},
VisibilityConfig={
"SampledRequestsEnabled": False,
"CloudWatchMetricsEnabled": False,
"MetricName": "idk",
},
)["Summary"]
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
wafv2.associate_web_acl(WebACLArn=waf["ARN"], ResourceArn=lb["LoadBalancerArn"])
# WAFv2 client for this test class
audit_info = self.set_mocked_audit_info()
wafv2 = WAFv2(audit_info)
wafv2.web_acls[0].albs.append(lb["LoadBalancerArn"])
assert len(wafv2.web_acls) == 1
assert len(wafv2.web_acls[0].albs) == 1
assert lb["LoadBalancerArn"] in wafv2.web_acls[0].albs