feat(RDS): Service and missing checks (#1513)

This commit is contained in:
Sergio Garcia
2022-11-23 14:34:51 +01:00
committed by GitHub
parent 9204142eaf
commit 989638a42d
75 changed files with 2293 additions and 494 deletions

View File

View File

@@ -1,58 +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.
# Remediation:
#
# https://www.cloudconformity.com/knowledge-base/aws/RDS/instance-deletion-protection.html
# https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
#
# aws rds modify-db-instance \
# --region us-east-1 \
# --db-instance-identifier test-db \
# --deletion-protection \
# [--apply-immediately | --no-apply-immediately]
CHECK_ID_extra7113="7.113"
CHECK_TITLE_extra7113="[extra7113] Check if RDS instances have deletion protection enabled "
CHECK_SCORED_extra7113="NOT_SCORED"
CHECK_CIS_LEVEL_extra7113="EXTRA"
CHECK_SEVERITY_extra7113="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7113="AwsRdsDbInstance"
CHECK_ALTERNATE_check7113="extra7113"
CHECK_SERVICENAME_extra7113="rds"
CHECK_RISK_extra7113='You can only delete instances that do not have deletion protection enabled.'
CHECK_REMEDIATION_extra7113='Enable deletion protection using the AWS Management Console for production DB instances.'
CHECK_DOC_extra7113='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html'
CHECK_CAF_EPIC_extra7113='Data Protection'
extra7113(){
for regx in $REGIONS; do
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $LIST_OF_RDS_INSTANCES ]];then
for rdsinstance in $LIST_OF_RDS_INSTANCES; do
IS_DELETIONPROTECTION=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].DeletionProtection' --output text)
if [[ $IS_DELETIONPROTECTION == "False" ]]; then
textFail "$regx: RDS instance $rdsinstance deletion protection is not enabled!" "$regx" "$rdsinstance"
else
textPass "$regx: RDS instance $rdsinstance deletion protection is enabled" "$regx" "$rdsinstance"
fi
done
else
textInfo "$regx: No RDS instances found" "$regx"
fi
done
}

View File

