Amazon Configuration Review Checklist
SQS Queues with Encryption Control
for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
echo "Region: $region"
queue_urls=($(aws sqs list-queues --region "$region" --query 'QueueUrls[*]' --output text))
for queue_url in "${queue_urls[@]}"; do
echo " Queue URL: $queue_url"
aws sqs get-queue-attributes --region "$region" --queue-url "$queue_url" --attribute-names "SqsManagedSseEnabled" "KmsMasterKeyId" --query 'Attributes'
done
done
EBS Volumes Encryption Control
aws ec2 describe-volumes --region eu-west1 --query 'Volumes[?Encrypted==`false`].[VolumeId,Encrypted,Tags[?Key==`Name`].Value | [0]]'
This settings can check in EC2 > Elastic Block Store > Volumes > (Click the volumes check button) > control the Encryption section on the AWS console.
Remember to change the region on the command.
Root Account Hardware MFA Control
List the MFA devices along with their corresponding users.
aws iam list-mfa-devices --query 'MFADevices[*].[SerialNumber,User.Arn]' --output json
List the virtual MFA devices along with their corresponding users.
aws iam list-virtual-mfa-devices --assignment-status Assigned --query 'VirtualMFADevices[*].[SerialNumber,User.Arn]' --output json | jq .
This settings can check in the IAM > Users > MFA section on AWS console.
Private EBS Snapshot Encryption Control
aws ec2 describe-snapshots –-region eu-west-1 –-owner-ids <Owner ID>
This settings can check in EC2 > Elastic Block Store > Snapshots > (Click the snapshots check button) > control the Encryption section on the AWS console.
Dropping Invalid Header Fields Control
for region in $(aws ec2 describe-regions --region eu-central-1 --query 'Regions[*].RegionName' --output text); do
echo "Load balancers in region $region:"
for arn in $(aws elbv2 describe-load-balancers --region $region --query 'LoadBalancers[].LoadBalancerArn' --output text); do
drop_invalid_header_fields=$(aws elbv2 describe-load-balancer-attributes --region $region --load-balancer-arn $arn --query 'Attributes[?(Key == `routing.http.drop_invalid_header_fields.enabled`)].Value | [0]' --output text)
if [ "$drop_invalid_header_fields" == "false" ]; then
echo " Load Balancer ARN: $arn"
echo " Drop Invalid Header Fields Enabled: $drop_invalid_header_fields"
fi
done
echo ""
done
This settings can check in EC2 > Load Balancers > (Click the Load Balancer check button) > Attributes > control the ‘Drop invalid header fields’ section on the AWS console.
ELB Load Balancers Clear-Text (HTTP) Communication Control
for region in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
echo "Load balancer listeners with HTTP protocol in region $region:"
aws elb describe-load-balancers --region $region --query 'LoadBalancerDescriptions[?ListenerDescriptions[?Listener.Protocol==`HTTP`]].{LoadBalancerName: LoadBalancerName, Protocol: ListenerDescriptions[].Listener.Protocol}' --output table
done
This settings can check in EC2 > Load Balancer > (Click the Load Balancer check button) > Listeners and rules > control the protocols on the AWS console.
ELB HTTPS Load Balancer SSL/TLS Policy Control
# Get all AWS regions
for region in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
echo -e "\n================= Region: $region ================="
# Get all Classic Load Balancer names in the region
elbs=$(aws elb describe-load-balancers \
--region "$region" \
--query 'LoadBalancerDescriptions[].LoadBalancerName' \
--output text 2>/dev/null)
# Skip if no ELBs found
if [[ -z "$elbs" ]]; then
echo "No Classic Load Balancers found in region $region."
continue
fi
# Loop over each ELB
for name in $elbs; do
echo -e "\n#---------- SSL/TLS Policies for Classic Load Balancer: $name ---------#"
aws elb describe-load-balancer-policies \
--region "$region" \
--load-balancer-name "$name" \
--query 'PolicyDescriptions[?PolicyTypeName==`SSLNegotiationPolicyType`].{PolicyName: PolicyName, PolicyTypeName: PolicyTypeName}' \
--output table 2>/dev/null
done
done
This settings can check in EC2 > Load Balancer > (Click the Load Balancer check button) > Listeners and rules > control the protocols on the AWS console.
Inline role Policy Allows ‘iam:PassRole’ For All Resources or all IAM Role Resource Control
aws iam get-role-policy --role-name <RoleName> --policy-name SsmOnboardingInlinePolicy --query 'PolicyDocument'; aws iam get-role-policy --role-name <RoleName> --policy-name STS_Assume_Role --query 'PolicyDocument'
EC2 Instance Metadata Service Version 2 (IMDSv2) Control
aws ec2 describe-instances --region eu-west-1 --query 'Reservations[].Instances[].{InstanceId: InstanceId, MetadataOptions: MetadataOptions.HttpTokens}' --output table
This settings can check in EC2 > Instances > (Click the Instance check button) > Description > Metadata section on the AWS console.
Non-Default Security Group Control for that Open All Ports to All Ingress Sources (TCP)
for region in `aws ec2 describe-regions --region eu-central-1 --query Regions[*].[RegionName] --output text`; do echo -e "\n #---------- Listing security groups in region: $region --------- #"; for secGroupIPPerm in $(aws ec2 describe-security-groups --region $region --output text --query 'SecurityGroups[*].GroupId'); do echo -e "\n------Listing IP Permissions in group: $secGroupIPPerm ..."; aws ec2 describe-security-groups --region $region --group-ids $secGroupIPPerm --query 'SecurityGroups[*].IpPermissions[]'; done; done;
Cross-Account AssumeRule Policy Lacks External ID and MFA Control
aws iam get-role --role-name Cloudreach_Automated_Patching --query 'Role.AssumeRolePolicyDocument'
Managed Policies Allow All Action or Uses Wildcard in Actions Control
aws iam get-policy-version --policy-arn "arn:aws:iam::aws:policy/AdministratorAccess" --version-id v1 --query 'PolicyVersion.Document' && aws iam get-policy-version --policy-arn "arn:aws:iam::723974773212:policy/RI-TransitGateway-Policy" --version-id v1 --query 'PolicyVersion.Document'
Bucket Access Logging Control
for bucket_name in $(aws s3api list-buckets --query "Buckets[*].Name" --output text); do echo "Checking whether access logging is enabled for Bucket: $bucket_name" aws s3api get-bucket-logging --bucket "$bucket_name" --query 'LoggingEnabled'; done
Bucket Clear-text (HTTP) Communication Control
for bucket_name in $(aws s3api list-buckets --query "Buckets[*].Name" --output text); do
echo "Checking whether secure transport enabled attribute is either present or enabled for Bucket: $bucket_name"
policy_output=$(aws s3api get-bucket-policy --bucket "$bucket_name" --query 'Policy' 2>&1)
if [[ $policy_output == *"NoSuchBucketPolicy"* ]]; then
echo "No bucket policy found for $bucket_name"
else
echo "$policy_output" | jq --arg bucket "$bucket_name" -r 'try(if (.secure_transport_enabled == false) then "Secure transport is set to false for Bucket: \($bucket)" else "Secure transport is set to true for Bucket: \($bucket)" end) catch "Secure_transport_enabled attribute not found on bucket \($bucket)."'
fi
echo ""
done
Bucket Server-Side Encryption Control
for bucket_name in $(aws s3api list-buckets --query "Buckets[*].Name" --output text); do
echo "Checking whether server-side encryption is enabled for Bucket: $bucket_name"
policy_output=$(aws s3api get-bucket-policy --bucket "$bucket_name" --query 'Policy' 2>&1)
done;
Bucket Public Access Control
for s3Buckets in `aws s3api list-buckets --query 'Buckets[*].Name' --output text | tr '\t' '\n'`; do echo -e "\n #---------- Listing versioning status properties for S3 bucket: $s3Buckets --------- #"; aws s3api get-bucket-acl --bucket $s3Buckets --query 'Grants[?(Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`)]'; done;
Network ACLs with Allow All Engress Traffic Rule (Default) Control
for region in `aws ec2 describe-regions --region us-east-1 --query Regions[*].[RegionName] --output text `; do echo -e "\n #---------- Listing ACLs in region: $region --------- #"; for acl in $(aws ec2 describe-network-acls --region $region --output text --query 'NetworkAcls[*].NetworkAclId' ); do echo -e "\n------Listing ACL details in ACL: $acl ..."; aws ec2 describe-network-acls --region $region --network-acl-ids $acl --query 'NetworkAcls[*].Entries[?(RuleAction==`allow`) && (Egress==`true`)] | []' ; done; done;
User with both password and keys control
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d | csvjson | jq 'map(select(.password_enabled == "true" and (.access_key_1_active == true or .access_key_2_active == true)) | {user, arn, password_enabled, access_key_1_active, access_key_2_active, mfa_active})'
This settings can check in IAM > Users > (Click the User check button) > Security credentials section on the AWS console.
Deletion Protection on Load Balancer Control
for region in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
echo "Load balancer deletion protection status in region $region:"
for arn in $(aws elbv2 describe-load-balancers --region $region --query 'LoadBalancers[?contains(Type, `application`) || contains(Type, `net`)].LoadBalancerArn' --output text); do
echo -e "\n#---------- Deletion protection status for load balancer with the ARN: $arn --------- #"
deletion_protection_enabled=$(aws elbv2 describe-load-balancer-attributes --region $region --load-balancer-arn $arn --query 'Attributes[?Key==`deletion_protection.enabled`].Value' --output text)
echo "Deletion Protection Enabled: $deletion_protection_enabled"
done
done
This settings can check in EC2 > Load Balancing > Load Balancer > (Click the volumes check button) > Attributes > control the ‘Protection’ section on the AWS console.
Bucket MFA Delete Control
for s3Buckets in `aws s3api list-buckets --query 'Buckets[*].Name' --output text | tr '\t' '\n'`; do echo -e "\n #---------- Listing bucket MFA properties for S3 bucket: $s3Buckets --------- #"; aws s3api get-bucket-versioning --bucket $s3Buckets --query 'MFADelete'; done;
This settings can control in S3 > Buckets > (Click the bucket check button) > Properties > control the ‘Multi-factor authentication (MFA) delete’ section on the AWS console.
Alarm without associated action control
aws cloudwatch describe-alarms --region eu-west-1 --query 'MetricAlarms[?length(AlarmActions) == `0`]'
RDP Port Check to All Ingress Sources
for region in $(aws ec2 describe-regions --region eu-central-1 --query 'Regions[].RegionName' --output text);do echo "Region: $region" && aws ec2 describe-security-groups --region "$region" --query "SecurityGroups[?IpPermissions[?ToPort==\`3389\` && IpProtocol==\`tcp\` && IpRanges[?CidrIp=='0.0.0.0/0']]].{Region: \`$region\`, GroupId: GroupId, GroupName: GroupName, Ingress: IpPermissions[?ToPort==\`3389\` && IpProtocol==\`tcp\` && IpRanges[?CidrIp=='0.0.0.0/0']]}"; done
SSH Port Check to All Ingress Sources
for region in $(aws ec2 describe-regions --region eu-central-1 --query 'Regions[].RegionName' --output text);do echo "Region: $region" && aws ec2 describe-security-groups --region $region --query "SecurityGroups[?IpPermissions[?ToPort==\`22\` && IpProtocol==\`tcp\` && IpRanges[?CidrIp=="
Data Events Logging Configuration Check for S3 Buckets
aws cloudtrail describe-trails --region eu-west-1 --trail-name-list riverisland-prod-cloudtrail --query 'trailList[0].{ARN: TrailARN, CloudWatchLogsLogGroupArn: CloudWatchLogsLogGroupArn, CloudWatchLogsRoleArn: CloudWatchLogsRoleArn, DataEventsEnabled: IsMultiRegionTrail}' --output json
### ELBv2 Access Log Control
for region in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
echo "Checking access logs enabled status for ELBv2 load balancers in region $region:"
for arn in $(aws elbv2 describe-load-balancers --region $region --query 'LoadBalancers[?contains(Type, `application`) || contains(Type, `net`)].LoadBalancerArn' --output text); do
echo -e "\n#---------- Load Balancer ARN: $arn --------- #"
logs_enabled=$(aws elbv2 describe-load-balancer-attributes --region "$region" --load-balancer-arn "$arn" --query 'Attributes[?Key==`access_logs.s3.enabled`].Value | [0]')
echo "Access Logs to S3: $logs_enabled"
done
done
Active Access Key Rotation Control
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
# Get all access keys for the user
keys=$(aws iam list-access-keys --user-name $user --query 'AccessKeyMetadata[?Status==`Active`].{AccessKeyId: AccessKeyId, CreateDate: CreateDate}' --output json)
# Loop through each access key and check if it's active and older than 90 days
for key in $(echo "$keys" | jq -r '.[] | @base64'); do
_jq() {
echo "${key}" | base64 --decode | jq -r "${1}"
}
access_key_id=$(_jq '.AccessKeyId')
create_date=$(_jq '.CreateDate')
create_date_unix=$(date -d "$create_date" +%s)
ninety_days_ago=$(date -d 'now - 90 days' +%s)
if [ $create_date_unix -lt $ninety_days_ago ]; then
echo "User: $user - Active Keys:"
echo " Access Key: $access_key_id - Created: $create_date"
echo " Access Key older than 90 days from today."
echo ""
fi
done
done
KSM Customer Master Keys (CMKs) with Rotation Disable Control
for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
echo "Region: $region"
key_ids=($(aws kms list-keys --region "$region" --query 'Keys[*].KeyId' --output text))
for key_id in "${key_ids[@]}"; do
rotation_status=$(aws kms get-key-rotation-status --region "$region" --key-id "$key_id" --query 'KeyRotationEnabled' --output text)
echo " Key ID: $key_id (Rotation Enabled: $rotation_status)"
done
done
Security Group Open UDP Port Control
for region in $(aws ec2 describe-regions --region eu-central-1 --query 'Regions[*].[RegionName]' --output text); do
echo -e "\n #---------- Listing security groups in region: $region --------- #";
for secGroupIPPerm in $(aws ec2 describe-security-groups --region $region --output text --query 'SecurityGroups[*].GroupId'); do
echo -e "\n------Listing IP Permissions in group: $secGroupIPPerm ...";
aws ec2 describe-security-groups --region $region --group-ids $secGroupIPPerm --query 'SecurityGroups[*].IpPermissions[?IpProtocol==`udp`]';
done;
done;
Flow Log Control
for region in $(aws ec2 describe-regions --region eu-central-1 --query 'Regions[].RegionName' --output text); do
echo "Region: $region"
for subnetName in $(aws ec2 describe-subnets --query 'Subnets[*].SubnetId' --region eu-central-1 --output text); do
echo "Subnet: $subnetName"
aws ec2 describe-flow-logs --filter Name=resource-id,Values=$subnetName --region $region --output json
done
done
Unused Security Group Control
EC2 Control
comm -23 <(aws ec2 describe-security-groups --region eu-west-1 --query 'SecurityGroups[*].GroupId' --output text | tr '\t' '\n'| sort) <(aws ec2 describe-instances --region eu-west-1 --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text | tr '\t' '\n' | sort | uniq)
ELB Control
aws elb describe-load-balancers --region eu-west-1 --query 'LoadBalancerDescriptions[*].SecurityGroups[*]' --output text | tr '\t' '\n' | sort | uniq
RDS Control
aws rds describe-db-security-groups --region eu-west-1 --query 'DBSecurityGroups[*].EC2SecurityGroups[*].EC2SecurityGroupId' --output text | tr '\t' '\n' | sort | uniq
Password Policy Control
This settings can check in IAM > Account Management > Account Settings section on the AWS console.