diff --git a/providers/aws/config.yaml b/providers/aws/config.yaml index cb1cb280..1add8a98 100644 --- a/providers/aws/config.yaml +++ b/providers/aws/config.yaml @@ -1,6 +1,7 @@ # ec2_elastic_ip_shodan shodan_api_key: null +# VPC Checks # Single account environment: No action required. The AWS account number will be automatically added by the checks. # Multi account environment: Any additional trusted account number should be added as a space separated list, e.g. # trusted_account_ids : ["123456789012", "098765432109", "678901234567"] @@ -9,6 +10,12 @@ trusted_account_ids : [] # cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 +# ec2_securitygroup_with_many_ingress_egress_rules --> by default is 50 rules +max_security_group_rules: 50 + +# ec2_instance_older_than_specific_days --> by default is 6 months (180 days) +max_ec2_instance_age_in_days: 180 + # AppStream Session Configuration max_idle_disconnect_timeout_in_seconds: 600 # 10 Minutes max_disconnect_timeout_in_seconds: 300 # 5 Minutes diff --git a/providers/aws/services/ec2/check_extra7102 b/providers/aws/services/ec2/check_extra7102 deleted file mode 100644 index 6f6eb131..00000000 --- a/providers/aws/services/ec2/check_extra7102 +++ /dev/null @@ -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 " - 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 -} diff --git a/providers/aws/services/ec2/check_extra7135 b/providers/aws/services/ec2/check_extra7135 deleted file mode 100644 index 61c625fc..00000000 --- a/providers/aws/services/ec2/check_extra7135 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra7136 b/providers/aws/services/ec2/check_extra7136 deleted file mode 100644 index fee08e98..00000000 --- a/providers/aws/services/ec2/check_extra7136 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra7137 b/providers/aws/services/ec2/check_extra7137 deleted file mode 100644 index c6c6f925..00000000 --- a/providers/aws/services/ec2/check_extra7137 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra7146 b/providers/aws/services/ec2/check_extra7146 deleted file mode 100644 index 2c4682a8..00000000 --- a/providers/aws/services/ec2/check_extra7146 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra7173 b/providers/aws/services/ec2/check_extra7173 deleted file mode 100644 index 7af800f5..00000000 --- a/providers/aws/services/ec2/check_extra7173 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra74 b/providers/aws/services/ec2/check_extra74 deleted file mode 100644 index 50ed2aa8..00000000 --- a/providers/aws/services/ec2/check_extra74 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra741 b/providers/aws/services/ec2/check_extra741 deleted file mode 100644 index eb1ff336..00000000 --- a/providers/aws/services/ec2/check_extra741 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra75 b/providers/aws/services/ec2/check_extra75 deleted file mode 100644 index 6f39aa54..00000000 --- a/providers/aws/services/ec2/check_extra75 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra751 b/providers/aws/services/ec2/check_extra751 deleted file mode 100644 index 07fa30e2..00000000 --- a/providers/aws/services/ec2/check_extra751 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra752 b/providers/aws/services/ec2/check_extra752 deleted file mode 100644 index b74193f0..00000000 --- a/providers/aws/services/ec2/check_extra752 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra753 b/providers/aws/services/ec2/check_extra753 deleted file mode 100644 index a89c3796..00000000 --- a/providers/aws/services/ec2/check_extra753 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra754 b/providers/aws/services/ec2/check_extra754 deleted file mode 100644 index 9ab1763b..00000000 --- a/providers/aws/services/ec2/check_extra754 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra755 b/providers/aws/services/ec2/check_extra755 deleted file mode 100644 index 24e289d9..00000000 --- a/providers/aws/services/ec2/check_extra755 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra757 b/providers/aws/services/ec2/check_extra757 deleted file mode 100644 index 8fd1c284..00000000 --- a/providers/aws/services/ec2/check_extra757 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra758 b/providers/aws/services/ec2/check_extra758 deleted file mode 100644 index 5575cb2b..00000000 --- a/providers/aws/services/ec2/check_extra758 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra76 b/providers/aws/services/ec2/check_extra76 deleted file mode 100644 index e606b322..00000000 --- a/providers/aws/services/ec2/check_extra76 +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_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 -} diff --git a/providers/aws/services/ec2/check_extra761 b/providers/aws/services/ec2/check_extra761 deleted file mode 100644 index 1e1d2211..00000000 --- a/providers/aws/services/ec2/check_extra761 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra770 b/providers/aws/services/ec2/check_extra770 deleted file mode 100644 index 51c0f678..00000000 --- a/providers/aws/services/ec2/check_extra770 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra772 b/providers/aws/services/ec2/check_extra772 deleted file mode 100644 index 6a23abef..00000000 --- a/providers/aws/services/ec2/check_extra772 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra777 b/providers/aws/services/ec2/check_extra777 deleted file mode 100644 index b7d459df..00000000 --- a/providers/aws/services/ec2/check_extra777 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra778 b/providers/aws/services/ec2/check_extra778 deleted file mode 100644 index c1771752..00000000 --- a/providers/aws/services/ec2/check_extra778 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra779 b/providers/aws/services/ec2/check_extra779 deleted file mode 100644 index 668d529b..00000000 --- a/providers/aws/services/ec2/check_extra779 +++ /dev/null @@ -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 -} diff --git a/providers/aws/services/ec2/check_extra786 b/providers/aws/services/ec2/check_extra786 deleted file mode 100644 index 76430778..00000000 --- a/providers/aws/services/ec2/check_extra786 +++ /dev/null @@ -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 diff --git a/providers/aws/services/ec2/ec2_ami_public/__init__.py b/providers/aws/services/ec2/ec2_ami_public/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.metadata.json b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.metadata.json new file mode 100644 index 00000000..6a6feba2 --- /dev/null +++ b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.metadata.json @@ -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": [] +} diff --git a/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py new file mode 100644 index 00000000..2320beba --- /dev/null +++ b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py new file mode 100644 index 00000000..ba06374f --- /dev/null +++ b/providers/aws/services/ec2/ec2_ami_public/ec2_ami_public_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_ebs_default_encryption/__init__.py b/providers/aws/services/ec2/ec2_ebs_default_encryption/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.metadata.json b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.metadata.json new file mode 100644 index 00000000..336d3ec7 --- /dev/null +++ b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.metadata.json @@ -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": [] +} diff --git a/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.py b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.py new file mode 100644 index 00000000..08e885ad --- /dev/null +++ b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption_test.py b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption_test.py new file mode 100644 index 00000000..5eb4c68e --- /dev/null +++ b/providers/aws/services/ec2/ec2_ebs_default_encryption/ec2_ebs_default_encryption_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.metadata.json b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.metadata.json index e261c3a2..84f7ebb3 100644 --- a/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.metadata.json +++ b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.metadata.json @@ -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": { diff --git a/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.py b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.py index 11d2e57b..08170c76 100644 --- a/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.py +++ b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot.py @@ -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) diff --git a/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot_test.py b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot_test.py new file mode 100644 index 00000000..cf01116a --- /dev/null +++ b/providers/aws/services/ec2/ec2_ebs_public_snapshot/ec2_ebs_public_snapshot_test.py @@ -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." + ) diff --git a/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.metadata.json b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.metadata.json index 383fb099..b4862920 100644 --- a/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.metadata.json +++ b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.metadata.json @@ -13,10 +13,10 @@ "RelatedUrl": "", "Remediation": { "Code": { - "CLI": "", - "NativeIaC": "", - "Other": "", - "Terraform": "" + "CLI": "aws ec2 --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.", diff --git a/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.py b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.py index 14961a00..045a33e4 100644 --- a/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.py +++ b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted.py @@ -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) diff --git a/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted_test.py b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted_test.py new file mode 100644 index 00000000..54c6ae13 --- /dev/null +++ b/providers/aws/services/ec2/ec2_ebs_snapshots_encrypted/ec2_ebs_snapshots_encrypted_test.py @@ -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." + ) diff --git a/providers/aws/services/ec2/ec2_elastic_ip_shodan/ec2_elastic_ip_shodan.py b/providers/aws/services/ec2/ec2_elastic_ip_shodan/ec2_elastic_ip_shodan.py index b7e7f3ee..0db2bf2e 100644 --- a/providers/aws/services/ec2/ec2_elastic_ip_shodan/ec2_elastic_ip_shodan.py +++ b/providers/aws/services/ec2/ec2_elastic_ip_shodan/ec2_elastic_ip_shodan.py @@ -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" diff --git a/providers/aws/services/ec2/ec2_elastic_ip_unassgined/__init__.py b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.metadata.json b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.metadata.json new file mode 100644 index 00000000..66aeb023 --- /dev/null +++ b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.metadata.json @@ -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 ", + "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": [] +} diff --git a/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.py b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.py new file mode 100644 index 00000000..18353b9f --- /dev/null +++ b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined_test.py b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined_test.py new file mode 100644 index 00000000..be732125 --- /dev/null +++ b/providers/aws/services/ec2/ec2_elastic_ip_unassgined/ec2_elastic_ip_unassgined_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/__init__.py b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.metadata.json b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.metadata.json new file mode 100644 index 00000000..6cd364a3 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.metadata.json @@ -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 --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": [] +} diff --git a/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.py b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.py new file mode 100644 index 00000000..7c16365f --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled_test.py b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled_test.py new file mode 100644 index 00000000..26bf92d2 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_imdsv2_enabled/ec2_instance_imdsv2_enabled_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/__init__.py b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.metadata.json b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.metadata.json new file mode 100644 index 00000000..cd4bf63c --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.metadata.json @@ -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": [] +} diff --git a/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.py b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.py new file mode 100644 index 00000000..7b5a38a7 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile_test.py b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile_test.py new file mode 100644 index 00000000..2f86fedc --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_internet_facing_with_instance_profile/ec2_instance_internet_facing_with_instance_profile_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_older_than_specific_days/__init__.py b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.metadata.json b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.metadata.json new file mode 100644 index 00000000..93c5f90d --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.metadata.json @@ -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": [] +} diff --git a/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.py b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.py new file mode 100644 index 00000000..3fbb680b --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days_test.py b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days_test.py new file mode 100644 index 00000000..090bfb18 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_older_than_specific_days/ec2_instance_older_than_specific_days_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.metadata.json b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.metadata.json index e6b75295..191a7c49 100644 --- a/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.metadata.json +++ b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.metadata.json @@ -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.", diff --git a/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.py b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.py index f1cdfaf7..49ada205 100644 --- a/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.py +++ b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip.py @@ -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) diff --git a/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip_test.py b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip_test.py new file mode 100644 index 00000000..e4c9729d --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_public_ip/ec2_instance_public_ip_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_secrets_user_data/__init__.py b/providers/aws/services/ec2/ec2_instance_secrets_user_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.metadata.json b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.metadata.json new file mode 100644 index 00000000..c1ca2f13 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.metadata.json @@ -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": [ + ] +} diff --git a/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.py b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.py new file mode 100644 index 00000000..d824a6da --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data_test.py b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data_test.py new file mode 100644 index 00000000..990321f4 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_secrets_user_data/ec2_instance_secrets_user_data_test.py @@ -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 diff --git a/providers/aws/services/ec2/ec2_instance_secrets_user_data/fixtures/fixture b/providers/aws/services/ec2/ec2_instance_secrets_user_data/fixtures/fixture new file mode 100644 index 00000000..2fb51389 --- /dev/null +++ b/providers/aws/services/ec2/ec2_instance_secrets_user_data/fixtures/fixture @@ -0,0 +1,4 @@ +DB_PASSWORD=foobar123 +DB_USER=foo +API_KEY=12345abcd +SERVICE_PASSWORD=bbaabb45 diff --git a/providers/aws/services/ec2/ec2_network_acls_allow_ingress_any_port/ec2_network_acls_allow_ingress_any_port_test.py b/providers/aws/services/ec2/ec2_network_acls_allow_ingress_any_port/ec2_network_acls_allow_ingress_any_port_test.py index bcc5a107..45ffdbf0 100644 --- a/providers/aws/services/ec2/ec2_network_acls_allow_ingress_any_port/ec2_network_acls_allow_ingress_any_port_test.py +++ b/providers/aws/services/ec2/ec2_network_acls_allow_ingress_any_port/ec2_network_acls_allow_ingress_any_port_test.py @@ -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 ( diff --git a/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/ec2_networkacl_allow_ingress_tcp_port_22_test.py b/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/ec2_networkacl_allow_ingress_tcp_port_22_test.py new file mode 100644 index 00000000..0ac06b8b --- /dev/null +++ b/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_22/ec2_networkacl_allow_ingress_tcp_port_22_test.py @@ -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." + ) diff --git a/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/ec2_networkacl_allow_ingress_tcp_port_3389_test.py b/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/ec2_networkacl_allow_ingress_tcp_port_3389_test.py new file mode 100644 index 00000000..0d3c5e65 --- /dev/null +++ b/providers/aws/services/ec2/ec2_networkacl_allow_ingress_tcp_port_3389/ec2_networkacl_allow_ingress_tcp_port_3389_test.py @@ -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." + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port_test.py new file mode 100644 index 00000000..e05b4ba7 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..6ba6ea9a --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..bc1d094e --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..164197b0 --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..321a3187 --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.metadata.json index fa401fe9..abdce7a9 100644 --- a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.metadata.json +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.metadata.json @@ -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.", diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py index 147c9a51..dbc5ba9b 100644 --- a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py @@ -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) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22_test.py new file mode 100644 index 00000000..8e27f0a0 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.metadata.json index 68ac24c6..6fdec1ad 100644 --- a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.metadata.json +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.metadata.json @@ -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.", diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389_test.py new file mode 100644 index 00000000..a4f39ce0 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..f0549faa --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..8ad081a0 --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..912465eb --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..0d2c8eaf --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..69c30ce1 --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..9b896f3f --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..c2e0ad86 --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..d18ca2b9 --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..9280649b --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..36520e9d --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..11c5220f --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..7daaa3dd --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/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.py b/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.py index 02dfa5f5..7d9dbaea 100644 --- a/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.py +++ b/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.py @@ -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) diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..b053d483 --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..1f812d68 --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/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.metadata.json b/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.metadata.json new file mode 100644 index 00000000..58c3d238 --- /dev/null +++ b/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.metadata.json @@ -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": [] +} diff --git a/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.py b/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.py new file mode 100644 index 00000000..f7638595 --- /dev/null +++ b/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.py @@ -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 diff --git a/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_test.py b/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_test.py new file mode 100644 index 00000000..e21c725c --- /dev/null +++ b/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_test.py @@ -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, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.metadata.json new file mode 100644 index 00000000..a3f1953e --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379", + "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Redis port 6379.", + "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 Redis port 6379.", + "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": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py new file mode 100644 index 00000000..70202ca5 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py @@ -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_redis_6379(Check): + def execute(self): + findings = [] + check_ports = [6379] + 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 Redis port 6379 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 Redis port 6379 open to the Internet." + break + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379_test.py new file mode 100644 index 00000000..bdd7434b --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379_test.py @@ -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_redis_6379: + @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_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379() + ) + 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": 6379, + "ToPort": 6379, + "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_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379() + ) + 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 Redis port 6379 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": 6379, + "ToPort": 6379, + "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_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379() + ) + 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 Redis port 6379 open to the Internet", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.metadata.json new file mode 100644 index 00000000..980e7386 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434", + "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Windows SQL Server ports 1433 or 1434.", + "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 Windows SQL Server ports 1433 or 1434.", + "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": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py new file mode 100644 index 00000000..a0a7eb3c --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py @@ -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_sql_server_1433_1434( + Check +): + def execute(self): + findings = [] + check_ports = [1433, 1434] + 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 Microsoft SQL Server ports 1433 and 1434 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 Microsoft SQL Server ports 1433 and 1434 open to the Internet." + break + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434_test.py new file mode 100644 index 00000000..e3db7564 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434_test.py @@ -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 ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434: + @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_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434() + ) + 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": 1433, + "ToPort": 1434, + "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_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434() + ) + 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 SQL Server ports 1433 and 1434 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": 1433, + "ToPort": 1434, + "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_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434() + ) + 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 SQL Server ports 1433 and 1434 open to the Internet", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.metadata.json new file mode 100644 index 00000000..0fc2bf12 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23", + "CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Telnet port 23.", + "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 Telnet port 23.", + "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": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py new file mode 100644 index 00000000..38b6bac1 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py @@ -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_telnet_23(Check): + def execute(self): + findings = [] + check_ports = [23] + 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 Telnet port 23 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 Telnet port 23 open to the Internet." + break + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23_test.py new file mode 100644 index 00000000..295a9a96 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23_test.py @@ -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_telnet_23: + @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_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23() + ) + 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": 23, + "ToPort": 23, + "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_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23() + ) + 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 Telnet port 23 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": 23, + "ToPort": 23, + "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_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23() + ) + 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 Telnet port 23 open to the Internet", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.metadata.json new file mode 100644 index 00000000..c2e0ad86 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.metadata.json @@ -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": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py new file mode 100644 index 00000000..e19d1182 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py @@ -0,0 +1,47 @@ +import ipaddress + +from lib.check.models import Check, Check_Report +from providers.aws.services.ec2.ec2_client import ec2_client + + +class ec2_securitygroup_allow_wide_open_public_ipv4(Check): + def execute(self): + findings = [] + cidr_treshold = 24 + 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 no potential wide-open non-RFC1918 address." + # Loop through every security group's ingress rule and check it + for ingress_rule in security_group.ingress_rules: + for ipv4 in ingress_rule["IpRanges"]: + ip = ipaddress.ip_network(ipv4["CidrIp"]) + # Check if IP is public according to RFC1918 and if 0 < prefixlen < 24 + if ( + ip.is_global + and ip.prefixlen < cidr_treshold + and ip.prefixlen > 0 + ): + report.status = "FAIL" + report.status_extended = f"Security group {security_group.name} ({security_group.id}) has potential wide-open non-RFC1918 address {ipv4['CidrIp']} in ingress rule." + break + + # Loop through every security group's egress rule and check it + for egress_rule in security_group.egress_rules: + for ipv4 in egress_rule["IpRanges"]: + ip = ipaddress.ip_network(ipv4["CidrIp"]) + # Check if IP is public according to RFC1918 and if 0 < prefixlen < 24 + if ( + ip.is_global + and ip.prefixlen < cidr_treshold + and ip.prefixlen > 0 + ): + report.status = "FAIL" + report.status_extended = f"Security group {security_group.name} ({security_group.id}) has potential wide-open non-RFC1918 address {ipv4['CidrIp']} in egress rule." + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4_test.py b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4_test.py new file mode 100644 index 00000000..4bf3805f --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4_test.py @@ -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_wide_open_public_ipv4: + @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_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4 import ( + ec2_securitygroup_allow_wide_open_public_ipv4, + ) + + check = ec2_securitygroup_allow_wide_open_public_ipv4() + 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_default_sg_with_RFC1918_address(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": "192.0.2.15/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_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4 import ( + ec2_securitygroup_allow_wide_open_public_ipv4, + ) + + check = ec2_securitygroup_allow_wide_open_public_ipv4() + 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 no potential wide-open non-RFC1918 address", + sg.status_extended, + ) + + @mock_ec2 + def test_ec2_default_sg_with_non_RFC1918_address(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": "82.122.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_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_allow_wide_open_public_ipv4.ec2_securitygroup_allow_wide_open_public_ipv4 import ( + ec2_securitygroup_allow_wide_open_public_ipv4, + ) + + check = ec2_securitygroup_allow_wide_open_public_ipv4() + 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 potential wide-open non-RFC1918 address", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.metadata.json new file mode 100644 index 00000000..2c07914b --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_from_launch_wizard", + "CheckTitle": "Security Groups created by EC2 Launch Wizard.", + "CheckType": ["Infrastructure Security"], + "ServiceName": "ec2", + "SubServiceName": "securitygroup", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "medium", + "ResourceType": "AwsEc2SecurityGroup", + "Description": "Security Groups created by EC2 Launch Wizard.", + "Risk": "Security Groups Created on the AWS Console using the EC2 wizard may allow port 22 from 0.0.0.0/0.", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/EC2/security-group-prefixed-with-launch-wizard.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "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.", + "Url": "https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py new file mode 100644 index 00000000..2f6e09e8 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py @@ -0,0 +1,20 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.ec2.ec2_client import ec2_client + + +class ec2_securitygroup_from_launch_wizard(Check): + def execute(self): + findings = [] + 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}) was not created using the EC2 Launch Wizard." + if "launch-wizard" in security_group.name: + report.status = "FAIL" + report.status_extended = f"Security group {security_group.name} ({security_group.id}) was created using the EC2 Launch Wizard." + + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard_test.py b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard_test.py new file mode 100644 index 00000000..76bbeb7a --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard_test.py @@ -0,0 +1,121 @@ +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_securitygroup_from_launch_wizard: + @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_from_launch_wizard.ec2_securitygroup_from_launch_wizard.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_from_launch_wizard.ec2_securitygroup_from_launch_wizard import ( + ec2_securitygroup_from_launch_wizard, + ) + + check = ec2_securitygroup_from_launch_wizard() + 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_launch_wizard_sg(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION) + ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + sg_id = ec2_client.create_security_group( + GroupName="launch-wizard-1", Description="launch wizard sg" + )["GroupId"] + + 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_from_launch_wizard.ec2_securitygroup_from_launch_wizard.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_from_launch_wizard.ec2_securitygroup_from_launch_wizard import ( + ec2_securitygroup_from_launch_wizard, + ) + + check = ec2_securitygroup_from_launch_wizard() + result = check.execute() + + # One default sg per region + created one + assert len(result) == 25 + # Search changed sg + for sg in result: + if sg.resource_id == sg_id: + assert sg.status == "FAIL" + assert search( + "was created using the EC2 Launch Wizard", + 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 = resource("ec2", region_name=AWS_REGION) + ec2.create_instances( + ImageId=EXAMPLE_AMI_ID, + MinCount=1, + MaxCount=1, + SecurityGroupIds=[ + default_sg_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_securitygroup_from_launch_wizard.ec2_securitygroup_from_launch_wizard.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_from_launch_wizard.ec2_securitygroup_from_launch_wizard import ( + ec2_securitygroup_from_launch_wizard, + ) + + check = ec2_securitygroup_from_launch_wizard() + 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( + "was not created using the EC2 Launch Wizard", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.metadata.json new file mode 100644 index 00000000..3eb399c9 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_with_many_ingress_egress_rules", + "CheckTitle": "Ensure there are no Security Groups without ingress filtering being used.", + "CheckType": ["Infrastructure Security"], + "ServiceName": "ec2", + "SubServiceName": "securitygroup", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "high", + "ResourceType": "AwsEc2SecurityGroup", + "Description": "Ensure there are no Security Groups without ingress filtering being used.", + "Risk": "If Security groups are not filtering traffic appropriately the attack surface is increased.", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "You can grant access to a specific CIDR range or to another security group in your VPC or in a peer VPC.", + "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.py b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.py new file mode 100644 index 00000000..94a1c57f --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering.py @@ -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_in_use_without_ingress_filtering(Check): + def execute(self): + findings = [] + 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 ingress filtering." + for ingress_rule in security_group.ingress_rules: + if check_security_group(ingress_rule, "-1"): + report.status = "FAIL" + if len(security_group.network_interfaces) > 0: + report.status_extended = f"Security group {security_group.name} ({security_group.id}) has no ingress filtering and it is being used." + else: + report.status_extended = f"Security group {security_group.name} ({security_group.id}) has no ingress filtering and it is not being used." + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering_test.py b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering_test.py new file mode 100644 index 00000000..63e01d15 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_in_use_without_ingress_filtering/ec2_securitygroup_in_use_without_ingress_filtering_test.py @@ -0,0 +1,175 @@ +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_securitygroup_in_use_without_ingress_filtering: + @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_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering import ( + ec2_securitygroup_in_use_without_ingress_filtering, + ) + + check = ec2_securitygroup_in_use_without_ingress_filtering() + 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_unused_public_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_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering import ( + ec2_securitygroup_in_use_without_ingress_filtering, + ) + + check = ec2_securitygroup_in_use_without_ingress_filtering() + 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 no ingress filtering and it is not being used", + sg.status_extended, + ) + + @mock_ec2 + def test_ec2_used_public_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"}], + } + ], + ) + ec2 = resource("ec2", region_name=AWS_REGION) + ec2.create_instances( + ImageId=EXAMPLE_AMI_ID, + MinCount=1, + MaxCount=1, + SecurityGroupIds=[ + default_sg_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_securitygroup_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering import ( + ec2_securitygroup_in_use_without_ingress_filtering, + ) + + check = ec2_securitygroup_in_use_without_ingress_filtering() + 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 no ingress filtering and it is being used", + sg.status_extended, + ) + + @mock_ec2 + def test_ec2_private_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"] + + 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_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_in_use_without_ingress_filtering.ec2_securitygroup_in_use_without_ingress_filtering import ( + ec2_securitygroup_in_use_without_ingress_filtering, + ) + + check = ec2_securitygroup_in_use_without_ingress_filtering() + 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 ingress filtering", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_not_used/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_not_used/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.metadata.json new file mode 100644 index 00000000..eece52fa --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_not_used", + "CheckTitle": "Ensure there are no Security Groups not being used.", + "CheckType": ["Infrastructure Security"], + "ServiceName": "ec2", + "SubServiceName": "securitygroup", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "low", + "ResourceType": "AwsEc2SecurityGroup", + "Description": "Ensure there are no Security Groups not being used.", + "Risk": "Having clear definition and scope for Security Groups creates a better administration environment.", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "List all the security groups and then use the cli to check if they are attached to an instance.", + "Url": "https://aws.amazon.com/premiumsupport/knowledge-center/ec2-find-security-group-resources/" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py new file mode 100644 index 00000000..0ce5652b --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py @@ -0,0 +1,20 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.ec2.ec2_client import ec2_client + + +class ec2_securitygroup_not_used(Check): + def execute(self): + findings = [] + 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}) it is being used." + if len(security_group.network_interfaces) == 0: + report.status = "FAIL" + report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is not being used." + + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py new file mode 100644 index 00000000..0a9d5163 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py @@ -0,0 +1,121 @@ +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_securitygroup_not_used: + @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_not_used.ec2_securitygroup_not_used.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_not_used.ec2_securitygroup_not_used import ( + ec2_securitygroup_not_used, + ) + + check = ec2_securitygroup_not_used() + result = check.execute() + + # One default sg per region + assert len(result) == 24 + # All are unused by default + assert result[0].status == "FAIL" + + @mock_ec2 + def test_ec2_unused_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"] + + 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_not_used.ec2_securitygroup_not_used.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_not_used.ec2_securitygroup_not_used import ( + ec2_securitygroup_not_used, + ) + + check = ec2_securitygroup_not_used() + 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( + "it is not being used", + sg.status_extended, + ) + + @mock_ec2 + def test_ec2_used_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 = resource("ec2", region_name=AWS_REGION) + ec2.create_instances( + ImageId=EXAMPLE_AMI_ID, + MinCount=1, + MaxCount=1, + SecurityGroupIds=[ + default_sg_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_securitygroup_not_used.ec2_securitygroup_not_used.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_not_used.ec2_securitygroup_not_used import ( + ec2_securitygroup_not_used, + ) + + check = ec2_securitygroup_not_used() + 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( + "it is being used", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/__init__.py b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.metadata.json b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.metadata.json new file mode 100644 index 00000000..b94c5e0e --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "ec2_securitygroup_with_many_ingress_egress_rules", + "CheckTitle": "Find security groups with more than 50 ingress or egress rules.", + "CheckType": ["Infrastructure Security"], + "ServiceName": "ec2", + "SubServiceName": "securitygroup", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "high", + "ResourceType": "AwsEc2SecurityGroup", + "Description": "Find security groups with more than 50 ingress or egress rules.", + "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": [] +} diff --git a/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py new file mode 100644 index 00000000..83825e08 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py @@ -0,0 +1,23 @@ +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_securitygroup_with_many_ingress_egress_rules(Check): + def execute(self): + findings = [] + max_security_group_rules = get_config_var("max_security_group_rules") + 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 {len(security_group.ingress_rules)} inbound rules and {len(security_group.egress_rules)} outbound rules" + if ( + len(security_group.ingress_rules) > max_security_group_rules + or len(security_group.egress_rules) > max_security_group_rules + ): + report.status = "FAIL" + findings.append(report) + + return findings diff --git a/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules_test.py b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules_test.py new file mode 100644 index 00000000..f9492cc4 --- /dev/null +++ b/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules_test.py @@ -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_with_many_ingress_egress_rules: + @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_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules import ( + ec2_securitygroup_with_many_ingress_egress_rules, + ) + + check = ec2_securitygroup_with_many_ingress_egress_rules() + 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"] + for i in range(60): + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": i, + "ToPort": i, + "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_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules import ( + ec2_securitygroup_with_many_ingress_egress_rules, + ) + + check = ec2_securitygroup_with_many_ingress_egress_rules() + 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 60 inbound rules and 1 outbound rules", 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": 23, + "ToPort": 23, + "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_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from providers.aws.services.ec2.ec2_securitygroup_with_many_ingress_egress_rules.ec2_securitygroup_with_many_ingress_egress_rules import ( + ec2_securitygroup_with_many_ingress_egress_rules, + ) + + check = ec2_securitygroup_with_many_ingress_egress_rules() + 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 1 inbound rules and 1 outbound rules", + sg.status_extended, + ) diff --git a/providers/aws/services/ec2/ec2_service.py b/providers/aws/services/ec2/ec2_service.py index d5c3c98c..fab2b272 100644 --- a/providers/aws/services/ec2/ec2_service.py +++ b/providers/aws/services/ec2/ec2_service.py @@ -14,6 +14,7 @@ class EC2: self.regional_clients = generate_regional_clients(self.service, audit_info) self.instances = [] self.__threading_call__(self.__describe_instances__) + self.__get_instance_user_data__() self.security_groups = [] self.__threading_call__(self.__describe_security_groups__) self.network_acls = [] @@ -21,10 +22,15 @@ class EC2: self.snapshots = [] self.__threading_call__(self.__describe_snapshots__) self.__get_snapshot_public__() - self.elastic_ips = [] - self.__threading_call__(self.__describe_elastic_ips__) + self.__threading_call__(self.__describe_network_interfaces__) + self.images = [] + self.__threading_call__(self.__describe_images__) self.volumes = [] self.__threading_call__(self.__describe_volumes__) + self.ebs_encryption_by_default = [] + self.__threading_call__(self.__get_ebs_encryption_by_default__) + self.elastic_ips = [] + self.__threading_call__(self.__describe_addresses__) def __get_session__(self): return self.session @@ -47,37 +53,40 @@ class EC2: for page in describe_instances_paginator.paginate(): for reservation in page["Reservations"]: for instance in reservation["Instances"]: + http_tokens = None + http_endpoint = None + public_dns = None + public_ip = None + instance_profile = None + if "MetadataOptions" in instance: + http_tokens = instance["MetadataOptions"]["HttpTokens"] + http_endpoint = instance["MetadataOptions"]["HttpEndpoint"] if ( "PublicDnsName" in instance and "PublicIpAddress" in instance ): - self.instances.append( - Instance( - instance["InstanceId"], - regional_client.region, - instance["InstanceType"], - instance["ImageId"], - instance["LaunchTime"], - instance["PrivateDnsName"], - instance["PrivateIpAddress"], - instance["PublicDnsName"], - instance["PublicIpAddress"], - ) - ) - else: - self.instances.append( - Instance( - instance["InstanceId"], - regional_client.region, - instance["InstanceType"], - instance["ImageId"], - instance["LaunchTime"], - instance["PrivateDnsName"], - instance["PrivateIpAddress"], - None, - None, - ) + public_dns = instance["PublicDnsName"] + public_ip = instance["PublicIpAddress"] + if "IamInstanceProfile" in instance: + instance_profile = instance["IamInstanceProfile"] + + self.instances.append( + Instance( + instance["InstanceId"], + instance["State"]["Name"], + regional_client.region, + instance["InstanceType"], + instance["ImageId"], + instance["LaunchTime"], + instance["PrivateDnsName"], + instance["PrivateIpAddress"], + public_dns, + public_ip, + http_tokens, + http_endpoint, + instance_profile, ) + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -132,9 +141,7 @@ class EC2: "describe_snapshots" ) encrypted = False - for page in describe_snapshots_paginator.paginate( - OwnerIds=[str(self.audited_account)] - ): + for page in describe_snapshots_paginator.paginate(OwnerIds=["self"]): for snapshot in page["Snapshots"]: if snapshot["Encrypted"]: encrypted = True @@ -149,7 +156,7 @@ class EC2: ) def __get_snapshot_public__(self): - logger.info("EC2 - Get snapshots encryption...") + logger.info("EC2 - Gettting snapshots encryption...") try: for snapshot in self.snapshots: regional_client = self.regional_clients[snapshot.region] @@ -165,24 +172,60 @@ class EC2: f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def __describe_elastic_ips__(self, regional_client): + def __describe_network_interfaces__(self, regional_client): logger.info("EC2 - Describing Network Interfaces...") try: - describe_network_interfaces_paginator = regional_client.get_paginator( - "describe_network_interfaces" + # Get SGs Network Interfaces + for sg in self.security_groups: + regional_client = self.regional_clients[sg.region] + describe_network_interfaces_paginator = regional_client.get_paginator( + "describe_network_interfaces" + ) + for page in describe_network_interfaces_paginator.paginate( + Filters=[ + { + "Name": "group-id", + "Values": [ + sg.id, + ], + }, + ], + ): + for interface in page["NetworkInterfaces"]: + sg.network_interfaces.append(interface["NetworkInterfaceId"]) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - for page in describe_network_interfaces_paginator.paginate(): - for eip in page["NetworkInterfaces"]: - # Get only public attached ones - if "Association" in eip: - self.elastic_ips.append( - ElasticIP( - eip["Association"]["PublicIp"], - eip["VpcId"], - eip["SubnetId"], - regional_client.region, - ) - ) + + def __get_instance_user_data__(self): + logger.info("EC2 - Gettting instance user data...") + try: + for instance in self.instances: + regional_client = self.regional_clients[instance.region] + user_data = regional_client.describe_instance_attribute( + Attribute="userData", InstanceId=instance.id + )["UserData"] + if "Value" in user_data: + instance.user_data = user_data["Value"] + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __describe_images__(self, regional_client): + logger.info("EC2 - Describing Images...") + try: + public = False + for image in regional_client.describe_images(Owners=["self"])["Images"]: + if image["Public"]: + public = True + self.images.append( + Image( + image["ImageId"], image["Name"], public, regional_client.region + ) + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -208,10 +251,54 @@ class EC2: f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def __describe_addresses__(self, regional_client): + logger.info("EC2 - Describing Elastic IPs...") + try: + for address in regional_client.describe_addresses()["Addresses"]: + print(address) + public_ip = None + association_id = None + allocation_id = None + if "PublicIp" in address: + public_ip = address["PublicIp"] + if "AssociationId" in address: + association_id = address["AssociationId"] + if "AllocationId" in address: + allocation_id = address["AllocationId"] + self.elastic_ips.append( + ElasticIP( + public_ip, + association_id, + allocation_id, + regional_client.region, + ) + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __get_ebs_encryption_by_default__(self, regional_client): + logger.info("EC2 - Get EBS Encryption By Default...") + try: + self.ebs_encryption_by_default.append( + EbsEncryptionByDefault( + regional_client.get_ebs_encryption_by_default()[ + "EbsEncryptionByDefault" + ], + regional_client.region, + ) + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + @dataclass class Instance: id: str + state: str region: str type: str image_id: str @@ -220,10 +307,15 @@ class Instance: private_ip: str public_dns: str public_ip: str + user_data: str + http_tokens: str + http_endpoint: str + instance_profile: str def __init__( self, id, + state, region, type, image_id, @@ -232,8 +324,12 @@ class Instance: private_ip, public_dns, public_ip, + http_tokens, + http_endpoint, + instance_profile, ): self.id = id + self.state = state self.region = region self.type = type self.image_id = image_id @@ -242,6 +338,10 @@ class Instance: self.private_ip = private_ip self.public_dns = public_dns self.public_ip = public_ip + self.http_tokens = http_tokens + self.http_endpoint = http_endpoint + self.user_data = None + self.instance_profile = instance_profile @dataclass @@ -275,6 +375,7 @@ class SecurityGroup: name: str region: str id: str + network_interfaces: list[str] ingress_rules: list[dict] egress_rules: list[dict] @@ -284,6 +385,7 @@ class SecurityGroup: self.id = id self.ingress_rules = ingress_rules self.egress_rules = egress_rules + self.network_interfaces = [] @dataclass @@ -301,12 +403,36 @@ class NetworkACL: @dataclass class ElasticIP: public_ip: str - vpc: str - subnet: str + association_id: str + allocation_id: str region: str - def __init__(self, public_ip, vpc, subnet, region): + def __init__(self, public_ip, association_id, allocation_id, region): self.public_ip = public_ip - self.vpc = vpc - self.subnet = subnet + self.association_id = association_id + self.allocation_id = allocation_id + self.region = region + + +@dataclass +class Image: + id: str + name: str + public: bool + region: str + + def __init__(self, id, name, public, region): + self.id = id + self.name = name + self.public = public + self.region = region + + +@dataclass +class EbsEncryptionByDefault: + status: bool + region: str + + def __init__(self, status, region): + self.status = status self.region = region diff --git a/providers/aws/services/ec2/ec2_service_test.py b/providers/aws/services/ec2/ec2_service_test.py index 268365ef..982002a9 100644 --- a/providers/aws/services/ec2/ec2_service_test.py +++ b/providers/aws/services/ec2/ec2_service_test.py @@ -1,3 +1,5 @@ +from base64 import b64decode + from boto3 import client, resource, session from moto import mock_ec2 @@ -6,6 +8,7 @@ from providers.aws.services.ec2.ec2_service import EC2 AWS_ACCOUNT_NUMBER = 123456789012 AWS_REGION = "us-east-1" +EXAMPLE_AMI_ID = "ami-12c6146b" class Test_EC2_Service: @@ -136,7 +139,7 @@ class Test_EC2_Service: ec2 = EC2(audit_info) assert snapshot_id in str(ec2.snapshots) - # Test EC2 Describe Snapshots + # Test EC2 Get Snapshot Public @mock_ec2 def test__get_snapshot_public__(self): # Generate EC2 Client @@ -164,4 +167,46 @@ class Test_EC2_Service: ec2 = EC2(audit_info) for snapshot in ec2.snapshots: if snapshot.id == snapshot_id: - assert snapshot.public is True + assert snapshot.public + + # Test EC2 Instance User Data + @mock_ec2 + def test__get_instance_user_data__(self): + user_data = "This is some user_data" + ec2 = resource("ec2", region_name=AWS_REGION) + ec2.create_instances( + ImageId=EXAMPLE_AMI_ID, + MinCount=1, + MaxCount=1, + UserData="This is some user_data", + ) + # EC2 client for this test class + audit_info = self.set_mocked_audit_info() + ec2 = EC2(audit_info) + assert user_data == b64decode(ec2.instances[0].user_data).decode("utf-8") + + # Test EC2 Instance User Data + @mock_ec2 + def test__get_ebs_encryption_by_default__(self): + ec2_client = client("ec2", region_name=AWS_REGION) + ec2_client.enable_ebs_encryption_by_default() + # EC2 client for this test class + audit_info = self.set_mocked_audit_info() + ec2 = EC2(audit_info) + + # One result per region + assert len(ec2.ebs_encryption_by_default) == 23 + for result in ec2.ebs_encryption_by_default: + if result.region == AWS_REGION: + assert result.status + + # Test EC2 Describe Snapshots + @mock_ec2 + def test__describe_addresses__(self): + # Generate EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222") + # EC2 client for this test class + audit_info = self.set_mocked_audit_info() + ec2 = EC2(audit_info) + assert "127.38.43.222" in str(ec2.elastic_ips)