mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
feat(EC2): add EC2 tests and checks (#1482)
Co-authored-by: sergargar <sergio@verica.io>
This commit is contained in:
@@ -1,62 +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_extra7102="7.102"
|
||||
CHECK_TITLE_extra7102="[extra7102] Check if any of the Elastic or Public IP are in Shodan (requires Shodan API KEY)"
|
||||
CHECK_SCORED_extra7102="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7102="EXTRA"
|
||||
CHECK_SEVERITY_extra7102="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7102="AwsEc2Eip"
|
||||
CHECK_ALTERNATE_check7102="extra7102"
|
||||
CHECK_SERVICENAME_extra7102="ec2"
|
||||
CHECK_RISK_extra7102='Sites like Shodan index exposed systems and further expose them to wider audiences as a quick way to find exploitable systems.'
|
||||
CHECK_REMEDIATION_extra7102='Check Identified IPs; consider changing them to private ones and delete them from Shodan.'
|
||||
CHECK_DOC_extra7102='https://www.shodan.io/'
|
||||
CHECK_CAF_EPIC_extra7102='Infrastructure Security'
|
||||
|
||||
# Watch out, always use Shodan API key, if you use `curl https://www.shodan.io/host/{ip}` massively
|
||||
# your IP will be banned by Shodan
|
||||
|
||||
# This is the right way to do so
|
||||
# curl -ks https://api.shodan.io/shodan/host/{ip}?key={YOUR_API_KEY}
|
||||
|
||||
# Each finding will be saved in prowler/output folder for further review.
|
||||
|
||||
extra7102(){
|
||||
if [[ ! $SHODAN_API_KEY ]]; then
|
||||
textInfo "[extra7102] Requires a Shodan API key to work. Use -N <shodan_api_key>"
|
||||
else
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_EIP=$($AWSCLI $PROFILE_OPT --region $regx ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_EIP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe network interfaces" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_EIP ]]; then
|
||||
for ip in $LIST_OF_EIP;do
|
||||
SHODAN_QUERY=$(curl -ks https://api.shodan.io/shodan/host/$ip?key=$SHODAN_API_KEY)
|
||||
# Shodan has a request rate limit of 1 request/second.
|
||||
sleep 1
|
||||
if [[ $SHODAN_QUERY == *"No information available for that IP"* ]]; then
|
||||
textPass "$regx: IP $ip is not listed in Shodan" "$regx"
|
||||
else
|
||||
echo $SHODAN_QUERY > $OUTPUT_DIR/shodan-output-$ip.json
|
||||
IP_SHODAN_INFO=$(cat $OUTPUT_DIR/shodan-output-$ip.json | jq -r '. | { ports: .ports, org: .org, country: .country_name }| @text' | tr -d \"\{\}\}\]\[ | tr , '\ ' )
|
||||
textFail "$regx: IP $ip is listed in Shodan with data $IP_SHODAN_INFO. More info https://www.shodan.io/host/$ip and $OUTPUT_DIR/shodan-output-$ip.json" "$regx" "$ip"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No Public or Elastic IPs found" "$regx"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
@@ -1,41 +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.
|
||||
CHECK_ID_extra7135="7.135"
|
||||
CHECK_TITLE_extra7135="[extra7135] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Kafka port 9092 "
|
||||
CHECK_SCORED_extra7135="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7135="EXTRA"
|
||||
CHECK_SEVERITY_extra7135="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7135="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check7135="extra7135"
|
||||
CHECK_SERVICENAME_extra7135="ec2"
|
||||
CHECK_RISK_extra7135='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra7135='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra7135='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra7135='Infrastructure Security'
|
||||
|
||||
extra7135(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`9092` && ToPort==`9092`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Kafka ports" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Kafka ports" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,41 +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.
|
||||
CHECK_ID_extra7136="7.136"
|
||||
CHECK_TITLE_extra7136="[extra7136] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Telnet port 23 "
|
||||
CHECK_SCORED_extra7136="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7136="EXTRA"
|
||||
CHECK_SEVERITY_extra7136="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7136="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check7136="extra7136"
|
||||
CHECK_SERVICENAME_extra7136="ec2"
|
||||
CHECK_RISK_extra7136='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra7136='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra7136='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra7136='Infrastructure Security'
|
||||
|
||||
extra7136(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`23` && ToPort==`23`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Telnet ports" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Telnet ports" "$regx" "$SG"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,41 +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.
|
||||
CHECK_ID_extra7137="7.137"
|
||||
CHECK_TITLE_extra7137="[extra7137] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Windows SQL Server ports 1433 or 1434 "
|
||||
CHECK_SCORED_extra7137="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7137="EXTRA"
|
||||
CHECK_SEVERITY_extra7137="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7137="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check7137="extra7137"
|
||||
CHECK_SERVICENAME_extra7137="ec2"
|
||||
CHECK_RISK_extra7137='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra7137='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra7137='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra7137='Infrastructure Security'
|
||||
|
||||
extra7137(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`1433` && ToPort==`1434`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Microsoft SQL Server ports" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Microsoft SQL Server ports" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -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_extra7146="7.146"
|
||||
CHECK_TITLE_extra7146="[extra7146] Check if there is any unassigned Elastic IP"
|
||||
CHECK_SCORED_extra7146="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7146="EXTRA"
|
||||
CHECK_SEVERITY_extra7146="Low"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7146="AwsElasticIPs"
|
||||
CHECK_ALTERNATE_check7146="extra7146"
|
||||
CHECK_SERVICENAME_extra7146="ec2"
|
||||
CHECK_RISK_extra7146='Unassigned Elastic IPs may result in extra cost'
|
||||
CHECK_REMEDIATION_extra7146='Ensure Elastic IPs are not unassigned'
|
||||
CHECK_DOC_extra7146='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html'
|
||||
CHECK_CAF_EPIC_extra7146='Infrastructure Security'
|
||||
|
||||
extra7146(){
|
||||
# "Check if there is any unassigned elastic ip (Not Scored) (Not part of CIS benchmark)"
|
||||
for regx in $REGIONS; do
|
||||
ELASTIC_IP_ADDRESSES=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query Addresses --output json 2>&1)
|
||||
if [[ $(echo "$ELASTIC_IP_ADDRESSES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe addresses" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $ELASTIC_IP_ADDRESSES != [] ]]; then
|
||||
LIST_OF_ASSOCIATED_IPS=$(echo $ELASTIC_IP_ADDRESSES | jq -r '.[] | select(.AssociationId!=null) | .PublicIp')
|
||||
LIST_OF_UNASSOCIATED_IPS=$(echo $ELASTIC_IP_ADDRESSES | jq -r '.[] | select(.AssociationId==null) | .PublicIp')
|
||||
for ass_ip in $LIST_OF_ASSOCIATED_IPS; do
|
||||
textPass "$regx: Elastic IP $ass_ip is associated with an instance or network interface" "$regx" "$ass_ip"
|
||||
done
|
||||
for unass_ip in $LIST_OF_UNASSOCIATED_IPS; do
|
||||
textInfo "$regx: Elastic IP $unass_ip is not associated with any instance or network interface and it may incurr extra cost" "$regx" "$unass_ip"
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No Elastic IPs found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,44 +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.
|
||||
|
||||
CHECK_ID_extra7173="7.173"
|
||||
CHECK_TITLE_extra7173="[check7173] Security Groups created by EC2 Launch Wizard"
|
||||
CHECK_SCORED_extra7173="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7173="EXTRA"
|
||||
CHECK_SEVERITY_extra7173="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7173="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_extra7173="extra7173"
|
||||
CHECK_SERVICENAME_extra7173="ec2"
|
||||
CHECK_RISK_cextra7173="Security Groups Created on the AWS Console using the EC2 wizard may allow port 22 from 0.0.0.0/0"
|
||||
CHECK_REMEDIATION_extra7173="Apply Zero Trust approach. Implement a process to scan and remediate security groups created by the EC2 Wizard. Recommended best practices is to use an authorized security group."
|
||||
CHECK_DOC_extra7173="CHECK_DOC_extra7173='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'"
|
||||
CHECK_CAF_EPIC_extra7173="Infrastructure Security"
|
||||
|
||||
extra7173(){
|
||||
# Ensure no security groups are created using Console EC2 Wizard
|
||||
for regx in $REGIONS; do
|
||||
CHECK_SGDEFAULT_IDS=$("${AWSCLI}" ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --filters Name=group-name,Values='launch-wizard-*' --query 'SecurityGroups[*].GroupId[]' --output text 2>&1)
|
||||
if grep -q -E 'AccessDenied|UnauthorizedOperation' <<< "${CHECK_SGDEFAULT_IDS}"; then
|
||||
textInfo "${regx}: Access Denied trying to describe security groups" "${regx}"
|
||||
continue
|
||||
fi
|
||||
if [[ ${CHECK_SGDEFAULT_IDS} ]]; then
|
||||
for CHECK_SGDEFAULT_ID in ${CHECK_SGDEFAULT_IDS}; do
|
||||
SECURITY_GROUP_NAME=$(${AWSCLI} ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --group-ids "${CHECK_SGDEFAULT_ID}" --query 'SecurityGroups[*].GroupName[]' --output text 2>&1)
|
||||
textFail "${regx}: Security Group ${SECURITY_GROUP_NAME} (ID: ${CHECK_SGDEFAULT_ID}) was created using the EC2 Launch Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
|
||||
done
|
||||
else
|
||||
textPass "${regx}: No Security Groups found that were created using the Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -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_extra74="7.4"
|
||||
CHECK_TITLE_extra74="[extra74] Ensure there are no Security Groups without ingress filtering being used"
|
||||
CHECK_SCORED_extra74="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra74="EXTRA"
|
||||
CHECK_SEVERITY_extra74="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra74="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_extra704="extra74"
|
||||
CHECK_ALTERNATE_check74="extra74"
|
||||
CHECK_ALTERNATE_check704="extra74"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra74="ens-mp.com.4.aws.sg.2"
|
||||
CHECK_SERVICENAME_extra74="ec2"
|
||||
CHECK_RISK_extra74='If Security groups are not filtering traffic appropriately the attack surface is increased.'
|
||||
CHECK_REMEDIATION_extra74=' You can grant access to a specific CIDR range; or to another security group in your VPC or in a peer VPC.'
|
||||
CHECK_DOC_extra74='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra74='Infrastructure Security'
|
||||
|
||||
extra74(){
|
||||
# "Ensure there are no Security Groups without ingress filtering being used "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_SECURITYGROUPS=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --filters "Name=ip-permission.cidr,Values=0.0.0.0/0" --query "SecurityGroups[].[GroupId]" --output text --max-items $MAXITEMS 2>&1)
|
||||
if [[ $(echo "$LIST_OF_SECURITYGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
for SG_ID in $LIST_OF_SECURITYGROUPS; do
|
||||
SG_NO_INGRESS_FILTER=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text)
|
||||
if [[ $SG_NO_INGRESS_FILTER -ne 0 ]];then
|
||||
textFail "$regx: $SG_ID has no ingress filtering and it is being used!" "$regx" "$SG_ID"
|
||||
else
|
||||
textInfo "$regx: $SG_ID has no ingress filtering but it is not being used" "$regx" "$SG_ID"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
@@ -1,75 +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.
|
||||
CHECK_ID_extra741="7.41"
|
||||
CHECK_TITLE_extra741="[extra741] Find secrets in EC2 User Data"
|
||||
CHECK_SCORED_extra741="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra741="EXTRA"
|
||||
CHECK_SEVERITY_extra741="Critical"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra741="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check741="extra741"
|
||||
CHECK_SERVICENAME_extra741="ec2"
|
||||
CHECK_RISK_extra741='Secrets hardcoded into instance user data can be used by malware and bad actors to gain lateral access to other services.'
|
||||
CHECK_REMEDIATION_extra741='Implement automated detective control (e.g. using tools like Prowler ) to scan accounts for passwords and secrets. Use secrets manager service to store and retrieve passwords and secrets. '
|
||||
CHECK_DOC_extra741='https://docs.aws.amazon.com/secretsmanager/latest/userguide/tutorials_basic.html'
|
||||
CHECK_CAF_EPIC_extra741='IAM'
|
||||
|
||||
extra741(){
|
||||
SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM"
|
||||
if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
|
||||
# this folder is deleted once this check is finished
|
||||
mkdir $SECRETS_TEMP_FOLDER
|
||||
fi
|
||||
|
||||
for regx in $REGIONS; do
|
||||
CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
|
||||
if [[ $? -eq 241 ]]; then
|
||||
textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
|
||||
else
|
||||
LIST_OF_EC2_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query Reservations[*].Instances[*].InstanceId --output text --max-items $MAXITEMS 2>&1| grep -v None)
|
||||
if [[ $(echo "$LIST_OF_EC2_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe instances" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_EC2_INSTANCES ]];then
|
||||
for instance in $LIST_OF_EC2_INSTANCES; do
|
||||
EC2_USERDATA_FILE="$SECRETS_TEMP_FOLDER/extra741-$instance-userData.decoded"
|
||||
EC2_USERDATA=$($AWSCLI ec2 describe-instance-attribute --attribute userData $PROFILE_OPT --region $regx --instance-id $instance --query UserData.Value --output text| grep -v ^None | decode_report > $EC2_USERDATA_FILE)
|
||||
if [ -s "$EC2_USERDATA_FILE" ];then
|
||||
# This finds ftp or http URLs with credentials and common keywords
|
||||
# FINDINGS=$(egrep -i '[[:alpha:]]*://[[:alnum:]]*:[[:alnum:]]*@.*/|key|secret|token|pass' $EC2_USERDATA_FILE |wc -l|tr -d '\ ')
|
||||
# New implementation using https://github.com/Yelp/detect-secrets
|
||||
# Test if user data is a valid GZIP file, if so gunzip first
|
||||
if gunzip -t "$EC2_USERDATA_FILE" > /dev/null 2>&1; then
|
||||
mv "$EC2_USERDATA_FILE" "$EC2_USERDATA_FILE.gz" ; gunzip "$EC2_USERDATA_FILE.gz"
|
||||
fi
|
||||
FINDINGS=$(secretsDetector file "$EC2_USERDATA_FILE")
|
||||
if [[ $FINDINGS -eq 0 ]]; then
|
||||
textPass "$regx: No secrets found in $instance User Data" "$regx" "$instance"
|
||||
# delete file if nothing interesting is there
|
||||
rm -f "$EC2_USERDATA_FILE"
|
||||
else
|
||||
textFail "$regx: Potential secret found in $instance User Data" "$regx" "$instance"
|
||||
# delete file to not leave trace, user must look at the instance User Data
|
||||
rm -f "$EC2_USERDATA_FILE"
|
||||
fi
|
||||
else
|
||||
textPass "$regx: No secrets found in $instance User Data or it is empty" "$regx" "$instance"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No EC2 instances found" "$regx"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
rm -rf $SECRETS_TEMP_FOLDER
|
||||
}
|
||||
@@ -1,59 +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_extra75="7.5"
|
||||
CHECK_TITLE_extra75="[extra75] Ensure there are no Security Groups not being used"
|
||||
CHECK_SCORED_extra75="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra75="EXTRA"
|
||||
CHECK_SEVERITY_extra75="Informational"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra75="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_extra705="extra75"
|
||||
CHECK_ALTERNATE_check75="extra75"
|
||||
CHECK_ALTERNATE_check705="extra75"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra75="ens-mp.com.4.aws.sg.3"
|
||||
CHECK_SERVICENAME_extra75="ec2"
|
||||
CHECK_RISK_extra75='Having clear definition and scope for Security Groups creates a better administration environment.'
|
||||
CHECK_REMEDIATION_extra75='List all the security groups and then use the cli to check if they are attached to an instance.'
|
||||
CHECK_DOC_extra75='https://aws.amazon.com/premiumsupport/knowledge-center/ec2-find-security-group-resources/'
|
||||
CHECK_CAF_EPIC_extra75='Infrastructure Security'
|
||||
|
||||
extra75(){
|
||||
# "Ensure there are no Security Groups not being used "
|
||||
for regx in $REGIONS; do
|
||||
SECURITYGROUPS=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --max-items $MAXITEMS --output json 2>&1 )
|
||||
if [[ $(echo "$SECURITYGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $(echo "$SECURITYGROUPS" | jq '."SecurityGroups" | length') -eq 0 ]]; then
|
||||
textInfo "$regx: No Security Groups found in $regx" "$regx"
|
||||
continue
|
||||
fi
|
||||
SECURITYGROUP_NAMES=$(echo $SECURITYGROUPS | jq '.SecurityGroups|map({(.GroupId): (.GroupName)})|add')
|
||||
LIST_OF_SECURITYGROUPS=$(echo $SECURITYGROUP_NAMES | jq -r 'to_entries|sort_by(.key)|.[]|.key')
|
||||
for SG_ID in $LIST_OF_SECURITYGROUPS; do
|
||||
SG_NOT_USED=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text)
|
||||
# Default security groups can not be deleted, so draw attention to them
|
||||
if [[ $SG_NOT_USED -eq 0 ]];then
|
||||
GROUP_NAME=$(echo $SECURITYGROUP_NAMES | jq -r --arg id $SG_ID '.[$id]')
|
||||
if [[ $GROUP_NAME != "default" ]];
|
||||
then
|
||||
textFail "$regx: $SG_ID is not being used!" "$regx" "$SG_ID"
|
||||
else
|
||||
textInfo "$regx: $SG_ID is not being used - default security group" "$regx" "$SG_ID"
|
||||
fi
|
||||
else
|
||||
textPass "$regx: $SG_ID is being used" "$regx" "$SG_ID"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
@@ -1,42 +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.
|
||||
CHECK_ID_extra751="7.51"
|
||||
CHECK_TITLE_extra751="[extra751] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Postgres port 5432"
|
||||
CHECK_SCORED_extra751="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra751="EXTRA"
|
||||
CHECK_SEVERITY_extra751="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra751="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check751="extra751"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra751="ens-mp.com.4.aws.sg.8"
|
||||
CHECK_SERVICENAME_extra751="ec2"
|
||||
CHECK_RISK_extra751='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra751='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra751='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra751='Infrastructure Security'
|
||||
|
||||
extra751(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`5432` && ToPort>=`5432`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Postgres port" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Postgres port" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,42 +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.
|
||||
CHECK_ID_extra752="7.52"
|
||||
CHECK_TITLE_extra752="[extra752] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Redis port 6379"
|
||||
CHECK_SCORED_extra752="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra752="EXTRA"
|
||||
CHECK_SEVERITY_extra752="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra752="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check752="extra752"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra752="ens-mp.com.4.aws.sg.9"
|
||||
CHECK_SERVICENAME_extra752="ec2"
|
||||
CHECK_RISK_extra752='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra752='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra752='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra752='Infrastructure Security'
|
||||
|
||||
extra752(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`6379` && ToPort>=`6379`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Redis port" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Redis port" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,42 +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.
|
||||
CHECK_ID_extra753="7.53"
|
||||
CHECK_TITLE_extra753="[extra753] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MongoDB ports 27017 and 27018"
|
||||
CHECK_SCORED_extra753="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra753="EXTRA"
|
||||
CHECK_SEVERITY_extra753="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra753="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check753="extra753"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra753="ens-mp.com.4.aws.sg.10"
|
||||
CHECK_SERVICENAME_extra753="ec2"
|
||||
CHECK_RISK_extra753='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra753='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra753='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra753='Infrastructure Security'
|
||||
|
||||
extra753(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || ((FromPort<=`27017` && ToPort>=`27017`) || (FromPort<=`27018` && ToPort>=`27018`))) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for MongoDB ports" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for MongoDB ports" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,42 +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.
|
||||
CHECK_ID_extra754="7.54"
|
||||
CHECK_TITLE_extra754="[extra754] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Cassandra ports 7199 or 9160 or 8888"
|
||||
CHECK_SCORED_extra754="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra754="EXTRA"
|
||||
CHECK_SEVERITY_extra754="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra754="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check754="extra754"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra754="ens-mp.com.4.aws.sg.11"
|
||||
CHECK_SERVICENAME_extra754="ec2"
|
||||
CHECK_RISK_extra754='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra754='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra754='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra754='Infrastructure Security'
|
||||
|
||||
extra754(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || ((FromPort<=`7199` && ToPort>=`7199`) || (FromPort<=`9160` && ToPort>=`9160`)|| (FromPort<=`8888` && ToPort>=`8888`))) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Cassandra ports" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Cassandra ports" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,42 +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.
|
||||
CHECK_ID_extra755="7.55"
|
||||
CHECK_TITLE_extra755="[extra755] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Memcached port 11211"
|
||||
CHECK_SCORED_extra755="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra755="EXTRA"
|
||||
CHECK_SEVERITY_extra755="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra755="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check755="extra755"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra755="ens-mp.com.4.aws.sg.12"
|
||||
CHECK_SERVICENAME_extra755="ec2"
|
||||
CHECK_RISK_extra755='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra755='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra755='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra755='Infrastructure Security'
|
||||
|
||||
extra755(){
|
||||
for regx in $REGIONS; do
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`11211` && ToPort>=`11211`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SG_LIST ]];then
|
||||
for SG in $SG_LIST;do
|
||||
textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0 for Memcached port" "$regx" "$SG"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Memcached port" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,50 +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.
|
||||
CHECK_ID_extra757="7.57"
|
||||
CHECK_TITLE_extra757="[extra757] Check EC2 Instances older than 6 months"
|
||||
CHECK_SCORED_extra757="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra757="EXTRA"
|
||||
CHECK_SEVERITY_extra757="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra757="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check757="extra757"
|
||||
CHECK_SERVICENAME_extra757="ec2"
|
||||
CHECK_RISK_extra757='Having old instances within your AWS account could increase the risk of having vulnerable software.'
|
||||
CHECK_REMEDIATION_extra757='Check if software running in the instance is up to date and patched accordingly. Use AWS Systems Manager to patch instances and view patching compliance information.'
|
||||
CHECK_DOC_extra757='https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-patch-compliance-results.html'
|
||||
CHECK_CAF_EPIC_extra757='Infrastructure Security'
|
||||
|
||||
extra757(){
|
||||
OLDAGE="$(get_date_previous_than_months 6)"
|
||||
for regx in $REGIONS; do
|
||||
EC2_RUNNING=$($AWSCLI ec2 describe-instances --query "Reservations[*].Instances[*].[InstanceId]" $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$EC2_RUNNING" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe instances" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $EC2_RUNNING ]]; then
|
||||
INSTACES_OLD_THAN_AGE=$($AWSCLI ec2 describe-instances --query "Reservations[].Instances[?LaunchTime<='$OLDAGE'][].{id: InstanceId, launched: LaunchTime}" $PROFILE_OPT --region $regx --output text)
|
||||
if [[ $INSTACES_OLD_THAN_AGE ]]; then
|
||||
while IFS= read -r ec2_instace
|
||||
do
|
||||
EC2_ID=$(echo "$ec2_instace" | awk '{print $1}')
|
||||
LAUNCH_DATE=$(echo "$ec2_instace" | awk '{print $2}')
|
||||
textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE" "$regx" "$EC2_ID"
|
||||
done <<< "$INSTACES_OLD_THAN_AGE"
|
||||
else
|
||||
textPass "$regx: All Instances newer than 6 months" "$regx"
|
||||
fi
|
||||
else
|
||||
textInfo "$regx: No EC2 Instances Found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,44 +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.
|
||||
CHECK_ID_extra758="7.58"
|
||||
CHECK_TITLE_extra758="[extra758] Check EC2 Instances older than 12 months "
|
||||
CHECK_SCORED_extra758="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra758="EXTRA"
|
||||
CHECK_SEVERITY_extra758="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra758="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check758="extra758"
|
||||
CHECK_SERVICENAME_extra758="ec2"
|
||||
CHECK_RISK_extra758='Having old instances within your AWS account could increase the risk of having vulnerable software.'
|
||||
CHECK_REMEDIATION_extra758='Check if software running in the instance is up to date and patched accordingly. Use AWS Systems Manager to patch instances and view patching compliance information.'
|
||||
CHECK_DOC_extra758='https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-patch-compliance-results.html'
|
||||
CHECK_CAF_EPIC_extra758='Infrastructure Security'
|
||||
|
||||
extra758(){
|
||||
# OLDAGE has the following format: YYYY-MM-DD
|
||||
OLDAGE="$(get_date_previous_than_months 12)"
|
||||
for regx in ${REGIONS}; do
|
||||
INSTACES_OLD_THAN_AGE=$("${AWSCLI}" ec2 describe-instances --query "Reservations[].Instances[?LaunchTime<='${OLDAGE}'][].[InstanceId, LaunchTime, State.Name]" ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
|
||||
if [[ $(echo "${INSTACES_OLD_THAN_AGE}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "${regx}: Access Denied trying to describe instances" "${regx}"
|
||||
continue
|
||||
fi
|
||||
if [[ "${INSTACES_OLD_THAN_AGE}" ]]; then
|
||||
while read -r EC2_ID LAUNCH_DATE STATE
|
||||
do
|
||||
textFail "${regx}: EC2 Instance ${EC2_ID} launched before than ${OLDAGE}. Launch date: ${LAUNCH_DATE} - State: ${STATE}" "${regx}" "${EC2_ID}"
|
||||
done <<< "${INSTACES_OLD_THAN_AGE}"
|
||||
else
|
||||
textPass "${regx}: No EC2 Instances found older than 12 months" "${regx}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra76="7.6"
|
||||
CHECK_TITLE_extra76="[extra76] Ensure there are no EC2 AMIs set as Public"
|
||||
CHECK_SCORED_extra76="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra76="EXTRA"
|
||||
CHECK_SEVERITY_extra76="Critical"
|
||||
CHECK_ALTERNATE_extra706="extra76"
|
||||
CHECK_ALTERNATE_check76="extra76"
|
||||
CHECK_ALTERNATE_check706="extra76"
|
||||
CHECK_SERVICENAME_extra76="ec2"
|
||||
CHECK_RISK_extra76='A shared AMI is an AMI that a developer created and made available for other developers to use. If AMIs have embebed information about the environment could pose a security risk. You use a shared AMI at your own risk. Amazon can not vouch for the integrity or security of AMIs shared by Amazon EC2 users. '
|
||||
CHECK_REMEDIATION_extra76='List all shared AMIs and make sure there is a business reason for them.'
|
||||
CHECK_DOC_extra76='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/usingsharedamis-finding.html'
|
||||
CHECK_CAF_EPIC_extra76='Infrastructure Security'
|
||||
|
||||
extra76(){
|
||||
# "Ensure there are no EC2 AMIs set as Public "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_PUBLIC_AMIS=$($AWSCLI ec2 describe-images --owners self $PROFILE_OPT --region $regx --filters "Name=is-public,Values=true" --query 'Images[*].{ID:ImageId}' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_PUBLIC_AMIS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe images" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_PUBLIC_AMIS ]];then
|
||||
for ami in $LIST_OF_PUBLIC_AMIS; do
|
||||
textFail "$regx: $ami is currently Public!" "$regx" "$ami"
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Public AMIs found" "$regx" "$ami"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,43 +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.
|
||||
CHECK_ID_extra761="7.61"
|
||||
CHECK_TITLE_extra761="[extra761] Check if EBS Default Encryption is activated "
|
||||
CHECK_SCORED_extra761="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra761="EXTRA"
|
||||
CHECK_SEVERITY_extra761="Medium"
|
||||
CHECK_ALTERNATE_check761="extra761"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra761="ens-mp.info.3.aws.ebs.2"
|
||||
CHECK_SERVICENAME_extra761="ec2"
|
||||
CHECK_RISK_extra761='If not enabled sensitive information at rest is not protected.'
|
||||
CHECK_REMEDIATION_extra761='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
|
||||
CHECK_DOC_extra761='https://aws.amazon.com/premiumsupport/knowledge-center/ebs-automatic-encryption/'
|
||||
CHECK_CAF_EPIC_extra761='Data Protection'
|
||||
|
||||
extra761(){
|
||||
for regx in $REGIONS; do
|
||||
EBS_DEFAULT_ENCRYPTION=$($AWSCLI ec2 get-ebs-encryption-by-default $PROFILE_OPT --region $regx --query 'EbsEncryptionByDefault' --output text 2>&1)
|
||||
if [[ $(echo "$EBS_DEFAULT_ENCRYPTION" | grep "argument operation: Invalid choice") ]]; then
|
||||
textInfo "$regx: Newer aws cli needed for get-ebs-encryption-by-default" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $(echo "$EBS_DEFAULT_ENCRYPTION" | grep UnauthorizedOperation) ]]; then
|
||||
textInfo "$regx: Prowler needs ec2:GetEbsEncryptionByDefault permission for this check" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $EBS_DEFAULT_ENCRYPTION == "True" ]];then
|
||||
textPass "$regx: EBS Default Encryption is activated" "$regx"
|
||||
else
|
||||
textFail "$regx: EBS Default Encryption is not activated" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,45 +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_extra770="7.70"
|
||||
CHECK_TITLE_extra770="[extra770] Check for internet facing EC2 instances with Instance Profiles attached "
|
||||
CHECK_SCORED_extra770="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra770="EXTRA"
|
||||
CHECK_SEVERITY_extra770="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra770="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check770="extra770"
|
||||
CHECK_SERVICENAME_extra770="ec2"
|
||||
CHECK_RISK_extra770='Exposing an EC2 directly to internet increases the attack surface and therefore the risk of compromise.'
|
||||
CHECK_REMEDIATION_extra770='Use an ALB and apply WAF ACL.'
|
||||
CHECK_DOC_extra770='https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/'
|
||||
CHECK_CAF_EPIC_extra770='Infrastructure Security'
|
||||
|
||||
extra770(){
|
||||
# "Check for internet facing EC2 Instances "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?((IamInstanceProfile!=`null` && PublicIpAddress!=`null`))].[InstanceId,PublicIpAddress,IamInstanceProfile.Arn]' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe instances" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES ]];then
|
||||
while read -r instance;do
|
||||
INSTANCE_ID=$(echo $instance | awk '{ print $1; }')
|
||||
PUBLIC_IP=$(echo $instance | awk '{ print $2; }')
|
||||
INSTANCE_PROFILE=$(echo $instance | awk '{ print $3; }')
|
||||
textFail "$regx: Instance: $INSTANCE_ID at IP: $PUBLIC_IP is internet-facing with Instance Profile $INSTANCE_PROFILE" "$regx" "$INSTANCE_ID"
|
||||
done <<< "$LIST_OF_PUBLIC_INSTANCES_WITH_INSTANCE_PROFILES"
|
||||
else
|
||||
textPass "$regx: no Internet Facing EC2 Instances with Instance Profiles found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -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_extra772="7.72"
|
||||
CHECK_TITLE_extra772="[extra772] Check if elastic IPs are unused "
|
||||
CHECK_SCORED_extra772="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra772="EXTRA"
|
||||
CHECK_SEVERITY_extra772="Low"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra772="AwsEc2Eip"
|
||||
CHECK_ALTERNATE_check772="extra772"
|
||||
CHECK_SERVICENAME_extra772="ec2"
|
||||
CHECK_RISK_extra772='You are charged by the hour for each Elastic IP address that are not attached to an EC2 instance .'
|
||||
CHECK_REMEDIATION_extra772='If you don’t need an Elastic IP address; you can stop the charges by releasing the IP address.'
|
||||
CHECK_DOC_extra772='https://aws.amazon.com/premiumsupport/knowledge-center/elastic-ip-charges/'
|
||||
CHECK_CAF_EPIC_extra772='Infrastructure Security'
|
||||
|
||||
extra772(){
|
||||
for region in $REGIONS; do
|
||||
EIP_DUMP=$($AWSCLI ec2 describe-addresses ${PROFILE_OPT} --region $region --output json 2>&1)
|
||||
if [[ $(echo "$EIP_DUMP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe addresses" "$regx"
|
||||
continue
|
||||
fi
|
||||
EIP_LIST=$(echo $EIP_DUMP | jq -r '.Addresses[].AllocationId')
|
||||
if [[ $EIP_LIST ]]; then
|
||||
for eip in $EIP_LIST; do
|
||||
ASSOCIATION_ID=$(echo $EIP_DUMP | jq -r --arg i "$eip" '.Addresses[]|select(.AllocationId==$i)|.AssociationId')
|
||||
if [[ "$ASSOCIATION_ID" == "null" ]]; then
|
||||
textFail "$region: EIP $eip is unused" "$region" "$eip"
|
||||
else
|
||||
textPass "$region: EIP $eip is used" "$region" "$eip"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$region: No Elastic IPs found" $region
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2020) 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.
|
||||
|
||||
# Current VPC Limit is 120 rules (60 inbound and 60 outbound)
|
||||
# Reference: https://docs.aws.amazon.com/vpc/latest/userguide/amazon-vpc-limits.html
|
||||
|
||||
CHECK_ID_extra777="7.77"
|
||||
CHECK_TITLE_extra777="[extra777] Find VPC security groups with more than 50 ingress or egress rules "
|
||||
CHECK_SCORED_extra777="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra777="EXTRA"
|
||||
CHECK_SEVERITY_extra777="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra777="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check777="extra777"
|
||||
CHECK_SERVICENAME_extra777="ec2"
|
||||
CHECK_RISK_extra777='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra777='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra777='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra777='Infrastructure Security'
|
||||
|
||||
extra777(){
|
||||
THRESHOLD=50
|
||||
|
||||
for regx in ${REGIONS}; do
|
||||
SECURITY_GROUP_IDS=$(${AWSCLI} ec2 describe-security-groups \
|
||||
${PROFILE_OPT} \
|
||||
--region ${regx} \
|
||||
--query 'SecurityGroups[*].GroupId' \
|
||||
--output text 2>&1| xargs )
|
||||
if [[ $(echo "$SECURITY_GROUP_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
|
||||
for SECURITY_GROUP in ${SECURITY_GROUP_IDS}; do
|
||||
|
||||
INGRESS_TOTAL=$(${AWSCLI} ec2 describe-security-groups \
|
||||
${PROFILE_OPT} \
|
||||
--filter "Name=group-id,Values=${SECURITY_GROUP}" \
|
||||
--query "SecurityGroups[*].IpPermissions[*].IpRanges" \
|
||||
--region ${regx} \
|
||||
--output text | wc -l | xargs
|
||||
)
|
||||
|
||||
EGRESS_TOTAL=$(${AWSCLI} ec2 describe-security-groups \
|
||||
${PROFILE_OPT} \
|
||||
--filter "Name=group-id,Values=${SECURITY_GROUP}" \
|
||||
--query "SecurityGroups[*].IpPermissionsEgress[*].IpRanges" \
|
||||
--region ${regx} \
|
||||
--output text | wc -l | xargs
|
||||
)
|
||||
|
||||
if [[ (${INGRESS_TOTAL} -ge ${THRESHOLD}) || (${EGRESS_TOTAL} -ge ${THRESHOLD}) ]]; then
|
||||
textFail "${regx}: ${SECURITY_GROUP} has ${INGRESS_TOTAL} inbound rules and ${EGRESS_TOTAL} outbound rules" "${regx}" "${SECURITY_GROUP}"
|
||||
else
|
||||
textPass "${regx}: ${SECURITY_GROUP} has ${INGRESS_TOTAL} inbound rules and ${EGRESS_TOTAL} outbound rules" "${regx}" "${SECURITY_GROUP}"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
@@ -1,86 +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.
|
||||
|
||||
CHECK_ID_extra778="7.78"
|
||||
CHECK_TITLE_extra778="[extra778] Find VPC security groups with wide-open public IPv4 CIDR ranges (non-RFC1918) "
|
||||
CHECK_SCORED_extra778="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra778="EXTRA"
|
||||
CHECK_SEVERITY_extra778="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra778="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check778="extra778"
|
||||
CHECK_SERVICENAME_extra778="ec2"
|
||||
CHECK_RISK_extra778='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra778='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra778='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra778='Infrastructure Security'
|
||||
|
||||
extra778(){
|
||||
CIDR_THRESHOLD=24
|
||||
RFC1918_REGEX="(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)"
|
||||
|
||||
check_cidr() {
|
||||
local SECURITY_GROUP=$1
|
||||
local DIRECTION=$2
|
||||
local DIRECTION_FILTER=""
|
||||
local REGION=$3
|
||||
|
||||
case ${DIRECTION} in
|
||||
"inbound")
|
||||
DIRECTION_FILTER="IpPermissions"
|
||||
;;
|
||||
"outbound")
|
||||
DIRECTION_FILTER="IpPermissionsEgress"
|
||||
;;
|
||||
esac
|
||||
|
||||
CIDR_IP_LIST=$(${AWSCLI} ec2 describe-security-groups \
|
||||
${PROFILE_OPT} \
|
||||
--filter "Name=group-id,Values=${SECURITY_GROUP}" \
|
||||
--query "SecurityGroups[*].${DIRECTION_FILTER}[*].IpRanges[*].CidrIp" \
|
||||
--region ${REGION} \
|
||||
--output text 2>&1| xargs )
|
||||
if [[ $(echo "$CIDR_IP_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
|
||||
for CIDR_IP in ${CIDR_IP_LIST}; do
|
||||
if [[ ! ${CIDR_IP} =~ ${RFC1918_REGEX} ]]; then
|
||||
CIDR=$(echo ${CIDR_IP} | cut -d"/" -f2 | xargs)
|
||||
|
||||
# Edge case "0.0.0.0/0" for RDP and SSH are checked already by check41 and check42
|
||||
if [[ ${CIDR} < ${CIDR_THRESHOLD} && 0 < ${CIDR} ]]; then
|
||||
textFail "${REGION}: ${SECURITY_GROUP} has potential wide-open non-RFC1918 address ${CIDR_IP} in ${DIRECTION} rule" "${REGION}" "${SECURITY_GROUP}"
|
||||
else
|
||||
textPass "${REGION}: ${SECURITY_GROUP} has no potential wide-open non-RFC1918 address" "${REGION}" "${SECURITY_GROUP}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
for regx in ${REGIONS}; do
|
||||
SECURITY_GROUP_IDS=$(${AWSCLI} ec2 describe-security-groups \
|
||||
${PROFILE_OPT} \
|
||||
--region ${regx} \
|
||||
--query 'SecurityGroups[*].GroupId' \
|
||||
--output text 2>&1| xargs )
|
||||
if [[ $(echo "$SECURITY_GROUP_IDS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
for SECURITY_GROUP in ${SECURITY_GROUP_IDS}; do
|
||||
check_cidr "${SECURITY_GROUP}" "inbound" "${regx}"
|
||||
check_cidr "${SECURITY_GROUP}" "outbound" "${regx}"
|
||||
done
|
||||
done
|
||||
}
|
||||
@@ -1,62 +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.
|
||||
CHECK_ID_extra779="7.79"
|
||||
CHECK_TITLE_extra779="[extra779] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Elasticsearch/Kibana ports"
|
||||
CHECK_SCORED_extra779="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra779="EXTRA"
|
||||
CHECK_SEVERITY_extra779="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra779="AwsEc2SecurityGroup"
|
||||
CHECK_ALTERNATE_check779="extra779"
|
||||
CHECK_SERVICENAME_extra779="ec2"
|
||||
CHECK_RISK_extra779='If Security groups are not properly configured the attack surface is increased. '
|
||||
CHECK_REMEDIATION_extra779='Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.'
|
||||
CHECK_DOC_extra779='https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'
|
||||
CHECK_CAF_EPIC_extra779='Infrastructure Security'
|
||||
|
||||
extra779(){
|
||||
ES_API_PORT="9200"
|
||||
ES_DATA_PORT="9300"
|
||||
ES_KIBANA_PORT="5601"
|
||||
# Test connectivity and authentication is performed by check extra787
|
||||
for regx in $REGIONS; do
|
||||
# crate a list of SG open to the world with port $ES_API_PORT or $ES_DATA_PORT or $ES_KIBANA_PORT
|
||||
SG_LIST=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --output text \
|
||||
--query "SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=\`$ES_API_PORT\` && ToPort>=\`$ES_API_PORT\`) || (FromPort<=\`$ES_DATA_PORT\` && ToPort>=\`$ES_DATA_PORT\`) || (FromPort<=\`$ES_KIBANA_PORT\` && ToPort>=\`$ES_KIBANA_PORT\`)) && (contains(IpRanges[].CidrIp, \`0.0.0.0/0\`) || contains(Ipv6Ranges[].CidrIpv6, \`::/0\`))]) > \`0\`].{GroupId:GroupId}" 2>&1)
|
||||
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
# in case of open security groups goes through each one
|
||||
if [[ $SG_LIST ]];then
|
||||
for sg in $SG_LIST;do
|
||||
# temp file store the list of instances IDs and public IP address if found
|
||||
TEMP_EXTRA779_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.EXTRA779.XXXXXXXXXX)
|
||||
# finds instances with that open security group attached and get its public ip address (if it has one)
|
||||
$AWSCLI $PROFILE_OPT --region $regx ec2 describe-instances --filters Name=instance.group-id,Values=$sg --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress]' --output text > $TEMP_EXTRA779_FILE
|
||||
# in case of exposed instances it does access checks
|
||||
if [[ -s "$TEMP_EXTRA779_FILE" ]];then
|
||||
while read instance eip ; do
|
||||
if [[ "$eip" == "None" ]];then
|
||||
textInfo "$regx: Found instance $instance with private IP on Security Group: $sg" "$regx"
|
||||
else
|
||||
textFail "$regx: Found instance $instance with public IP $eip on Security Group: $sg open to 0.0.0.0/0 on for Elasticsearch/Kibana ports - use extra787 to test AUTH" "$regx" "$sg"
|
||||
fi
|
||||
done < <(cat $TEMP_EXTRA779_FILE)
|
||||
fi
|
||||
rm -rf $TEMP_EXTRA779_FILE
|
||||
done
|
||||
else
|
||||
textPass "$regx: No Security Groups found open to 0.0.0.0/0 for Elasticsearch/Kibana ports" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2020) 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_extra786="7.86"
|
||||
CHECK_TITLE_extra786="[extra786] Check if EC2 Instance Metadata Service Version 2 (IMDSv2) is Enabled and Required "
|
||||
CHECK_SCORED_extra786="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra786="EXTRA"
|
||||
CHECK_SEVERITY_extra786="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra786="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check786="extra786"
|
||||
CHECK_SERVICENAME_extra786="ec2"
|
||||
CHECK_RISK_extra786='Using IMDSv2 will protect from misconfiguration and SSRF vulnerabilities. IMDSv1 will not.'
|
||||
CHECK_REMEDIATION_extra786='If you don’t need IMDS you can turn it off. Using aws-cli you can force the instance to use only IMDSv2.'
|
||||
CHECK_DOC_extra786='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#configuring-instance-metadata-options'
|
||||
CHECK_CAF_EPIC_extra786='Infrastructure Security'
|
||||
|
||||
extra786(){
|
||||
for regx in $REGIONS; do
|
||||
TEMP_EXTRA786_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.EXTRA786.XXXXXXXXXX)
|
||||
$AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx \
|
||||
--query 'Reservations[*].Instances[*].{HttpTokens:MetadataOptions.HttpTokens,HttpEndpoint:MetadataOptions.HttpEndpoint,InstanceId:InstanceId}' \
|
||||
--output text --max-items $MAXITEMS > $TEMP_EXTRA786_FILE 2>&1
|
||||
if [[ $(cat $TEMP_EXTRA786_FILE | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe instances" "$regx"
|
||||
continue
|
||||
fi
|
||||
# if the file contains data, there are instances in that region
|
||||
if [[ -s "$TEMP_EXTRA786_FILE" ]];then
|
||||
# here we read content from the file fields instanceid httptokens_status httpendpoint
|
||||
while read httpendpoint httptokens_status instanceid ; do
|
||||
#echo i:$instanceid tok:$httptokens_status end:$httpendpoint
|
||||
if [[ "$httpendpoint" == "enabled" && "$httptokens_status" == "required" ]];then
|
||||
textPass "$regx: EC2 Instance $instanceid has IMDSv2 enabled and required" "$regx" "$instanceid"
|
||||
elif [[ "$httpendpoint" == "disabled" ]];then
|
||||
textInfo "$regx: EC2 Instance $instanceid has HTTP endpoint access to metadata service disabled" "$regx"
|
||||
else
|
||||
textFail "$regx: EC2 Instance $instanceid has IMDSv2 disabled or not required" "$regx" "$instanceid"
|
||||
fi
|
||||
done < <(cat $TEMP_EXTRA786_FILE)
|
||||
else
|
||||
textInfo "$regx: no EC2 Instances found" "$regx"
|
||||
fi
|
||||
rm -fr $TEMP_EXTRA786_FILE
|
||||
done
|
||||
}
|
||||
|
||||
# Remediation:
|
||||
|
||||
# aws ec2 modify-instance-metadata-options \
|
||||
# --instance-id i-1234567898abcdef0 \
|
||||
# --http-tokens required \
|
||||
# --http-endpoint enabled
|
||||
|
||||
# More information here https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_ami_public",
|
||||
"CheckTitle": "Ensure there are no EC2 AMIs set as Public.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "ami",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure there are no EC2 AMIs set as Public.",
|
||||
"Risk": "A shared AMI is an AMI that a developer created and made available for other developers to use. If AMIs have embebed information about the environment could pose a security risk. You use a shared AMI at your own risk. Amazon can not vouch for the integrity or security of AMIs shared by Amazon EC2 users.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/public_8#cli-command",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.bridgecrew.io/docs/public_8#aws-console",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "List all shared AMIs and make sure there is a business reason for them.",
|
||||
"Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/usingsharedamis-finding.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
21
providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py
Normal file
21
providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_ami_public(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for image in ec2_client.images:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = image.region
|
||||
report.resource_id = image.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 AMI {image.id} is not public."
|
||||
if image.public:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 AMI {image.id} is currently public."
|
||||
report.resource_id = image.id
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
110
providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py
Normal file
110
providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_ami_public:
|
||||
@mock_ec2
|
||||
def test_no_amis(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ami_public.ec2_ami_public.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ami_public.ec2_ami_public import (
|
||||
ec2_ami_public,
|
||||
)
|
||||
|
||||
check = ec2_ami_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_one_private_ami(self):
|
||||
|
||||
ec2 = client("ec2", region_name="us-east-1")
|
||||
|
||||
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
||||
instance = reservation["Instances"][0]
|
||||
instance_id = instance["InstanceId"]
|
||||
|
||||
image_id = ec2.create_image(
|
||||
InstanceId=instance_id, Name="test-ami", Description="this is a test ami"
|
||||
)["ImageId"]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ami_public.ec2_ami_public.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_ami_public.ec2_ami_public import (
|
||||
ec2_ami_public,
|
||||
)
|
||||
|
||||
check = ec2_ami_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status_extended == f"EC2 AMI {image_id} is not public."
|
||||
assert result[0].resource_id == image_id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_public_ami(self):
|
||||
|
||||
ec2 = client("ec2", region_name="us-east-1")
|
||||
|
||||
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
||||
instance = reservation["Instances"][0]
|
||||
instance_id = instance["InstanceId"]
|
||||
|
||||
image_id = ec2.create_image(
|
||||
InstanceId=instance_id, Name="test-ami", Description="this is a test ami"
|
||||
)["ImageId"]
|
||||
|
||||
image = resource("ec2", region_name="us-east-1").Image(image_id)
|
||||
ADD_GROUP_ARGS = {
|
||||
"ImageId": image_id,
|
||||
"Attribute": "launchPermission",
|
||||
"OperationType": "add",
|
||||
"UserGroups": ["all"],
|
||||
}
|
||||
image.modify_attribute(**ADD_GROUP_ARGS)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ami_public.ec2_ami_public.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_ami_public.ec2_ami_public import (
|
||||
ec2_ami_public,
|
||||
)
|
||||
|
||||
check = ec2_ami_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended == f"EC2 AMI {image_id} is currently public."
|
||||
)
|
||||
assert result[0].resource_id == image_id
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_ebs_default_encryption",
|
||||
"CheckTitle": "Check if EBS Default Encryption is activated.",
|
||||
"CheckType": ["Data Protection"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "ebs",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Other",
|
||||
"Description": "Check if EBS Default Encryption is activated.",
|
||||
"Risk": "If not enabled sensitive information at rest is not protected.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ec2 enable-ebs-encryption-by-default",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.bridgecrew.io/docs/ensure-ebs-default-encryption-is-enabled#aws-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/ensure-ebs-default-encryption-is-enabled#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.",
|
||||
"Url": "https://aws.amazon.com/premiumsupport/knowledge-center/ebs-automatic-encryption/"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_ebs_default_encryption(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for ebs_encryption in ec2_client.ebs_encryption_by_default:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = ebs_encryption.region
|
||||
report.resource_id = "EBS Default Encryption"
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "EBS Default Encryption is not activated."
|
||||
if ebs_encryption.status:
|
||||
report.status = "PASS"
|
||||
report.status_extended = "EBS Default Encryption is activated."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,71 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_ebs_default_encryption:
|
||||
@mock_ec2
|
||||
def test_ec2_ebs_encryption_enabled(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.enable_ebs_encryption_by_default()
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption import (
|
||||
ec2_ebs_default_encryption,
|
||||
)
|
||||
|
||||
check = ec2_ebs_default_encryption()
|
||||
results = check.execute()
|
||||
|
||||
# One result per region
|
||||
assert len(results) == 23
|
||||
for result in results:
|
||||
if result.region == AWS_REGION:
|
||||
assert result.status == "PASS"
|
||||
assert search(
|
||||
"EBS Default Encryption is activated",
|
||||
result.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_ebs_encryption_disabled(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption import (
|
||||
ec2_ebs_default_encryption,
|
||||
)
|
||||
|
||||
check = ec2_ebs_default_encryption()
|
||||
result = check.execute()
|
||||
|
||||
# One result per region
|
||||
assert len(result) == 23
|
||||
assert result[0].status == "FAIL"
|
||||
assert search(
|
||||
"EBS Default Encryption is not activated",
|
||||
result[0].status_extended,
|
||||
)
|
||||
@@ -13,9 +13,9 @@
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"CLI": "https://docs.bridgecrew.io/docs/public_7#cli-command",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Other": "https://docs.bridgecrew.io/docs/public_7#aws-console",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
|
||||
@@ -10,12 +10,12 @@ class ec2_ebs_public_snapshot(Check):
|
||||
report.region = snapshot.region
|
||||
if not snapshot.public:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is not Public"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is not Public."
|
||||
report.resource_id = snapshot.id
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"EBS Snapshot {snapshot.id} is currently Public"
|
||||
f"EBS Snapshot {snapshot.id} is currently Public."
|
||||
)
|
||||
report.resource_id = snapshot.id
|
||||
findings.append(report)
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_ebs_public_snapshot:
|
||||
@mock_ec2
|
||||
def test_ec2_default_snapshots(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot import (
|
||||
ec2_ebs_public_snapshot,
|
||||
)
|
||||
|
||||
check = ec2_ebs_public_snapshot()
|
||||
result = check.execute()
|
||||
|
||||
# Default snapshots
|
||||
assert len(result) == 1345
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_public_snapshot(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
volume = ec2.create_volume(Size=80, AvailabilityZone=f"{AWS_REGION}a")
|
||||
snapshot = volume.create_snapshot(Description="testsnap")
|
||||
ec2_client.modify_snapshot_attribute(
|
||||
SnapshotId=snapshot.id,
|
||||
Attribute="createVolumePermission",
|
||||
OperationType="add",
|
||||
GroupNames=["all"],
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot import (
|
||||
ec2_ebs_public_snapshot,
|
||||
)
|
||||
|
||||
check = ec2_ebs_public_snapshot()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created
|
||||
assert len(results) == 1346
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.status == "FAIL"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is currently Public."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_private_snapshot(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
snapshot = volume = ec2.create_volume(
|
||||
Size=80, AvailabilityZone=f"{AWS_REGION}a", Encrypted=True
|
||||
)
|
||||
snapshot = volume.create_snapshot(Description="testsnap")
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot import (
|
||||
ec2_ebs_public_snapshot,
|
||||
)
|
||||
|
||||
check = ec2_ebs_public_snapshot()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created
|
||||
assert len(results) == 1346
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.status == "PASS"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is not Public."
|
||||
)
|
||||
@@ -13,10 +13,10 @@
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
"CLI": "aws ec2 --region <REGION> enable-ebs-encryption-by-default",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/general_3-encrypt-eps-volume#cloudformation",
|
||||
"Other": "https://docs.bridgecrew.io/docs/general_3-encrypt-eps-volume#aws-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/general_3-encrypt-eps-volume#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Encrypt all EBS Snapshot and Enable Encryption by default. You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.",
|
||||
|
||||
@@ -10,11 +10,11 @@ class ec2_ebs_snapshots_encrypted(Check):
|
||||
report.region = snapshot.region
|
||||
if snapshot.encrypted:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is encrypted"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is encrypted."
|
||||
report.resource_id = snapshot.id
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is unencrypted"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is unencrypted."
|
||||
report.resource_id = snapshot.id
|
||||
findings.append(report)
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_ebs_snapshots_encrypted:
|
||||
@mock_ec2
|
||||
def test_ec2_default_snapshots(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted import (
|
||||
ec2_ebs_snapshots_encrypted,
|
||||
)
|
||||
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
result = check.execute()
|
||||
|
||||
# Default snapshots
|
||||
assert len(result) == 1345
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_unencrypted_snapshot(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
volume = ec2.create_volume(Size=80, AvailabilityZone=f"{AWS_REGION}a")
|
||||
snapshot = volume.create_snapshot(Description="testsnap")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted import (
|
||||
ec2_ebs_snapshots_encrypted,
|
||||
)
|
||||
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created
|
||||
assert len(results) == 1346
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.status == "FAIL"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is unencrypted."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_encrypted_snapshot(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
snapshot = volume = ec2.create_volume(
|
||||
Size=80, AvailabilityZone=f"{AWS_REGION}a", Encrypted=True
|
||||
)
|
||||
snapshot = volume.create_snapshot(Description="testsnap")
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_ebs_snapshots_encrypted.ec2_ebs_snapshots_encrypted import (
|
||||
ec2_ebs_snapshots_encrypted,
|
||||
)
|
||||
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created
|
||||
assert len(results) == 1346
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.status == "PASS"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is encrypted."
|
||||
)
|
||||
@@ -21,6 +21,7 @@ class ec2_elastic_ip_shodan(Check):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Elastic IP {eip.public_ip} listed in Shodan with open ports {str(shodan_info['ports'])} and ISP {shodan_info['isp']} in {shodan_info['country_name']}. More info https://www.shodan.io/host/{eip.public_ip}"
|
||||
report.resource_id = eip.public_ip
|
||||
findings.append(report)
|
||||
except shodan.APIError as error:
|
||||
if "No information available for that IP" in error.value:
|
||||
report.status = "PASS"
|
||||
@@ -32,13 +33,7 @@ class ec2_elastic_ip_shodan(Check):
|
||||
continue
|
||||
else:
|
||||
logger.error(f"Unknown Shodan API Error: {error.value}")
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Elastic IP {eip.public_ip} has not a Public IP."
|
||||
)
|
||||
report.resource_id = eip.public_ip
|
||||
findings.append(report)
|
||||
|
||||
else:
|
||||
logger.error(
|
||||
"ERROR: No Shodan API Key -- Please input a Shodan API Key with -N/--shodan or in config.yaml"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_elastic_ip_unassgined",
|
||||
"CheckTitle": "Check if there is any unassigned Elastic IP.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "AwsElasticIPs",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "low",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Check if there is any unassigned Elastic IP.",
|
||||
"Risk": "Unassigned Elastic IPs may result in extra cost.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ec2 release-address --public-ip <theIPyoudontneed>",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/general_19#cloudformation",
|
||||
"Other": "https://docs.bridgecrew.io/docs/general_19#ec2-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/general_19#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Ensure Elastic IPs are not unassigned.",
|
||||
"Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_elastic_ip_unassgined(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for eip in ec2_client.elastic_ips:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = eip.region
|
||||
if eip.public_ip:
|
||||
report.resource_id = eip.public_ip
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Elastic IP {eip.public_ip} is not associated with an instance or network interface."
|
||||
if eip.association_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Elastic IP {eip.public_ip} is associated with an instance or network interface."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,105 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_elastic_ip_unassgined:
|
||||
@mock_ec2
|
||||
def test_no_eips(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined import (
|
||||
ec2_elastic_ip_unassgined,
|
||||
)
|
||||
|
||||
check = ec2_elastic_ip_unassgined()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_unassociated(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined import (
|
||||
ec2_elastic_ip_unassgined,
|
||||
)
|
||||
|
||||
check = ec2_elastic_ip_unassgined()
|
||||
results = check.execute()
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status == "FAIL"
|
||||
assert search(
|
||||
"is not associated",
|
||||
results[0].status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_eip_associated(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_resource = resource("ec2", region_name=AWS_REGION)
|
||||
|
||||
reservation = ec2_client.run_instances(
|
||||
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
|
||||
)
|
||||
instance = ec2_resource.Instance(reservation["Instances"][0]["InstanceId"])
|
||||
|
||||
eip = ec2_client.allocate_address(Domain="vpc")
|
||||
|
||||
eip = ec2_resource.VpcAddress(eip["AllocationId"])
|
||||
|
||||
ec2_client.associate_address(
|
||||
InstanceId=instance.id, AllocationId=eip.allocation_id
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_elastic_ip_unassgined.ec2_elastic_ip_unassgined import (
|
||||
ec2_elastic_ip_unassgined,
|
||||
)
|
||||
|
||||
check = ec2_elastic_ip_unassgined()
|
||||
results = check.execute()
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].status == "PASS"
|
||||
assert search(
|
||||
"is associated",
|
||||
results[0].status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_instance_imdsv2_enabled",
|
||||
"CheckTitle": "Check if EC2 Instance Metadata Service Version 2 (IMDSv2) is Enabled and Required.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEc2Instance",
|
||||
"Description": "Check if EC2 Instance Metadata Service Version 2 (IMDSv2) is Enabled and Required.",
|
||||
"Risk": "Using IMDSv2 will protect from misconfiguration and SSRF vulnerabilities. IMDSv1 will not.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ec2 modify-instance-metadata-options --instance-id <instance-id> --http-tokens required --http-endpoint enabled",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_general_31#cloudformation",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/EC2/require-imds-v2.html",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_general_31#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "If you dont need IMDS you can turn it off. Using aws-cli you can force the instance to use only IMDSv2.",
|
||||
"Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html#configuring-instance-metadata-options"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_instance_imdsv2_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for instance in ec2_client.instances:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"EC2 Instance {instance.id} has IMDSv2 disabled or not required."
|
||||
)
|
||||
if (
|
||||
instance.http_endpoint == "enabled"
|
||||
and instance.http_tokens == "required"
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"EC2 Instance {instance.id} has IMDSv2 enabled and required."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,114 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_instance_imdsv2_enabled:
|
||||
@mock_ec2
|
||||
def test_ec2_no_instances(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled import (
|
||||
ec2_instance_imdsv2_enabled,
|
||||
)
|
||||
|
||||
check = ec2_instance_imdsv2_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_one_compliant_ec2(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
MetadataOptions={
|
||||
"HttpTokens": "required",
|
||||
"HttpEndpoint": "enabled",
|
||||
},
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled import (
|
||||
ec2_instance_imdsv2_enabled,
|
||||
)
|
||||
|
||||
service_client.instances[0].http_endpoint = "enabled"
|
||||
service_client.instances[0].http_tokens = "required"
|
||||
|
||||
check = ec2_instance_imdsv2_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} has IMDSv2 enabled and required",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_uncompliant_ec2(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
MetadataOptions={
|
||||
"HttpTokens": "optional",
|
||||
"HttpEndpoint": "disabled",
|
||||
},
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.ec2.ec2_instance_imdsv2_enabled.ec2_instance_imdsv2_enabled import (
|
||||
ec2_instance_imdsv2_enabled,
|
||||
)
|
||||
|
||||
service_client.instances[0].http_endpoint = "disabled"
|
||||
service_client.instances[0].http_tokens = "optional"
|
||||
|
||||
check = ec2_instance_imdsv2_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} has IMDSv2 disabled or not required",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_instance_internet_facing_with_instance_profile",
|
||||
"CheckTitle": "Check for internet facing EC2 instances with Instance Profiles attached.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEc2Instance",
|
||||
"Description": "Check for internet facing EC2 instances with Instance Profiles attached.",
|
||||
"Risk": "Exposing an EC2 directly to internet increases the attack surface and therefore the risk of compromise.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use an ALB and apply WAF ACL.",
|
||||
"Url": "https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_instance_internet_facing_with_instance_profile(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for instance in ec2_client.instances:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 Instance {instance.id} is not internet facing with an instance profile."
|
||||
if instance.public_ip and instance.instance_profile:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 Instance {instance.id} at IP {instance.public_ip} is internet-facing with Instance Profile {instance.instance_profile}."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,129 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource
|
||||
from moto import mock_ec2, mock_iam
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_instance_internet_facing_with_instance_profile:
|
||||
@mock_ec2
|
||||
def test_ec2_no_instances(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile import (
|
||||
ec2_instance_internet_facing_with_instance_profile,
|
||||
)
|
||||
|
||||
check = ec2_instance_internet_facing_with_instance_profile()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_iam
|
||||
@mock_ec2
|
||||
def test_one_compliant_ec2(self):
|
||||
iam = client("iam", "us-west-1")
|
||||
profile_name = "fake_profile"
|
||||
_ = iam.create_instance_profile(
|
||||
InstanceProfileName=profile_name,
|
||||
)
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
IamInstanceProfile={"Name": profile_name},
|
||||
NetworkInterfaces=[
|
||||
{
|
||||
"DeviceIndex": 0,
|
||||
"SubnetId": subnet.id,
|
||||
"AssociatePublicIpAddress": False,
|
||||
}
|
||||
],
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile import (
|
||||
ec2_instance_internet_facing_with_instance_profile,
|
||||
)
|
||||
|
||||
check = ec2_instance_internet_facing_with_instance_profile()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} is not internet facing with an instance profile",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_iam
|
||||
@mock_ec2
|
||||
def test_one_non_compliant_ec2(self):
|
||||
iam = client("iam", "us-west-1")
|
||||
profile_name = "fake_profile"
|
||||
_ = iam.create_instance_profile(
|
||||
InstanceProfileName=profile_name,
|
||||
)
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
IamInstanceProfile={"Name": profile_name},
|
||||
NetworkInterfaces=[
|
||||
{
|
||||
"DeviceIndex": 0,
|
||||
"SubnetId": subnet.id,
|
||||
"AssociatePublicIpAddress": True,
|
||||
}
|
||||
],
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_internet_facing_with_instance_profile.ec2_instance_internet_facing_with_instance_profile import (
|
||||
ec2_instance_internet_facing_with_instance_profile,
|
||||
)
|
||||
|
||||
check = ec2_instance_internet_facing_with_instance_profile()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert search(
|
||||
"is internet-facing with Instance Profile", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_instance_older_than_specific_days",
|
||||
"CheckTitle": "Check EC2 Instances older than specific days.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEc2Instance",
|
||||
"Description": "Check EC2 Instances older than specific days.",
|
||||
"Risk": "Having old instances within your AWS account could increase the risk of having vulnerable software.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/EC2/ec2-instance-too-old.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Check if software running in the instance is up to date and patched accordingly. Use AWS Systems Manager to patch instances and view patching compliance information.",
|
||||
"Url": "https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-patch-compliance-results.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from config.config import get_config_var
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_instance_older_than_specific_days(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
max_ec2_instance_age_in_days = get_config_var("max_ec2_instance_age_in_days")
|
||||
for instance in ec2_client.instances:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 Instance {instance.id} is not running."
|
||||
if instance.state == "running":
|
||||
time_since_launch = (
|
||||
datetime.now().replace(tzinfo=timezone.utc) - instance.launch_time
|
||||
)
|
||||
report.status_extended = f"EC2 Instance {instance.id} is not older than {max_ec2_instance_age_in_days} days ({time_since_launch.days} days)."
|
||||
if time_since_launch.days > max_ec2_instance_age_in_days:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 Instance {instance.id} is older than {max_ec2_instance_age_in_days} days ({time_since_launch.days} days)."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,106 @@
|
||||
import datetime
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
from dateutil.tz import tzutc
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_instance_older_than_specific_days:
|
||||
@mock_ec2
|
||||
def test_ec2_no_instances(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days import (
|
||||
ec2_instance_older_than_specific_days,
|
||||
)
|
||||
|
||||
check = ec2_instance_older_than_specific_days()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_one_compliant_ec2(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
UserData="This is some user_data",
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days import (
|
||||
ec2_instance_older_than_specific_days,
|
||||
)
|
||||
|
||||
check = ec2_instance_older_than_specific_days()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} is not older", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_old_ec2(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
UserData="This is some user_data",
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.ec2.ec2_instance_older_than_specific_days.ec2_instance_older_than_specific_days import (
|
||||
ec2_instance_older_than_specific_days,
|
||||
)
|
||||
|
||||
service_client.instances[0].launch_time = datetime.datetime(
|
||||
2021, 11, 1, 17, 18, tzinfo=tzutc()
|
||||
)
|
||||
|
||||
check = ec2_instance_older_than_specific_days()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} is older", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
@@ -14,9 +14,9 @@
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/public_12#cloudformation",
|
||||
"Other": "https://docs.bridgecrew.io/docs/public_12#aws-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/public_12#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use an ALB and apply WAF ACL.",
|
||||
|
||||
@@ -10,12 +10,12 @@ class ec2_instance_public_ip(Check):
|
||||
report.region = instance.region
|
||||
if instance.public_ip:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
|
||||
report.status_extended = f"EC2 Instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
|
||||
report.resource_id = instance.id
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"EC2 instance {instance.id} has not a Public IP."
|
||||
f"EC2 Instance {instance.id} has not a Public IP."
|
||||
)
|
||||
report.resource_id = instance.id
|
||||
findings.append(report)
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_instance_public_ip:
|
||||
@mock_ec2
|
||||
def test_ec2_no_instances(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip import (
|
||||
ec2_instance_public_ip,
|
||||
)
|
||||
|
||||
check = ec2_instance_public_ip()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_one_compliant_ec2(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
NetworkInterfaces=[
|
||||
{
|
||||
"DeviceIndex": 0,
|
||||
"SubnetId": subnet.id,
|
||||
"AssociatePublicIpAddress": False,
|
||||
}
|
||||
],
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip import (
|
||||
ec2_instance_public_ip,
|
||||
)
|
||||
|
||||
check = ec2_instance_public_ip()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} has not a Public IP",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_ec2_with_public_ip(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
NetworkInterfaces=[
|
||||
{
|
||||
"DeviceIndex": 0,
|
||||
"SubnetId": subnet.id,
|
||||
"AssociatePublicIpAddress": True,
|
||||
}
|
||||
],
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_public_ip.ec2_instance_public_ip import (
|
||||
ec2_instance_public_ip,
|
||||
)
|
||||
|
||||
check = ec2_instance_public_ip()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert search(
|
||||
f"EC2 Instance {instance.id} has a Public IP", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_instance_secrets_user_data",
|
||||
"CheckTitle": "Find secrets in EC2 User Data.",
|
||||
"CheckType": ["IAM"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:access-analyzer:region:account-id:analyzer/resource-id",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsEc2Instance",
|
||||
"Description": "Find secrets in EC2 User Data.",
|
||||
"Risk": "Secrets hardcoded into instance user data can be used by malware and bad actors to gain lateral access to other services.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/bc_aws_secrets_1#cli-command",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_secrets_1#cloudformation",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_secrets_1#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Implement automated detective control (e.g. using tools like Prowler) to scan accounts for passwords and secrets. Use secrets manager service to store and retrieve passwords and secrets.",
|
||||
"Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/tutorials_basic.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
import tempfile
|
||||
from base64 import b64decode
|
||||
|
||||
from detect_secrets import SecretsCollection
|
||||
from detect_secrets.settings import default_settings
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_instance_secrets_user_data(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for instance in ec2_client.instances:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
|
||||
if instance.user_data:
|
||||
temp_user_data_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
user_data = b64decode(instance.user_data).decode("utf-8")
|
||||
|
||||
temp_user_data_file.write(
|
||||
bytes(user_data, encoding="raw_unicode_escape")
|
||||
)
|
||||
temp_user_data_file.close()
|
||||
secrets = SecretsCollection()
|
||||
with default_settings():
|
||||
secrets.scan_file(temp_user_data_file.name)
|
||||
|
||||
if secrets.json():
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential secret found in EC2 instance {instance.id} User Data."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"No secrets found in EC2 instance {instance.id} User Data."
|
||||
)
|
||||
|
||||
os.remove(temp_user_data_file.name)
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"No secrets found in EC2 instance {instance.id} since User Data is empty."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,170 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_instance_secrets_user_data:
|
||||
@mock_ec2
|
||||
def test_no_ec2(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data import (
|
||||
ec2_instance_secrets_user_data,
|
||||
)
|
||||
|
||||
check = ec2_instance_secrets_user_data()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_ec2
|
||||
def test_one_ec2_with_no_secrets(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
UserData="This is some user_data",
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data import (
|
||||
ec2_instance_secrets_user_data,
|
||||
)
|
||||
|
||||
check = ec2_instance_secrets_user_data()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"No secrets found in EC2 instance {instance.id} User Data."
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_ec2_with_secrets(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
UserData="DB_PASSWORD=foobar123",
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data import (
|
||||
ec2_instance_secrets_user_data,
|
||||
)
|
||||
|
||||
check = ec2_instance_secrets_user_data()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Potential secret found in EC2 instance {instance.id} User Data."
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_ec2_file_with_secrets(self):
|
||||
# Include launch_configurations to check
|
||||
f = open(
|
||||
"providers/aws/services/ec2/ec2_instance_secrets_user_data/fixtures/fixture",
|
||||
"r",
|
||||
)
|
||||
secrets = f.read()
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1, UserData=secrets
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data import (
|
||||
ec2_instance_secrets_user_data,
|
||||
)
|
||||
|
||||
check = ec2_instance_secrets_user_data()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Potential secret found in EC2 instance {instance.id} User Data."
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
|
||||
@mock_ec2
|
||||
def test_one_launch_configurations_without_user_data(self):
|
||||
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
instance = ec2.create_instances(
|
||||
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1, UserData=""
|
||||
)[0]
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.ec2.ec2_instance_secrets_user_data.ec2_instance_secrets_user_data import (
|
||||
ec2_instance_secrets_user_data,
|
||||
)
|
||||
|
||||
check = ec2_instance_secrets_user_data()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"No secrets found in EC2 instance {instance.id} since User Data is empty."
|
||||
)
|
||||
assert result[0].resource_id == instance.id
|
||||
@@ -0,0 +1,4 @@
|
||||
DB_PASSWORD=foobar123
|
||||
DB_USER=foo
|
||||
API_KEY=12345abcd
|
||||
SERVICE_PASSWORD=bbaabb45
|
||||
@@ -98,7 +98,6 @@ class Test_ec2_network_acls_allow_ingress_any_port:
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
print(nacl.status)
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "FAIL"
|
||||
assert (
|
||||
@@ -144,7 +143,6 @@ class Test_ec2_network_acls_allow_ingress_any_port:
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
print(nacl.status)
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "PASS"
|
||||
assert (
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_networkacl_allow_ingress_tcp_port_22:
|
||||
@mock_ec2
|
||||
def test_ec2_default_nacls(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default nacl per region
|
||||
assert len(result) == 23
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_default_compliant_nacl(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 23
|
||||
|
||||
# by default nacls are public
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Network ACL {result[0].resource_id} has SSH port 22 open to the Internet."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_nacl(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
||||
nacl_id = ec2_client.create_network_acl(VpcId=vpc_id)["NetworkAcl"][
|
||||
"NetworkAclId"
|
||||
]
|
||||
ec2_client.create_network_acl_entry(
|
||||
NetworkAclId=nacl_id,
|
||||
RuleNumber=100,
|
||||
Protocol="6",
|
||||
PortRange={"From": 22, "To": 22},
|
||||
RuleAction="allow",
|
||||
Egress=False,
|
||||
CidrBlock="0.0.0.0/0",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region + default of new VPC + new NACL
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "FAIL"
|
||||
assert (
|
||||
nacl.status_extended
|
||||
== f"Network ACL {nacl_id} has SSH port 22 open to the Internet."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_nacl(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
||||
nacl_id = ec2_client.create_network_acl(VpcId=vpc_id)["NetworkAcl"][
|
||||
"NetworkAclId"
|
||||
]
|
||||
ec2_client.create_network_acl_entry(
|
||||
NetworkAclId=nacl_id,
|
||||
RuleNumber=100,
|
||||
Protocol="6",
|
||||
PortRange={"From": 22, "To": 22},
|
||||
RuleAction="allow",
|
||||
Egress=False,
|
||||
CidrBlock="10.0.0.2/32",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_22.ec2_networkacl_allow_ingress_tcp_port_22 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region + default of new VPC + new NACL
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "PASS"
|
||||
assert (
|
||||
nacl.status_extended
|
||||
== f"Network ACL {nacl_id} has not SSH port 22 open to the Internet."
|
||||
)
|
||||
@@ -0,0 +1,153 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_networkacl_allow_ingress_tcp_port_3389:
|
||||
@mock_ec2
|
||||
def test_ec2_default_nacls(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default nacl per region
|
||||
assert len(result) == 23
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_default_compliant_nacl(self):
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 23
|
||||
|
||||
# by default nacls are public
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Network ACL {result[0].resource_id} has Microsoft RDP port 3389 open to the Internet."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_nacl(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
||||
nacl_id = ec2_client.create_network_acl(VpcId=vpc_id)["NetworkAcl"][
|
||||
"NetworkAclId"
|
||||
]
|
||||
ec2_client.create_network_acl_entry(
|
||||
NetworkAclId=nacl_id,
|
||||
RuleNumber=100,
|
||||
Protocol="6",
|
||||
PortRange={"From": 3389, "To": 3389},
|
||||
RuleAction="allow",
|
||||
Egress=False,
|
||||
CidrBlock="0.0.0.0/0",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region + default of new VPC + new NACL
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "FAIL"
|
||||
assert (
|
||||
nacl.status_extended
|
||||
== f"Network ACL {nacl_id} has Microsoft RDP port 3389 open to the Internet."
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_nacl(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
||||
nacl_id = ec2_client.create_network_acl(VpcId=vpc_id)["NetworkAcl"][
|
||||
"NetworkAclId"
|
||||
]
|
||||
ec2_client.create_network_acl_entry(
|
||||
NetworkAclId=nacl_id,
|
||||
RuleNumber=100,
|
||||
Protocol="6",
|
||||
PortRange={"From": 3389, "To": 3389},
|
||||
RuleAction="allow",
|
||||
Egress=False,
|
||||
CidrBlock="10.0.0.2/32",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_networkacl_allow_ingress_tcp_port_3389.ec2_networkacl_allow_ingress_tcp_port_3389 import (
|
||||
ec2_networkacl_allow_ingress_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_networkacl_allow_ingress_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region + default of new VPC + new NACL
|
||||
assert len(result) == 25
|
||||
# Search changed sg
|
||||
for nacl in result:
|
||||
if nacl.resource_id == nacl_id:
|
||||
assert nacl.status == "PASS"
|
||||
assert (
|
||||
nacl.status_extended
|
||||
== f"Network ACL {nacl_id} has not Microsoft RDP port 3389 open to the Internet."
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_any_port,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_any_port()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "-1",
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_any_port,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_any_port()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has all ports open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "-1",
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_any_port,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_any_port()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not all ports open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MongoDB ports 27017 and 27018.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to MongoDB ports 27017 and 27018.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [27017, 27018]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MongoDB ports 27017 and 27018 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has MongoDB ports 27017 and 27018 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 27017,
|
||||
"ToPort": 27018,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has MongoDB ports 27017 and 27018 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 27017,
|
||||
"ToPort": 27018,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not MongoDB ports 27017 and 27018 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 20,
|
||||
"ToPort": 21,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has FTP ports 20 and 21 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 20,
|
||||
"ToPort": 21,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not FTP ports 20 and 21 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -13,10 +13,10 @@
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
"CLI": "https://docs.bridgecrew.io/docs/networking_1-port-security#cli-command",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/networking_1-port-security#cloudformation",
|
||||
"Other": "https://docs.bridgecrew.io/docs/networking_1-port-security#aws-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/networking_1-port-security#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
|
||||
@@ -17,7 +17,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22(Check):
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the SSH port 22 open to the Internet."
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has SSH port 22 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 22,
|
||||
"ToPort": 22,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has SSH port 22 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 22,
|
||||
"ToPort": 22,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not SSH port 22 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -13,10 +13,10 @@
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
"CLI": "https://docs.bridgecrew.io/docs/networking_2#cli-command",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/networking_2#cloudformation",
|
||||
"Other": "https://docs.bridgecrew.io/docs/networking_2#aws-console",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/networking_2#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 3389,
|
||||
"ToPort": 3389,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Microsoft RDP port 3389 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 3389,
|
||||
"ToPort": 3389,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389,
|
||||
)
|
||||
|
||||
check = ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389()
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Microsoft RDP port 3389 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Cassandra ports 7199 or 9160 or 8888.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Cassandra ports 7199 or 9160 or 8888.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888(
|
||||
Check
|
||||
):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [7199, 9160, 8888]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Casandra ports 7199, 8888 and 9160 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Casandra ports 7199, 8888 and 9160 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 7199,
|
||||
"ToPort": 9160,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Casandra ports 7199, 8888 and 9160 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 7199,
|
||||
"ToPort": 9160,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Casandra ports 7199, 8888 and 9160 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Elasticsearch/Kibana ports.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Elasticsearch/Kibana ports.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601(
|
||||
Check
|
||||
):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [9200, 9300, 5601]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 5601,
|
||||
"ToPort": 9300,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 5601,
|
||||
"ToPort": 9300,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Kafka port 9092.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Kafka port 9092.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [9092]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Kafka port 9092 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Kafka port 9092 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,138 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 9092,
|
||||
"ToPort": 9092,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Kafka port 9092 open to the Internet", sg.status_extended
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 9092,
|
||||
"ToPort": 9092,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Kafka port 9092 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Memcached port 11211.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Memcached port 11211.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [11211]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Memcached port 11211 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Memcached port 11211 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 11211,
|
||||
"ToPort": 11211,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Memcached port 11211 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 11211,
|
||||
"ToPort": 11211,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Memcached port 11211 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -17,7 +17,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the MySQL port 3306 open to the Internet."
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has MySQL port 3306 open to the Internet."
|
||||
report.resource_id = security_group.id
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 3306,
|
||||
"ToPort": 3306,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has MySQL port 3306 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 3306,
|
||||
"ToPort": 3306,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not MySQL port 3306 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 1521,
|
||||
"ToPort": 2483,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Oracle ports 1521 and 2483 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 1521,
|
||||
"ToPort": 2483,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Oracle ports 1521 and 2483 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432",
|
||||
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Postgres port 5432.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "securitygroup",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2SecurityGroup",
|
||||
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Postgres port 5432.",
|
||||
"Risk": "If Security groups are not properly configured the attack surface is increased.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
|
||||
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
check_ports = [5432]
|
||||
for security_group in ec2_client.security_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Postgres port 5432 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "tcp", check_ports):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Postgres port 5432 open to the Internet."
|
||||
break
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,139 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_ec2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432:
|
||||
@mock_ec2
|
||||
def test_ec2_default_sgs(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# All are compliant by default
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_non_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 5432,
|
||||
"ToPort": 5432,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "FAIL"
|
||||
assert search(
|
||||
"has Postgres port 5432 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
|
||||
@mock_ec2
|
||||
def test_ec2_compliant_default_sg(self):
|
||||
# Create EC2 Mocked Resources
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
default_sg_id = ec2_client.describe_security_groups(GroupNames=["default"])[
|
||||
"SecurityGroups"
|
||||
][0]["GroupId"]
|
||||
ec2_client.authorize_security_group_ingress(
|
||||
GroupId=default_sg_id,
|
||||
IpPermissions=[
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 5432,
|
||||
"ToPort": 5432,
|
||||
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_client",
|
||||
new=EC2(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432 import (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432,
|
||||
)
|
||||
|
||||
check = (
|
||||
ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
# One default sg per region
|
||||
assert len(result) == 24
|
||||
# Search changed sg
|
||||
for sg in result:
|
||||
if sg.resource_id == default_sg_id:
|
||||
assert sg.status == "PASS"
|
||||
assert search(
|
||||
"has not Postgres port 5432 open to the Internet",
|
||||
sg.status_extended,
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user