@@ -1,48 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7131="7.131"
CHECK_TITLE_extra7131="[extra7131] Ensure RDS instances have minor version upgrade enabled"
CHECK_SCORED_extra7131="NOT_SCORED"
CHECK_CIS_LEVEL_extra7131="EXTRA"
CHECK_SEVERITY_extra7131="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7131="AwsRdsDbInstance"
CHECK_ALTERNATE_check7131="extra7131"
CHECK_SERVICENAME_extra7131="rds"
CHECK_RISK_extra7131='Auto Minor Version Upgrade is a feature that you can enable to have your database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs; and therefor should be applied.'
CHECK_REMEDIATION_extra7131='Enable auto minor version upgrade for all databases and environments.'
CHECK_DOC_extra7131='https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/'
CHECK_CAF_EPIC_extra7131='Infrastructure Security'
extra7131(){
for regx in $REGIONS; do
# LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true` && DBInstanceStatus==`"available"`].[DBInstanceIdentifier,Endpoint.Address]' --output text)
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].[DBInstanceIdentifier,AutoMinorVersionUpgrade]' --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $LIST_OF_RDS_INSTANCES ]];then
while read -r rds_instance;do
RDS_NAME=$(echo $rds_instance | awk '{ print $1; }')
RDS_AUTOMINORUPGRADE_FLAG=$(echo $rds_instance | awk '{ print $2; }')
if [[ $RDS_AUTOMINORUPGRADE_FLAG == "True" ]];then
textPass "$regx: RDS instance: $RDS_NAME is has minor version upgrade enabled" "$regx" "$RDS_NAME"
else
textFail "$regx: RDS instance: $RDS_NAME does not have minor version upgrade enabled" "$regx" "$RDS_NAME"
fi
done <<< "$LIST_OF_RDS_INSTANCES"
else
textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
fi
done
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7132="7.132"
CHECK_TITLE_extra7132="[extra7132] Check if RDS instances has enhanced monitoring enabled"
CHECK_SCORED_extra7132="NOT_SCORED"
CHECK_CIS_LEVEL_extra7132="EXTRA"
CHECK_SEVERITY_extra7132="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7132="AwsRdsDbInstance"
CHECK_ALTERNATE_check7132="extra7132"
CHECK_SERVICENAME_extra7132="rds"
CHECK_RISK_extra7132='A smaller monitoring interval results in more frequent reporting of OS metrics.'
CHECK_REMEDIATION_extra7132='To use Enhanced Monitoring; you must create an IAM role; and then enable Enhanced Monitoring.'
CHECK_DOC_extra7132='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.html'
CHECK_CAF_EPIC_extra7132='Logging and Monitoring'
extra7132(){
for regx in $REGIONS; do
RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
if [[ $(echo "$RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $RDS_INSTANCES ]];then
for rdsinstance in ${RDS_INSTANCES}; do
RDS_NAME="$rdsinstance"
MONITORING_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].[EnhancedMonitoringResourceArn]' --output text)
if [[ $MONITORING_FLAG == "None" ]];then
textFail "$regx: RDS instance: $RDS_NAME has enhanced monitoring disabled!" "$rex" "$RDS_NAME"
else
textPass "$regx: RDS instance: $RDS_NAME has enhanced monitoring enabled." "$regx" "$RDS_NAME"
fi
done
else
textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
fi
done
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7133="7.133"
CHECK_TITLE_extra7133="[extra7133] Check if RDS instances have multi-AZ enabled"
CHECK_SCORED_extra7133="NOT_SCORED"
CHECK_CIS_LEVEL_extra7133="EXTRA"
CHECK_SEVERITY_extra7133="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7133="AwsRdsDbInstance"
CHECK_ALTERNATE_check7133="extra7133"
CHECK_SERVICENAME_extra7133="rds"
CHECK_RISK_extra7133='In case of failure; with a single-AZ deployment configuration; should an availability zone specific database failure occur; Amazon RDS can not automatically fail over to the standby availability zone.'
CHECK_REMEDIATION_extra7133='Enable multi-AZ deployment for production databases.'
CHECK_DOC_extra7133='https://aws.amazon.com/rds/features/multi-az/'
CHECK_CAF_EPIC_extra7133='Data Protection'
extra7133(){
for regx in $REGIONS; do
RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].DBInstanceIdentifier" --output text 2>&1)
if [[ $(echo "$RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $RDS_INSTANCES ]];then
for rdsinstance in ${RDS_INSTANCES}; do
RDS_NAME="$rdsinstance"
MULTIAZ_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].MultiAZ' --output text)
if [[ $MULTIAZ_FLAG == "True" ]];then
textPass "$regx: RDS instance: $RDS_NAME has multi-AZ enabled" "$regx" "$RDS_NAME"
else
textFail "$regx: RDS instance: $RDS_NAME has multi-AZ disabled!" "$regx" "$RDS_NAME"
fi
done
else
textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
fi
done
}

View File

@@ -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_extra723="7.23"
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots and Cluster Snapshots are public"
CHECK_SCORED_extra723="NOT_SCORED"
CHECK_CIS_LEVEL_extra723="EXTRA"
CHECK_SEVERITY_extra723="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra723="AwsRdsDbSnapshot"
CHECK_ALTERNATE_check723="extra723"
CHECK_SERVICENAME_extra723="rds"
CHECK_RISK_extra723='Publicly accessible services could expose sensitive data to bad actors. t is recommended that your RDS snapshots should not be public in order to prevent potential leak or misuse of sensitive data or any other kind of security threat. If your RDS snapshot is public; then the data which is backed up in that snapshot is accessible to all other AWS accounts.'
CHECK_REMEDIATION_extra723='Use AWS Config to identify any sanpshot that is public.'
CHECK_DOC_extra723='https://docs.aws.amazon.com/config/latest/developerguide/rds-snapshots-public-prohibited.html'
CHECK_CAF_EPIC_extra723='Data Protection'
extra723(){
# "Check if RDS Snapshots are public "
for regx in $REGIONS; do
# RDS snapshots
LIST_OF_RDS_SNAPSHOTS=$($AWSCLI rds describe-db-snapshots $PROFILE_OPT --region $regx --query DBSnapshots[*].DBSnapshotIdentifier --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_SNAPSHOTS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to describe db snapshots" "$regx"
continue
fi
if [[ $LIST_OF_RDS_SNAPSHOTS ]]; then
for rdssnapshot in $LIST_OF_RDS_SNAPSHOTS;do
SNAPSHOT_IS_PUBLIC=$($AWSCLI rds describe-db-snapshot-attributes $PROFILE_OPT --region $regx --db-snapshot-identifier $rdssnapshot --query DBSnapshotAttributesResult.DBSnapshotAttributes[*] --output text|grep ^ATTRIBUTEVALUES|cut -f2|grep all)
if [[ $SNAPSHOT_IS_PUBLIC ]];then
textFail "$regx: RDS Snapshot $rdssnapshot is public!" "$regx" "$rdssnapshot"
else
textPass "$regx: RDS Snapshot $rdssnapshot is not shared" "$regx" "$rdssnapshot"
fi
done
else
textInfo "$regx: No RDS Snapshots found" "$regx" "$rdssnapshot"
fi
# RDS cluster snapshots
LIST_OF_RDS_CLUSTER_SNAPSHOTS=$($AWSCLI rds describe-db-cluster-snapshots $PROFILE_OPT --region $regx --query DBClusterSnapshots[*].DBClusterSnapshotIdentifier --output text)
if [[ $LIST_OF_RDS_CLUSTER_SNAPSHOTS ]]; then
for rdsclustersnapshot in $LIST_OF_RDS_CLUSTER_SNAPSHOTS;do
CLUSTER_SNAPSHOT_IS_PUBLIC=$($AWSCLI rds describe-db-cluster-snapshot-attributes $PROFILE_OPT --region $regx --db-cluster-snapshot-identifier $rdsclustersnapshot --query DBClusterSnapshotAttributesResult.DBClusterSnapshotAttributes[*] --output text|grep ^ATTRIBUTEVALUES|cut -f2|grep all)
if [[ $CLUSTER_SNAPSHOT_IS_PUBLIC ]];then
textFail "$regx: RDS Cluster Snapshot $rdsclustersnapshot is public!" "$regx" "$rdsclustersnapshot"
else
textPass "$regx: RDS Cluster Snapshot $rdsclustersnapshot is not shared" "$regx" "$rdsclustersnapshot"
fi
done
else
textInfo "$regx: No RDS Cluster Snapshots found" "$regx" "$rdsclustersnapshot"
fi
done
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra735="7.35"
CHECK_TITLE_extra735="[extra735] Check if RDS instances storage is encrypted"
CHECK_SCORED_extra735="NOT_SCORED"
CHECK_CIS_LEVEL_extra735="EXTRA"
CHECK_SEVERITY_extra735="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra735="AwsRdsDbInstance"
CHECK_ALTERNATE_check735="extra735"
CHECK_ASFF_COMPLIANCE_TYPE_extra735="ens-mp.info.3.aws.rds.1"
CHECK_SERVICENAME_extra735="rds"
CHECK_RISK_extra735='If not enabled sensitive information at rest is not protected.'
CHECK_REMEDIATION_extra735='Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.'
CHECK_DOC_extra735='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html'
CHECK_CAF_EPIC_extra735='Data Protection'
extra735(){
for regx in $REGIONS; do
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $LIST_OF_RDS_INSTANCES ]];then
for rdsinstance in $LIST_OF_RDS_INSTANCES; do
IS_ENCRYPTED=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].StorageEncrypted' --output text)
if [[ $IS_ENCRYPTED == "False" ]]; then
textFail "$regx: RDS instance $rdsinstance is not encrypted!" "$regx" "$rdsinstance"
else
textPass "$regx: RDS instance $rdsinstance is encrypted" "$regx" "$rdsinstance"
fi
done
else
textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
fi
done
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra739="7.39"
CHECK_TITLE_extra739="[extra739] Check if RDS instances have backup enabled"
CHECK_SCORED_extra739="NOT_SCORED"
CHECK_CIS_LEVEL_extra739="EXTRA"
CHECK_SEVERITY_extra739="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra739="AwsRdsDbInstance"
CHECK_ALTERNATE_check739="extra739"
CHECK_SERVICENAME_extra739="rds"
CHECK_RISK_extra739='If backup is not enabled; data is vulnerable. Human error or bad actors could erase or modify data.'
CHECK_REMEDIATION_extra739='Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.'
CHECK_DOC_extra739='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html'
CHECK_CAF_EPIC_extra739='Data Protection'
extra739(){
for regx in $REGIONS; do
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue
fi
if [[ $LIST_OF_RDS_INSTANCES ]];then
for rdsinstance in $LIST_OF_RDS_INSTANCES; do
# if retention is 0 then is disabled
BACKUP_RETENTION=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].BackupRetentionPeriod' --output text)
if [[ $BACKUP_RETENTION == "0" ]]; then
textFail "$regx: RDS instance $rdsinstance has not backup enabled!" "$regx" "$rdsinstance"
else
textPass "$regx: RDS instance $rdsinstance has backup enabled with retention period $BACKUP_RETENTION days" "$regx" "$rdsinstance"
fi
done
else
textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
fi
done
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra747="7.47"
CHECK_TITLE_extra747="[extra747] Check if RDS instances is integrated with CloudWatch Logs"
CHECK_SCORED_extra747="NOT_SCORED"
CHECK_CIS_LEVEL_extra747="EXTRA"
CHECK_SEVERITY_extra747="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra747="AwsRdsDbInstance"
CHECK_ALTERNATE_check747="extra747"
CHECK_SERVICENAME_extra747="rds"
CHECK_RISK_extra747='If logs are not enabled; monitoring of service use and threat analysis is not possible.'
CHECK_REMEDIATION_extra747='Use CloudWatch Logs to perform real-time analysis of the log data. Create alarms and view metrics.'
CHECK_DOC_extra747='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html'
CHECK_CAF_EPIC_extra747='Logging and Monitoring'
extra747(){
for regx in $REGIONS; do
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
continue
fi
if [[ $LIST_OF_RDS_INSTANCES ]];then
for rdsinstance in $LIST_OF_RDS_INSTANCES; do
# if retention is 0 then is disabled
ENABLED_CLOUDWATCHLOGS_EXPORTS=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].EnabledCloudwatchLogsExports' --output text)
if [[ $ENABLED_CLOUDWATCHLOGS_EXPORTS ]]; then
textPass "$regx: RDS instance $rdsinstance is shipping $ENABLED_CLOUDWATCHLOGS_EXPORTS to CloudWatch Logs" "$regx" "$rdsinstance"
else
textFail "$regx: RDS instance $rdsinstance has no CloudWatch Logs enabled!" "$regx" "$rdsinstance"
fi
done
else
textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
fi
done
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra78="7.8"
CHECK_TITLE_extra78="[extra78] Ensure there are no Public Accessible RDS instances"
CHECK_SCORED_extra78="NOT_SCORED"
CHECK_CIS_LEVEL_extra78="EXTRA"
CHECK_SEVERITY_extra78="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra78="AwsRdsDbInstance"
CHECK_ALTERNATE_extra708="extra78"
CHECK_ALTERNATE_check78="extra78"
CHECK_ALTERNATE_check708="extra78"
CHECK_SERVICENAME_extra78="rds"
CHECK_RISK_extra78='Publicly accessible databases could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra78='Using an AWS Config rule check for RDS public instances periodically and check there is a business reason for it.'
CHECK_DOC_extra78='https://docs.amazonaws.cn/en_us/config/latest/developerguide/rds-instance-public-access-check.html'
CHECK_CAF_EPIC_extra78='Data Protection'
extra78(){
# "Ensure there are no Public Accessible RDS instances "
for regx in $REGIONS; do
LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true` && DBInstanceStatus==`"available"`].[DBInstanceIdentifier,Endpoint.Address]' --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_PUBLIC_INSTANCES" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied Trying to describe DB instances" "$regx"
continue
fi
if [[ $LIST_OF_RDS_PUBLIC_INSTANCES ]];then
while read -r rds_instance;do
RDS_NAME=$(echo $rds_instance | awk '{ print $1; }')
RDS_DNSNAME=$(echo $rds_instance | awk '{ print $2; }')
textFail "$regx: RDS instance: $RDS_NAME at $RDS_DNSNAME is set as Publicly Accessible!" "$regx" "$RDS_NAME"
done <<< "$LIST_OF_RDS_PUBLIC_INSTANCES"
else
textPass "$regx: no Publicly Accessible RDS instances found" "$regx" "$RDS_NAME"
fi
done
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
rds_client = RDS(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_backup_enabled",
"CheckTitle": "Check if RDS instances have backup enabled.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "medium",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances have backup enabled.",
"Risk": "If backup is not enabled, data is vulnerable. Human error or bad actors could erase or modify data.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-instance --db-instance-identifier <db_instance_id> --backup-retention-period 7 --apply-immediately",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-automated-backups-enabled.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-rds-instances-have-backup-policy#terraform"
},
"Recommendation": {
"Text": "Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.",
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_backup_enabled(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.backup_retention_period > 0:
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} has backup enabled with retention period {db_instance.backup_retention_period} days."
else:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance {db_instance.id} has not backup enabled."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,103 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_backup_enabled:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled import (
rds_instance_backup_enabled,
)
check = rds_instance_backup_enabled()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_backup(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled.rds_client",
new=RDS(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled import (
rds_instance_backup_enabled,
)
service_client.db_instances[0].backup_retention_period = 0
check = rds_instance_backup_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has not backup enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_backup(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
BackupRetentionPeriod=10,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled import (
rds_instance_backup_enabled,
)
check = rds_instance_backup_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has backup enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_deletion_protection",
"CheckTitle": "Check if RDS instances have deletion protection enabled.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "medium",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances have deletion protection enabled.",
"Risk": "You can only delete instances that do not have deletion protection enabled.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-instance --db-instance-identifier <db_instance_id> --deletion-protection --apply-immediately",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/instance-deletion-protection.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-rds-clusters-and-instances-have-deletion-protection-enabled#terraform"
},
"Recommendation": {
"Text": "Enable deletion protection using the AWS Management Console for production DB instances.",
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_deletion_protection(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.deletion_protection:
report.status = "PASS"
report.status_extended = (
f"RDS Instance {db_instance.id} deletion protection is enabled."
)
else:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance {db_instance.id} deletion protection is not enabled."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_deletion_protection:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection import (
rds_instance_deletion_protection,
)
check = rds_instance_deletion_protection()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_deletion_protection(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection import (
rds_instance_deletion_protection,
)
check = rds_instance_deletion_protection()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"deletion protection is not enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_encryption(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
DeletionProtection=True,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_deletion_protection.rds_instance_deletion_protection import (
rds_instance_deletion_protection,
)
check = rds_instance_deletion_protection()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"deletion protection is enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_enhanced_monitoring_enabled",
"CheckTitle": "Check if RDS instances has enhanced monitoring enabled.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "low",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances has enhanced monitoring enabled.",
"Risk": "A smaller monitoring interval results in more frequent reporting of OS metrics.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.html",
"Remediation": {
"Code": {
"CLI": "aws rds create-db-instance --db-instance-identifier <db_instance_id> --db-instance-class <instance_class> --engine <engine> --storage-encrypted true",
"NativeIaC": "",
"Other": "",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-enhanced-monitoring-is-enabled-for-amazon-rds-instances#terraform"
},
"Recommendation": {
"Text": "To use Enhanced Monitoring, you must create an IAM role; and then enable Enhanced Monitoring.",
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_enhanced_monitoring_enabled(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.enhanced_monitoring_arn:
report.status = "PASS"
report.status_extended = (
f"RDS Instance {db_instance.id} has enhanced monitoring enabled."
)
else:
report.status = "FAIL"
report.status_extended = f"RDS Instance {db_instance.id} does not have enhanced monitoring enabled."
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_enhanced_monitoring_enabled:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled import (
rds_instance_enhanced_monitoring_enabled,
)
check = rds_instance_enhanced_monitoring_enabled()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_monitoring(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled import (
rds_instance_enhanced_monitoring_enabled,
)
check = rds_instance_enhanced_monitoring_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have enhanced monitoring enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_monitoring(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled.rds_client",
new=RDS(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.rds.rds_instance_enhanced_monitoring_enabled.rds_instance_enhanced_monitoring_enabled import (
rds_instance_enhanced_monitoring_enabled,
)
service_client.db_instances[0].enhanced_monitoring_arn = "log-stream"
check = rds_instance_enhanced_monitoring_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has enhanced monitoring enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_integration_cloudwatch_logs",
"CheckTitle": "Check if RDS instances is integrated with CloudWatch Logs.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "medium",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances is integrated with CloudWatch Logs.",
"Risk": "If logs are not enabled, monitoring of service use and threat analysis is not possible.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-instance --db-instance-identifier <db_instance_id> --cloudwatch-logs-export-configuration {'EnableLogTypes':['audit',error','general','slowquery']} --apply-immediately",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/log-exports.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-respective-logs-of-amazon-relational-database-service-amazon-rds-are-enabled#terraform"
},
"Recommendation": {
"Text": "Use CloudWatch Logs to perform real-time analysis of the log data. Create alarms and view metrics.",
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,21 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_integration_cloudwatch_logs(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.cloudwatch_logs:
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} is shipping {' '.join(db_instance.cloudwatch_logs)} to CloudWatch Logs."
else:
report.status = "FAIL"
report.status_extended = f"RDS Instance {db_instance.id} does not have CloudWatch Logs enabled."
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_integration_cloudwatch_logs:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs import (
rds_instance_integration_cloudwatch_logs,
)
check = rds_instance_integration_cloudwatch_logs()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_logs(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs import (
rds_instance_integration_cloudwatch_logs,
)
check = rds_instance_integration_cloudwatch_logs()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have CloudWatch Logs enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_logs(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
EnableCloudwatchLogsExports=["audit", "error"],
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_integration_cloudwatch_logs.rds_instance_integration_cloudwatch_logs import (
rds_instance_integration_cloudwatch_logs,
)
check = rds_instance_integration_cloudwatch_logs()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is shipping audit error to CloudWatch Logs",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_minor_version_upgrade_enabled",
"CheckTitle": "Ensure RDS instances have minor version upgrade enabled.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "low",
"ResourceType": "AwsRdsDbInstance",
"Description": "Ensure RDS instances have minor version upgrade enabled.",
"Risk": "Auto Minor Version Upgrade is a feature that you can enable to have your database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs and therefore should be applied.",
"RelatedUrl": "https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-instance --db-instance-identifier <db_instance_id> --auto-minor-version-upgrade --apply-immediately",
"NativeIaC": "https://docs.bridgecrew.io/docs/ensure-aws-db-instance-gets-all-minor-upgrades-automatically#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-auto-minor-version-upgrade.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-aws-db-instance-gets-all-minor-upgrades-automatically#terraform"
},
"Recommendation": {
"Text": "Enable auto minor version upgrade for all databases and environments.",
"Url": "https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_minor_version_upgrade_enabled(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.auto_minor_version_upgrade:
report.status = "PASS"
report.status_extended = (
f"RDS Instance {db_instance.id} has minor version upgrade enabled."
)
else:
report.status = "FAIL"
report.status_extended = f"RDS Instance {db_instance.id} does not have minor version upgrade enabled."
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_minor_version_upgrade_enabled:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled import (
rds_instance_minor_version_upgrade_enabled,
)
check = rds_instance_minor_version_upgrade_enabled()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_auto_upgrade(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled import (
rds_instance_minor_version_upgrade_enabled,
)
check = rds_instance_minor_version_upgrade_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have minor version upgrade enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_auto_upgrade(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
AutoMinorVersionUpgrade=True,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_minor_version_upgrade_enabled.rds_instance_minor_version_upgrade_enabled import (
rds_instance_minor_version_upgrade_enabled,
)
check = rds_instance_minor_version_upgrade_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has minor version upgrade enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_multi_az",
"CheckTitle": "Check if RDS instances have multi-AZ enabled.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "medium",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances have multi-AZ enabled.",
"Risk": "In case of failure, with a single-AZ deployment configuration, should an availability zone specific database failure occur, Amazon RDS can not automatically fail over to the standby availability zone.",
"RelatedUrl": "https://aws.amazon.com/rds/features/multi-az/",
"Remediation": {
"Code": {
"CLI": "aws rds create-db-instance --db-instance-identifier <db_instance_id> --multi-az true",
"NativeIaC": "https://docs.bridgecrew.io/docs/general_73#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-multi-az.html",
"Terraform": "https://docs.bridgecrew.io/docs/general_73#terraform"
},
"Recommendation": {
"Text": "Enable multi-AZ deployment for production databases.",
"Url": "https://aws.amazon.com/rds/features/multi-az/"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_multi_az(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.multi_az:
report.status = "PASS"
report.status_extended = (
f"RDS Instance {db_instance.id} has multi-AZ enabled."
)
else:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance {db_instance.id} does not have multi-AZ enabled."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_multi_az:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az import (
rds_instance_multi_az,
)
check = rds_instance_multi_az()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_multi_az(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az import (
rds_instance_multi_az,
)
check = rds_instance_multi_az()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have multi-AZ enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_multi_az(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
MultiAZ=True,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_multi_az.rds_instance_multi_az import (
rds_instance_multi_az,
)
check = rds_instance_multi_az()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has multi-AZ enabled",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_no_public_access",
"CheckTitle": "Ensure there are no Public Accessible RDS instances.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "critical",
"ResourceType": "AwsRdsDbInstance",
"Description": "Ensure there are no Public Accessible RDS instances.",
"Risk": "Publicly accessible databases could expose sensitive data to bad actors.",
"RelatedUrl": "https://docs.amazonaws.cn/en_us/config/latest/developerguide/rds-instance-public-access-check.html",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-instance --db-instance-identifier <db_instance_id> --no-publicly-accessible --apply-immediately",
"NativeIaC": "https://docs.bridgecrew.io/docs/public_2#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-publicly-accessible.html",
"Terraform": "https://docs.bridgecrew.io/docs/public_2#terraform"
},
"Recommendation": {
"Text": "Using an AWS Config rule check for RDS public instances periodically and check there is a business reason for it.",
"Url": "https://docs.amazonaws.cn/en_us/config/latest/developerguide/rds-instance-public-access-check.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,25 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_no_public_access(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if not db_instance.public:
report.status = "PASS"
report.status_extended = (
f"RDS Instance {db_instance.id} is not Publicly Accessible."
)
else:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance {db_instance.id} is set as Publicly Accessible."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_no_public_access:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access import (
rds_instance_no_public_access,
)
check = rds_instance_no_public_access()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_private(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access import (
rds_instance_no_public_access,
)
check = rds_instance_no_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is not Publicly Accessible",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_public(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
PubliclyAccessible=True,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_no_public_access.rds_instance_no_public_access import (
rds_instance_no_public_access,
)
check = rds_instance_no_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is set as Publicly Accessible",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_instance_storage_encrypted",
"CheckTitle": "Check if RDS instances storage is encrypted.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "medium",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances storage is encrypted.",
"Risk": "If not enabled sensitive information at rest is not protected.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html",
"Remediation": {
"Code": {
"CLI": "aws rds create-db-instance --db-instance-identifier <db_instance_id> --db-instance-class <instance_class> --engine <engine> --storage-encrypted true",
"NativeIaC": "https://docs.bridgecrew.io/docs/general_4#cloudformation",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-encryption-enabled.html",
"Terraform": "https://docs.bridgecrew.io/docs/general_4#terraform"
},
"Recommendation": {
"Text": "Enable Encryption. Use a CMK where possible. It will provide additional management and privacy benefits.",
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_instance_storage_encrypted(Check):
def execute(self):
findings = []
for db_instance in rds_client.db_instances:
report = Check_Report(self.metadata)
report.region = db_instance.region
report.resource_id = db_instance.id
if db_instance.encrypted:
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} is encrypted."
else:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance {db_instance.id} is not encrypted."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,101 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_instance_storage_encrypted:
@mock_rds
def test_rds_no_instances(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted import (
rds_instance_storage_encrypted,
)
check = rds_instance_storage_encrypted()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_instance_no_encryption(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted import (
rds_instance_storage_encrypted,
)
check = rds_instance_storage_encrypted()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is not encrypted",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"
@mock_rds
def test_rds_instance_with_encryption(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
StorageEncrypted=True,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_instance_storage_encrypted.rds_instance_storage_encrypted import (
rds_instance_storage_encrypted,
)
check = rds_instance_storage_encrypted()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is encrypted",
result[0].status_extended,
)
assert result[0].resource_id == "db-master-1"

View File

@@ -0,0 +1,176 @@
import threading
from typing import Optional
from pydantic import BaseModel
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################## RDS
class RDS:
def __init__(self, audit_info):
self.service = "rds"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.db_instances = []
self.db_snapshots = []
self.db_cluster_snapshots = []
self.__threading_call__(self.__describe_db_instances__)
self.__threading_call__(self.__describe_db_snapshots__)
self.__threading_call__(self.__describe_db_snapshot_attributes__)
self.__threading_call__(self.__describe_db_cluster_snapshots__)
self.__threading_call__(self.__describe_db_cluster_snapshot_attributes__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_db_instances__(self, regional_client):
logger.info("RDS - Describe Instances...")
try:
describe_db_instances_paginator = regional_client.get_paginator(
"describe_db_instances"
)
for page in describe_db_instances_paginator.paginate():
for instance in page["DBInstances"]:
self.db_instances.append(
DBInstance(
id=instance["DBInstanceIdentifier"],
endpoint=instance["Endpoint"]["Address"],
status=instance["DBInstanceStatus"],
public=instance["PubliclyAccessible"],
encrypted=instance["StorageEncrypted"],
auto_minor_version_upgrade=instance[
"AutoMinorVersionUpgrade"
],
backup_retention_period=instance.get(
"BackupRetentionPeriod"
),
cloudwatch_logs=instance.get(
"EnabledCloudwatchLogsExports"
),
deletion_protection=instance["DeletionProtection"],
enhanced_monitoring_arn=instance.get(
"EnhancedMonitoringResourceArn"
),
multi_az=instance["MultiAZ"],
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_db_snapshots__(self, regional_client):
logger.info("RDS - Describe Snapshots...")
try:
describe_db_snapshots_paginator = regional_client.get_paginator(
"describe_db_snapshots"
)
for page in describe_db_snapshots_paginator.paginate():
for snapshot in page["DBSnapshots"]:
self.db_snapshots.append(
DBSnapshot(
id=snapshot["DBSnapshotIdentifier"],
instance_id=snapshot["DBInstanceIdentifier"],
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_db_snapshot_attributes__(self, regional_client):
logger.info("RDS - Describe Snapshot Attributes...")
try:
for snapshot in self.db_snapshots:
if snapshot.region == regional_client.region:
response = regional_client.describe_db_snapshot_attributes(
DBSnapshotIdentifier=snapshot.id
)["DBSnapshotAttributesResult"]
for att in response["DBSnapshotAttributes"]:
if "all" in att["AttributeValues"]:
snapshot.public = True
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_db_cluster_snapshots__(self, regional_client):
logger.info("RDS - Describe Cluster Snapshots...")
try:
describe_db_snapshots_paginator = regional_client.get_paginator(
"describe_db_cluster_snapshots"
)
for page in describe_db_snapshots_paginator.paginate():
for snapshot in page["DBClusterSnapshots"]:
self.db_cluster_snapshots.append(
ClusterSnapshot(
id=snapshot["DBClusterSnapshotIdentifier"],
cluster_id=snapshot["DBClusterIdentifier"],
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_db_cluster_snapshot_attributes__(self, regional_client):
logger.info("RDS - Describe Cluster Snapshot Attributes...")
try:
for snapshot in self.db_cluster_snapshots:
if snapshot.region == regional_client.region:
response = regional_client.describe_db_cluster_snapshot_attributes(
DBClusterSnapshotIdentifier=snapshot.id
)["DBClusterSnapshotAttributesResult"]
for att in response["DBClusterSnapshotAttributes"]:
if "all" in att["AttributeValues"]:
snapshot.public = True
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class DBInstance(BaseModel):
id: str
endpoint: str
status: str
public: bool
encrypted: bool
backup_retention_period: int = 0
cloudwatch_logs: Optional[list]
deletion_protection: bool
auto_minor_version_upgrade: bool
enhanced_monitoring_arn: Optional[str]
multi_az: bool
region: str
class DBSnapshot(BaseModel):
id: str
instance_id: str
public: bool = False
region: str
class ClusterSnapshot(BaseModel):
id: str
cluster_id: str
public: bool = False
region: str

View File

@@ -0,0 +1,150 @@
from boto3 import client, session
from moto import mock_rds
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.rds.rds_service import RDS
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
class Test_RDS_Service:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test Dynamo Service
@mock_rds
def test_service(self):
# Dynamo client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert rds.service == "rds"
# Test Dynamo Client
@mock_rds
def test_client(self):
# Dynamo client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
for regional_client in rds.regional_clients.values():
assert regional_client.__class__.__name__ == "RDS"
# Test Dynamo Session
@mock_rds
def test__get_session__(self):
# Dynamo client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert rds.session.__class__.__name__ == "Session"
# Test Dynamo Session
@mock_rds
def test_audited_account(self):
# Dynamo client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert rds.audited_account == AWS_ACCOUNT_NUMBER
# Test RDS Describe DB Instances
@mock_rds
def test__describe_db_instances__(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
StorageEncrypted=True,
DeletionProtection=True,
PubliclyAccessible=True,
AutoMinorVersionUpgrade=True,
BackupRetentionPeriod=10,
EnableCloudwatchLogsExports=["audit", "error"],
MultiAZ=True,
)
# RDS client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert len(rds.db_instances) == 1
assert rds.db_instances[0].id == "db-master-1"
assert rds.db_instances[0].region == AWS_REGION
assert (
rds.db_instances[0].endpoint
== "db-master-1.aaaaaaaaaa.us-east-1.rds.amazonaws.com"
)
assert rds.db_instances[0].status == "available"
assert rds.db_instances[0].public
assert rds.db_instances[0].encrypted
assert rds.db_instances[0].backup_retention_period == 10
assert rds.db_instances[0].cloudwatch_logs == ["audit", "error"]
assert rds.db_instances[0].deletion_protection
assert rds.db_instances[0].auto_minor_version_upgrade
assert rds.db_instances[0].multi_az
# Test RDS Describe DB Snapshots
@mock_rds
def test__describe_db_snapshots__(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
conn.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
)
# RDS client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert len(rds.db_snapshots) == 1
assert rds.db_snapshots[0].id == "snapshot-1"
assert rds.db_snapshots[0].instance_id == "db-primary-1"
assert rds.db_snapshots[0].region == AWS_REGION
assert not rds.db_snapshots[0].public
# Test RDS Describe DB Cluster Snapshots
@mock_rds
def test__describe_db_cluster_snapshots__(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1"
)
# RDS client for this test class
audit_info = self.set_mocked_audit_info()
rds = RDS(audit_info)
assert len(rds.db_cluster_snapshots) == 1
assert rds.db_cluster_snapshots[0].id == "snapshot-1"
assert rds.db_cluster_snapshots[0].cluster_id == "db-primary-1"
assert rds.db_cluster_snapshots[0].region == AWS_REGION
assert not rds.db_cluster_snapshots[0].public

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "rds_snapshots_public_access",
"CheckTitle": "Check if RDS Snapshots and Cluster Snapshots are public.",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:snapshot",
"Severity": "critical",
"ResourceType": "AwsRdsDbSnapshot",
"Description": "Check if RDS Snapshots and Cluster Snapshots are public.",
"Risk": "Publicly accessible services could expose sensitive data to bad actors. t is recommended that your RDS snapshots should not be public in order to prevent potential leak or misuse of sensitive data or any other kind of security threat. If your RDS snapshot is public, then the data which is backed up in that snapshot is accessible to all other AWS accounts.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/rds-snapshots-public-prohibited.html",
"Remediation": {
"Code": {
"CLI": "aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot_id> --attribute-name restore --values-to-remove all",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/public-snapshots.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Use AWS Config to identify any snapshot that is public.",
"Url": "https://docs.aws.amazon.com/config/latest/developerguide/rds-snapshots-public-prohibited.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,40 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.rds.rds_client import rds_client
class rds_snapshots_public_access(Check):
def execute(self):
findings = []
for db_snap in rds_client.db_snapshots:
report = Check_Report(self.metadata)
report.region = db_snap.region
report.resource_id = db_snap.id
if db_snap.public:
report.status = "FAIL"
report.status_extended = (
f"RDS Instance Snapshot {db_snap.id} is public."
)
else:
report.status = "PASS"
report.status_extended = (
f"RDS Instance Snapshot {db_snap.id} is not shared."
)
findings.append(report)
for db_snap in rds_client.db_cluster_snapshots:
report = Check_Report(self.metadata)
report.region = db_snap.region
report.resource_id = db_snap.id
if db_snap.public:
report.status = "FAIL"
report.status_extended = f"RDS Cluster Snapshot {db_snap.id} is public."
else:
report.status = "PASS"
report.status_extended = (
f"RDS Cluster Snapshot {db_snap.id} is not shared."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,190 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_rds
AWS_REGION = "us-east-1"
class Test_rds_snapshots_public_access:
@mock_rds
def test_rds_no_snapshots(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access import (
rds_snapshots_public_access,
)
check = rds_snapshots_public_access()
result = check.execute()
assert len(result) == 0
@mock_rds
def test_rds_private_snapshot(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
conn.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access import (
rds_snapshots_public_access,
)
check = rds_snapshots_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is not shared",
result[0].status_extended,
)
assert result[0].resource_id == "snapshot-1"
@mock_rds
def test_rds_public_snapshot(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
)
conn.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access.rds_client",
new=RDS(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access import (
rds_snapshots_public_access,
)
service_client.db_snapshots[0].public = True
check = rds_snapshots_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is public",
result[0].status_extended,
)
assert result[0].resource_id == "snapshot-1"
@mock_rds
def test_rds_cluster_private_snapshot(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1"
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access.rds_client",
new=RDS(current_audit_info),
):
# Test Check
from providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access import (
rds_snapshots_public_access,
)
check = rds_snapshots_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is not shared",
result[0].status_extended,
)
assert result[0].resource_id == "snapshot-1"
@mock_rds
def test_rds_cluster_public_snapshot(self):
conn = client("rds", region_name=AWS_REGION)
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="snapshot-1"
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.rds.rds_service import RDS
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access.rds_client",
new=RDS(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.rds.rds_snapshots_public_access.rds_snapshots_public_access import (
rds_snapshots_public_access,
)
service_client.db_cluster_snapshots[0].public = True
check = rds_snapshots_public_access()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is public",
result[0].status_extended,
)
assert result[0].resource_id == "snapshot-1"