Files
prowler/prowler
2017-07-19 13:33:03 -04:00

64 KiB
Executable File

#!/bin/bash
 
# Prowler is a tool that provides automate auditing and hardening guidance of an AWS account.
# It is based on AWS-CLI commands. It follows guidelines present in the CIS Amazon
# Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
 
# This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
# International Public License. The link to the license terms can be found at
# https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
#
# Author: Toni de la Fuente - @ToniBlyx / Alfresco Software Inc.
 
# Prowler - Iron Maiden
#
# Walking through the city, looking oh so pretty
# I've just got to find my way
# See the ladies flashing
# All there legs and lashes
# I've just got to find my way...
 
# Exit if a pipeline results in an error.
# set -ue
# set -o pipefail
# set -vx
# Exits if any error is found
# set -e
 
OPTRED=""
OPTNORMAL=""
 
# Set the defaults for these getopts variables
PROFILE="default"
REGION="us-east-1"
FILTERREGION=""
MAXITEMS=100
MONOCHROME=0
MODE="text"
SEP=','
KEEPCREDREPORT=0
 
 
# Command usage menu
usage(){
echo "
USAGE:
`basename $0` -p <profile> -r <region> [ -h ]
Options:
-p <profile> specify your AWS profile to use (i.e.: default)
-r <region> specify an AWS region to direct API requests to (i.e.: us-east-1)
-c <checknum> specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1, check3 for entire section 3 or level1 for CIS Level 1 Profile Definitions)
-f <filterregion> specify an AWS region to run checks against (i.e.: us-west-1)
-m <maxitems> specify the maximum number of items to return for long-running requests (default: 100)
-M <mode> output mode: text (defalut), mono, csv (separator is \"${SEP}\"; data is on stdout; progress on stderr)
-k keep the credential report
-h this help
"
exit
}
 
while getopts ":hkp:r:c:f:m:M:" OPTION; do
case $OPTION in
h )
usage
exit 1
;;
k )
KEEPCREDREPORT=1
;;
p )
PROFILE=$OPTARG
;;
r )
REGION=$OPTARG
;;
c )
CHECKNUMBER=$OPTARG
;;
f )
FILTERREGION=$OPTARG
;;
m )
MAXITEMS=$OPTARG
;;
M )
MODE=$OPTARG
;;
: )
echo ""
echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument"
usage
exit 1
;;
? )
echo ""
echo "$OPTRED ERROR!$OPTNORMAL Invalid option"
usage
exit 1
;;
esac
done
 
if [[ $MODE != "mono" && $MODE != "text" && $MODE != "csv" ]]; then
echo ""
echo "$OPTRED ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, or csv."
usage
exit 1
fi
 
if [[ $MODE == "mono" || $MODE == "csv" ]]; then
MONOCHROME=1
fi
 
if [[ $MONOCHROME -eq 1 ]]; then
# Colors
NORMAL=''
WARNING='' # Bad (red)
SECTION='' # Section (yellow)
NOTICE='' # Notice (yellow)
OK='' # Ok (green)
BAD='' # Bad (red)
CYAN=''
BLUE=''
BROWN=''
DARKGRAY=''
GRAY=''
GREEN=''
MAGENTA=''
PURPLE=''
RED=''
YELLOW=''
WHITE=''
else
# Colors
# NOTE: Your editor may NOT show the 0x1b / escape character left of the '['
NORMAL=""
WARNING="" # Bad (red)
SECTION="" # Section (yellow)
NOTICE="" # Notice (yellow)
OK="" # Ok (green)
BAD="" # Bad (red)
CYAN=""
BLUE=""
BROWN=""
DARKGRAY=""
GRAY=""
GREEN=""
MAGENTA=""
PURPLE=""
RED=""
YELLOW=""
WHITE=""
fi
 
SCRIPT_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" )
 
# Functions to manage dates depending on OS
if [[ "$OSTYPE" == "linux-gnu" ]]; then
# function to compare in days, usage how_older_from_today date
# date format %Y-%m-%d
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date -d "$(date +%Y-%m-%d)" +%s)
DATE_FROM_IN_DAYS=$(date -d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
# function to convert from timestamp to date, usage timestamp_to_date timestamp
# output date format %Y-%m-%d
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -d @$TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
decode_report()
{
base64 -d
}
elif [[ "$OSTYPE" == "darwin"* ]]; then
# BSD/OSX coommands compatibility
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date +%s)
DATE_FROM_IN_DAYS=$(date -jf %Y-%m-%d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -r $TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
decode_report()
{
base64 -D
}
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date -d "$(date +%Y-%m-%d)" +%s)
DATE_FROM_IN_DAYS=$(date -d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -d @$TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
decode_report()
{
base64 -d
}
else
echo "Unknown Operating System"
exit
fi
 
if [[ ! -f ~/.aws/credentials ]]; then
echo -e "\n$RED ERROR!$NORMAL AWS credentials file not found (~/.aws/credentials). Run 'aws configure' first. \n"
return 1
fi
 
# AWS-CLI variables
AWSCLI=$(which aws)
if [ -z "${AWSCLI}" ]; then
echo -e "\n$RED ERROR!$NORMAL AWS-CLI (aws command) not found. Make sure it is installed correctly and in your \$PATH\n"
exit
fi
 
# if this script runs in an AWS instance
# INSTANCE_PROFILE=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/)
# AWS_ACCESS_KEY_ID=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
# AWS_SECRET_ACCESS_KEY_ID=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
# AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
# AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_ID}
 
TITLE_ID=""
TITLE_TEXT="CALLER ERROR - UNSET TITLE"
## Output formatting functions
textOK(){
if [[ $MODE == "csv" ]]; then
if [[ $2 ]]; then
REPREGION=$2
else
REPREGION=$REGION
fi
echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}PASS${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1"
else
echo " $OK OK! $NORMAL $1"
fi
}
 
textNotice(){
if [[ $MODE == "csv" ]]; then
if [[ $2 ]]; then
REPREGION=$2
else
REPREGION=$REGION
fi
echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}INFO${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1"
else
echo " $NOTICE INFO! $1 $NORMAL"
fi
}
 
textWarn(){
if [[ $MODE == "csv" ]]; then
if [[ $2 ]]; then
REPREGION=$2
else
REPREGION=$REGION
fi
echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}WARNING${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1"
else
echo " $BAD WARNING! $1 $NORMAL"
fi
}
 
textTitle(){
TITLE_ID=$1
TITLE_TEXT=$2
 
if [[ $3 ]]; then
ITEM_SCORED=$3
else
ITEM_SCORED="1"
fi
 
if [[ $MODE == "csv" ]]; then
>&2 echo "$TITLE_ID $TITLE_TEXT"
else
if [[ $ITEM_SCORED == "1" ]]; then
echo -e "\n$BLUE $TITLE_ID $NORMAL $TITLE_TEXT"
else
echo -e "\n$PURPLE $TITLE_ID $TITLE_TEXT $NORMAL"
fi
fi
}
 
printCsvHeader() {
>&2 echo ""
>&2 echo "Generating \"${SEP}\" delimited report on stdout for profile $PROFILE, account $ACCOUNT_NUM"
echo "PROFILE${SEP}ACCOUNT_NUM${SEP}REGION${SEP}TITLE_ID${SEP}RESULT${SEP}SCORED${SEP}TITLE_TEXT${SEP}NOTES"
}
 
prowlerBanner() {
echo -e "$CYAN _"
echo -e " _ __ _ __ _____ _| | ___ _ __"
echo -e " | '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|"
echo -e " | |_) | | | (_) \ V V /| | __/ |"
echo -e " | .__/|_| \___/ \_/\_/ |_|\___|_|"
echo -e " |_|$NORMAL$BLUE CIS based AWS Account Hardening Tool$NORMAL\n"
}
 
# Get whoami in AWS, who is the user running this shell script
getWhoami(){
ACCOUNT_NUM=$($AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION --query "Account" | tr -d '"')
if [[ $MODE == "csv" ]]; then
CALLER_ARN_RAW=$($AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION --query "Arn")
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
exit 2
fi
CALLER_ARN=$(echo $CALLER_ARN_RAW | tr -d '"')
textTitle "0.0" "Show report generation info"
textNotice "ARN: $CALLER_ARN TIMESTAMP: $SCRIPT_START_TIME"
else
echo ""
echo "This report is being generated using credentials below:"
echo ""
echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]$NORMAL\n"
if [[ $MONOCHROME -eq 1 ]]; then
echo "Caller Identity:"
$AWSCLI sts get-caller-identity --output text --profile $PROFILE --region $REGION --query "Arn"
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
exit 2
fi
echo ""
else
echo "Caller Identity:"
$AWSCLI sts get-caller-identity --output table --profile $PROFILE --region $REGION
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
exit 2
fi
echo ""
fi
fi
}
 
printCurrentDate(){
textNotice "Date: $(date)"
}
 
printColorsCode(){
if [[ $MONOCHROME -eq 0 ]]; then
echo -e "\nColors Code for results: $NOTICE INFORMATIVE$NORMAL,$OK OK (RECOMMENDED VALUE)$NORMAL, $BAD WARNING (FIX REQUIRED)$NORMAL \n"
fi
}
 
# Generate Credential Report
genCredReport() {
textTitle "0.1" "Generating AWS IAM Credential Report..."
until $( $AWSCLI iam generate-credential-report --output text --query 'State' --profile $PROFILE --region $REGION |grep -q -m 1 "COMPLETE") ; do
sleep 1
done
}
 
# Save report to a file, decode it, deletion at finish and after every single check, acb stands for AWS CIS Benchmark
saveReport(){
TEMP_REPORT_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-XXXXX.cred_report )
$AWSCLI iam get-credential-report --query 'Content' --output text --profile $PROFILE --region $REGION | decode_report > $TEMP_REPORT_FILE
if [[ $KEEPCREDREPORT -eq 1 ]]; then
textTitle "0.2" "Saving IAM Credential Report ..."
textNotice "IAM Credential Report saved in $TEMP_REPORT_FILE"
fi
}
 
# Delete temporary report file
cleanTemp(){
if [[ $KEEPCREDREPORT -ne 1 ]]; then
rm -fr $TEMP_REPORT_FILE
fi
}
 
# Delete the temporary report file if we get interrupted/terminated
trap cleanTemp EXIT
 
# Get a list of all available AWS Regions
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \
--output text \
--profile $PROFILE \
--region $REGION \
--region-names $FILTERREGION)
 
infoReferenceLong(){
# Report review note:
textNotice "For more information:"
textNotice "https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf"
textNotice "For bugs or feedback:"
textNotice "https://github.com/Alfresco/aws-cis-security-benchmark/issues"
}
 
 
infoReferenceShort(){
# Report review note:
textNotice "http://bit.ly/2g3PEf7"
}
 
check11(){
ID11="1.1"
TITLE11="Avoid the use of the root account (Scored)."
COMMAND11=$(cat $TEMP_REPORT_FILE| grep '<root_account>' | cut -d, -f5,11,16 | sed 's/,/\ /g')
textTitle "$ID11" "$TITLE11"
textNotice "Root account last accessed (password key_1 key_2): $COMMAND11"
}
 
check12(){
ID12="1.2"
TITLE12="Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)"
# List users with password enabled
COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }')
COMMAND12=$(
for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do
cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$8 }' |grep -w $i| grep false | awk '{ print $1 }'
done)
textTitle "$ID12" "$TITLE12"
if [[ $COMMAND12 ]]; then
for u in $COMMAND12; do
textWarn "User $u has Password enabled but MFA disabled"
done
else
textOK "No users found with Password enabled and MFA disabled"
fi
}
 
check13(){
ID13="1.3"
TITLE13="Ensure credentials unused for 90 days or greater are disabled (Scored)"
textTitle "$ID13" "$TITLE13"
COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }')
if [[ $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED ]]; then
COMMAND13=$(
for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do
cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$5 }' |grep $i| awk '{ print $1 }'|tr '\n' ' ';
done)
# list of users that have used password
USERS_PASSWORD_USED=$($AWSCLI iam list-users --query "Users[?PasswordLastUsed].UserName" --output text --profile $PROFILE --region $REGION)
if [[ $USERS_PASSWORD_USED ]]; then
# look for users with a password last used more or equal to 90 days
for i in $USERS_PASSWORD_USED; do
DATEUSED=$($AWSCLI iam list-users --query "Users[?UserName=='$i'].PasswordLastUsed" --output text --profile $PROFILE --region $REGION | cut -d'T' -f1)
HOWOLDER=$(how_older_from_today $DATEUSED)
if [ $HOWOLDER -gt "90" ];then
textWarn "User \"$i\" has not logged in during the last 90 days "
else
textOK "User \"$i\" found with credentials used in the last 90 days"
fi
done
fi
else
textOK "No users found with password enabled"
fi
 
}
 
check14(){
ID14="1.4"
TITLE14="Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey
LIST_OF_USERS_WITH_ACCESS_KEY1=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $9 }' |grep "\ true" | awk '{ print $1 }')
LIST_OF_USERS_WITH_ACCESS_KEY2=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $14 }' |grep "\ true" | awk '{ print $1 }')
textTitle "$ID14" "$TITLE14"
C14_NUM_USERS1=0
C14_NUM_USERS2=0
if [[ $LIST_OF_USERS_WITH_ACCESS_KEY1 ]]; then
# textWarn "Users with access key 1 older than 90 days:"
for user in $LIST_OF_USERS_WITH_ACCESS_KEY1; do
# check access key 1
DATEROTATED1=$(cat $TEMP_REPORT_FILE | grep $user| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }')
HOWOLDER=$(how_older_from_today $DATEROTATED1)
 
if [ $HOWOLDER -gt "90" ];then
textWarn " $user has not rotated access key1 in over 90 days "
C14_NUM_USERS1=$(expr $C14_NUM_USERS1 + 1)
fi
done
if [[ $C14_NUM_USERS1 -eq 0 ]]; then
textOK "No users with access key 1 older than 90 days."
fi
else
textOK "No users with access key 1."
fi
 
if [[ $LIST_OF_USERS_WITH_ACCESS_KEY2 ]]; then
# textWarn "Users with access key 2 older than 90 days:"
for user in $LIST_OF_USERS_WITH_ACCESS_KEY2; do
# check access key 2
DATEROTATED2=$(cat $TEMP_REPORT_FILE | grep $user| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }')
HOWOLDER=$(how_older_from_today $DATEROTATED2)
if [ $HOWOLDER -gt "90" ];then
textWarn " $user has not rotated access key2. "
C14_NUM_USERS2=$(expr $C14_NUM_USERS2 + 1)
fi
done
if [[ $C14_NUM_USERS2 -eq 0 ]]; then
textOK "No users with access key 2 older than 90 days."
fi
else
textOK "No users with access key 2."
fi
}
 
check15(){
ID15="1.5"
TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)"
COMMAND15=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json --query 'PasswordPolicy.RequireUppercaseCharacters') # must be true
textTitle "$ID15" "$TITLE15"
if [[ $COMMAND15 == "true" ]];then
textOK "Password Policy requires upper case"
else
textWarn "Password Policy missing upper-case requirement"
fi
}
 
check16(){
ID16="1.6"
TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)"
COMMAND16=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json --query 'PasswordPolicy.RequireLowercaseCharacters') # must be true
textTitle "$ID16" "$TITLE16"
if [[ $COMMAND16 == "true" ]];then
textOK "Password Policy requires lower case"
else
textWarn "Password Policy missing lower-case requirement"
fi
}
 
check17(){
ID17="1.7"
TITLE17="Ensure IAM password policy require at least one symbol (Scored)"
COMMAND17=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json --query 'PasswordPolicy.RequireSymbols') # must be true
textTitle "$ID17" "$TITLE17"
if [[ $COMMAND17 == "true" ]];then
textOK "Password Policy requires symbol"
else
textWarn "Password Policy missing symbol requirement"
fi
}
 
check18(){
ID18="1.8"
TITLE18="Ensure IAM password policy require at least one number (Scored)"
COMMAND18=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json --query 'PasswordPolicy.RequireNumbers') # must be true
textTitle "$ID18" "$TITLE18"
if [[ $COMMAND18 == "true" ]];then
textOK "Password Policy requires number"
else
textWarn "Password Policy missing number requirement"
fi
}
 
check19(){
ID19="1.9"
TITLE19="Ensure IAM password policy requires minimum length of 14 or greater (Scored)"
COMMAND19=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json --query 'PasswordPolicy.MinimumPasswordLength')
textTitle "$ID19" "$TITLE19"
if [[ $COMMAND19 -gt "13" ]];then
textOK "Password Policy requires more than 13 characters"
else
textWarn "Password Policy missing or weak length requirement"
fi
}
 
check110(){
ID110="1.10"
TITLE110="Ensure IAM password policy prevents password reuse (Scored)"
COMMAND110=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text)
textTitle "$ID110" "$TITLE110"
if [[ $COMMAND110 ]];then
if [[ $COMMAND110 -gt "23" ]];then
textOK "Password Policy limits reuse"
else
textWarn "Password Policy has weak reuse requirment (lower than 24)"
fi
else
textWarn "Password Policy missing reuse requirement"
fi
}
 
check111(){
ID111="1.11"
TITLE111="Ensure IAM password policy expires passwords within 90 days or less (Scored)"
COMMAND111=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --output json | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g')
textTitle "$ID111" "$TITLE111"
if [[ $COMMAND111 ]];then
if [ $COMMAND111 == "90" ];then
textOK "Password Policy includes expiration"
fi
else
textWarn "Passowrd expiration not set or set greater than 90 days "
fi
}
 
check112(){
ID112="1.12"
TITLE112="Ensure no root account access key exists (Scored)"
# ensure the access_key_1_active and access_key_2_active fields are set to FALSE.
ROOTKEY1=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $9 }')
ROOTKEY2=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $14 }')
textTitle "$ID112" "$TITLE112"
if [ $ROOTKEY1 == "false" ];then
textOK "No access key 1 found for root"
else
textWarn "Found access key 1 for root "
fi
if [ $ROOTKEY2 == "false" ];then
textOK "No access key 2 found for root"
else
textWarn "Found access key 2 for root "
fi
}
 
check113(){
ID113="1.13"
TITLE113="Ensure MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//')
textTitle "$ID113" "$TITLE113"
if [ $COMMAND113 == "1" ]; then
textOK "Virtual MFA is enabled for root"
else
textWarn "MFA is not ENABLED for root account "
fi
}
 
check114(){
ID114="1.14"
TITLE114="Ensure hardware MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//')
textTitle "$ID114" "$TITLE114"
if [ $COMMAND113 == "1" ]; then
COMMAND114=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l)
if [ $COMMAND114 == "1" ]; then
textOK "Virtual MFA is enabled for root"
else
textOK "Hardware MFA is enabled for root "
fi
else
textWarn "MFA is not ENABLED for root account "
fi
}
 
check115(){
ID115="1.15"
TITLE115="Ensure security questions are registered in the AWS account (Not Scored)"
# No command available
textTitle "$ID115" "$TITLE115"
textNotice "No command available for check 1.15 "
textNotice "Login to the AWS Console as root & click on the Account "
textNotice "Name -> My Account -> Configure Security Challenge Questions "
}
 
check116(){
ID116="1.16"
TITLE116="Ensure IAM policies are attached only to groups or roles (Scored)"
textTitle "$ID116" "$TITLE116"
LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION)
C116_NUM_USERS=0
for user in $LIST_USERS;do
USER_POLICY=$($AWSCLI iam list-attached-user-policies --output text --profile $PROFILE --region $REGION --user-name $user)
if [[ $USER_POLICY ]]; then
textWarn "$user has policy directly attached "
C116_NUM_USERS=$(expr $C116_NUM_USERS + 1)
fi
done
if [[ $C116_NUM_USERS -eq 0 ]]; then
textOK "No policies attached to users."
fi
}
 
check117(){
ID117="1.17"
TITLE117="Enable detailed billing (Scored)"
# No command available
textTitle "$ID117" "$TITLE117"
textNotice "No command available for check 1.17 "
textNotice "See section 1.17 on the CIS Benchmark guide for details "
infoReferenceShort
}
 
check118(){
ID118="1.18"
TITLE118="Ensure IAM Master and IAM Manager roles are active (Scored)"
textTitle "$ID118" "$TITLE118"
FINDMASTERANDMANAGER=$($AWSCLI iam list-roles --profile $PROFILE --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ')
if [[ $FINDMASTERANDMANAGER ]];then
textNotice "Found next roles as possible IAM Master and IAM Manager candidates: "
textNotice "$FINDMASTERANDMANAGER "
textNotice "run the commands below to check their policies with section 1.18 in the guide..."
for role in $FINDMASTERANDMANAGER;do
# find inline policies in found roles
INLINEPOLICIES=$($AWSCLI iam list-role-policies --role-name $role --profile $PROFILE --region $REGION --query "PolicyNames[*]" --output text)
for policy in $INLINEPOLICIES;do
textNotice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION --output json"
done
# find attached policies in found roles
ATTACHEDPOLICIES=$($AWSCLI iam list-attached-role-policies --role-name $role --profile $PROFILE --region $REGION --query "AttachedPolicies[*]" --output text)
for policy in $ATTACHEDPOLICIES;do
textNotice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION --output json"
done
done
else
textWarn "IAM Master and IAM Manager roles not found"
fi
}
 
check119(){
ID119="1.19"
TITLE119="Maintain current contact details (Scored)"
# No command available
textTitle "$ID119" "$TITLE119"
textNotice "No command available for check 1.19 "
textNotice "See section 1.19 on the CIS Benchmark guide for details "
infoReferenceShort
}
 
check120(){
ID120="1.20"
TITLE120="Ensure security contact information is registered (Scored)"
# No command available
textTitle "$ID120" "$TITLE120"
textNotice "No command available for check 1.20 "
textNotice "See section 1.20 on the CIS Benchmark guide for details "
infoReferenceShort
}
 
check121(){
ID121="1.21"
TITLE121="Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)"
textTitle "$ID121" "$TITLE121"
textNotice "No command available for check 1.21 "
textNotice "See section 1.21 on the CIS Benchmark guide for details "
infoReferenceShort
}
 
check122(){
ID122="1.22"
TITLE122="Ensure a support role has been created to manage incidents with AWS Support (Scored)"
textTitle "$ID122" "$TITLE122"
SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" --profile $PROFILE --region $REGION --output text)
if [[ $SUPPORTPOLICYARN ]];then
for policyarn in $SUPPORTPOLICYARN;do
POLICYUSERS=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN --profile $PROFILE --region $REGION --output json)
if [[ $POLICYUSERS ]];then
textOK "Support Policy attached to $policyarn"
for user in $(echo "$POLICYUSERS" | grep UserName | cut -d'"' -f4) ; do
textNotice "User $user has support access via $policyarn"
done
# textNotice "Make sure your team can create a Support case with AWS "
else
textWarn "Support Policy not applied to any Group / User / Role "
fi
done
else
textWarn "No Support Policy found"
fi
}
 
check123(){
ID123="1.23"
TITLE123="Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)"
textTitle "$ID123" "$TITLE123"
LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION)
# List of USERS with KEY1 last_used_date as N/A
LIST_USERS_KEY1_NA=$(for user in $LIST_USERS; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$11 }'|grep N/A |awk '{ print $1 }'; done)
LIST_USERS_KEY1_ACTIVE=$(for user in $LIST_USERS_KEY1_NA; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$9 }'|grep "true$"|awk '{ print $1 }'|sed 's/[:blank:]+/,/g' ; done)
if [[ $LIST_USERS_KEY1_ACTIVE ]]; then
for user in $LIST_USERS_KEY1_ACTIVE; do
textNotice "$user has never used Access Key 1"
done
else
textOK "No users found with Access Key 1 never used"
fi
# List of USERS with KEY2 last_used_date as N/A
LIST_USERS_KEY2_NA=$(for user in $LIST_USERS; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$16 }'|grep N/A |awk '{ print $1 }' ; done)
LIST_USERS_KEY2_ACTIVE=$(for user in $LIST_USERS_KEY2_NA; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$14 }'|grep "true$" |awk '{ print $1 }' ; done)
if [[ $LIST_USERS_KEY2_ACTIVE ]]; then
for user in $LIST_USERS_KEY2_ACTIVE; do
textNotice "$user has never used Access Key 2"
done
else
textOK "No users found with Access Key 2 never used"
fi
}
 
check124(){
ID124="1.24"
TITLE124="Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)"
textTitle "$ID124" "$TITLE124"
LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }')
if [[ $LIST_CUSTOM_POLICIES ]]; then
textNotice "Looking for custom policies: (skipping default policies - it may take few seconds...)"
for policy in $LIST_CUSTOM_POLICIES; do
POLICY_VERSION=$($AWSCLI iam list-policies --profile $PROFILE --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text|grep -w $policy |awk '{ print $2}')
POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $policy --version-id $POLICY_VERSION --query "PolicyVersion.Document.Statement[?Effect == 'Allow' && contains(Resource, '*') && contains (Action, '*')]" --profile $PROFILE --region $REGION)
if [[ $POLICY_WITH_FULL ]]; then
POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $policy"
fi
done
if [[ $POLICIES_ALLOW_LIST ]]; then
textNotice "List of custom policies: "
for policy in $POLICIES_ALLOW_LIST; do
textNotice "Policy $policy allows \"*:*\""
done
else
textOK "No custom policy found that allow full \"*:*\" administrative privileges"
fi
else
textOK "No custom policies found"
fi
}
 
check21(){
ID21="2.1"
TITLE21="Ensure CloudTrail is enabled in all regions (Scored)"
textTitle "$ID21" "$TITLE21"
LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text)
if [[ $LIST_OF_TRAILS ]];then
for trail in $LIST_OF_TRAILS;do
MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail)
if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then
textWarn "$trail trail in $REGION is not enabled in multi region mode"
else
textOK "$trail trail in $REGION is enabled for all regions"
fi
done
else
textWarn "No CloudTrail trails found!"
fi
}
 
check22(){
ID22="2.2"
TITLE22="Ensure CloudTrail log file validation is enabled (Scored)"
textTitle "$ID22" "$TITLE22"
LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text)
if [[ $LIST_OF_TRAILS ]];then
for trail in $LIST_OF_TRAILS;do
LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail)
if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then
textWarn "$trail trail in $REGION has not log file validation enabled"
else
textOK "$trail trail in $REGION has log file validation enabled"
fi
done
else
textWarn "No CloudTrail trails found!"
fi
}
 
check23(){
ID23="2.3"
TITLE23="Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
textTitle "$ID23" "$TITLE23"
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILBUCKET ]];then
for bucket in $CLOUDTRAILBUCKET;do
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --profile $PROFILE --region $REGION --output text)
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then
textWarn "check your $bucket CloudTrail bucket ACL and Policy!"
else
textOK "Bucket $bucket is set correctly"
fi
done
else
textWarn "No CloudTrail bucket found!"
fi
}
 
check24(){
ID24="2.4"
TITLE24="Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)"
textTitle "$ID24" "$TITLE24"
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion}' --output text | tr "\t" ',')
if [[ $TRAILS_AND_REGIONS ]];then
for reg_trail in $TRAILS_AND_REGIONS;do
trail=$(echo $reg_trail | cut -d',' -f2)
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail --profile $PROFILE --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None)
if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then
textWarn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)"
else
LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
if [ $HOWOLDER -gt "1" ];then
textWarn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)"
else
textOK "$trail trail has been logging during the last 24h (it is in $TRAIL_REGION)"
fi
fi
done
else
textWarn "No CloudTrail trails found!"
fi
}
 
check25(){
ID25="2.5"
TITLE25="Ensure AWS Config is enabled in all regions (Scored)"
textTitle "$ID25" "$TITLE25"
for regx in $REGIONS; do
CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx --output json| grep "recorder: ON")
if [[ $CHECK_AWSCONFIG_STATUS ]];then
textOK "Region $regx has AWS Config recorder: ON" "$regx"
else
textWarn "Region $regx has AWS Config disabled or not configured" "$regx"
fi
done
}
 
check26(){
ID26="2.6"
TITLE26="Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)"
textTitle "$ID26" "$TITLE26"
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILBUCKET ]];then
for bucket in $CLOUDTRAILBUCKET;do
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket --profile $PROFILE --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None)
if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then
textOK "Bucket access logging enabled in $bucket"
else
textWarn "access logging is not enabled in $bucket CloudTrail S3 bucket!"
fi
done
else
textWarn "CloudTrail bucket not found!"
fi
}
 
check27(){
ID27="2.7"
TITLE27="Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)"
textTitle "$ID27" "$TITLE27"
CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILNAME ]];then
for trail in $CLOUDTRAILNAME;do
CLOUDTRAILENC_ENABLED=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --trail $trail --query 'trailList[*].KmsKeyId' --output text)
if [[ $CLOUDTRAILENC_ENABLED ]];then
textOK "KMS key found for $trail"
else
textWarn "encryption is not enabled in your CloudTrail trail $trail but KMS key not found!"
fi
done
else
textWarn "CloudTrail bucket doesn't exist!"
fi
}
 
check28(){
ID28="2.8"
TITLE28="Ensure rotation for customer created CMKs is enabled (Scored)"
textTitle "$ID28" "$TITLE28"
for regx in $REGIONS; do
CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys --profile $PROFILE --region $regx --output text --query 'Keys[*].KeyId')
if [[ $CHECK_KMS_KEYLIST ]];then
CHECK_KMS_KEYLIST_NO_DEFAULT=$(for key in $CHECK_KMS_KEYLIST ; do $AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --output text|grep -v 'Default master key that protects my ACM private keys when no other key is defined'|awk '{ print $3 }'|awk -F'/' '{ print $2 }'; done)
for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do
CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g')
if [[ $CHECK_KMS_KEY_TYPE == "EXTERNAL" ]];then
textOK "Key $key in Region $regx Customer Uploaded Key Material." "$regx"
else
CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key --profile $PROFILE --region $regx --output text)
#CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --query 'KeyMetadata.Description' | sed -n '/Default master key that protects my ACM private keys when no other key is defined /p'|| echo "False")
if [[ $CHECK_KMS_KEY_ROTATION == "True" ]];then
textOK "Key $key in Region $regx is set correctly"
elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then
textNotice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." "$regx"
else
textWarn "Key $key in Region $regx is not set to rotate!!!" "$regx"
fi
fi
done
 
else
textNotice "Region $regx doesn't have encryption keys" "$regx"
fi
done
}
 
check31(){
ID31="3.1"
TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)"
textTitle "$ID31" "$TITLE31"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied)
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for Access Denied enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check32(){
ID32="3.2"
TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)"
textTitle "$ID32" "$TITLE32"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for sign-in Console without MFA enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check33(){
ID33="3.3"
TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)"
textTitle "$ID33" "$TITLE33"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for usage of root account enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check34(){
ID34="3.4"
TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)"
textTitle "$ID34" "$TITLE34"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'DeleteGroupPolicy.*DeleteRolePolicy.*DeleteUserPolicy.*PutGroupPolicy.*PutRolePolicy.*PutUserPolicy.*CreatePolicy.*DeletePolicy.*CreatePolicyVersion.*DeletePolicyVersion.*AttachRolePolicy.*DetachRolePolicy.*AttachUserPolicy.*DetachUserPolicy.*AttachGroupPolicy.*DetachGroupPolicy')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for IAM policy changes enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check35(){
ID35="3.5"
TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)"
textTitle "$ID35" "$TITLE35"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for CloudTrail configuration changes enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check36(){
ID36="3.6"
TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)"
textTitle "$ID36" "$TITLE36"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters for usage of root account enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check37(){
ID37="3.7"
TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)"
textTitle "$ID37" "$TITLE37"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check38(){
ID38="3.8"
TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)"
textTitle "$ID38" "$TITLE38"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check39(){
ID39="3.9"
TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)"
textTitle "$ID39" "$TITLE39"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check310(){
ID310="3.10"
TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)"
textTitle "$ID310" "$TITLE310"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check311(){
ID311="3.11"
TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)"
textTitle "$ID311" "$TITLE311"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check312(){
ID312="3.12"
TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)"
textTitle "$ID312" "$TITLE312"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check313(){
ID313="3.13"
TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)"
textTitle "$ID313" "$TITLE313"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check314(){
ID314="3.14"
TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)"
textTitle "$ID314" "$TITLE314"
CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }')
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink')
if [[ $METRICFILTER_SET ]];then
textOK "CloudWatch group found with metric filters enabled"
else
textWarn "CloudWatch group found but no metric filters or alarms associated"
fi
else
textWarn "No CloudWatch group found but no metric filters or alarms associated"
fi
}
 
check315(){
ID315="3.15"
TITLE315="Ensure appropriate subscribers to each SNS topic (Not Scored)"
textTitle "$ID315" "$TITLE315"
CAN_SNS_LIST_SUBS=1
for regx in $REGIONS; do
TOPICS_LIST=$($AWSCLI sns list-topics --profile $PROFILE --region $regx --output text --query 'Topics[*].TopicArn')
ntopics=$(echo $TOPICS_LIST | wc -w )
if [[ $TOPICS_LIST && $CAN_SNS_LIST_SUBS -eq 1 ]];then
textNotice "Region $regx has $ntopics topics" "$regx"
for topic in $TOPICS_LIST; do
TOPIC_SHORT=$(echo $topic | awk -F: '{ print $6 }')
CHECK_TOPIC_LIST=$($AWSCLI sns list-subscriptions-by-topic --topic-arn $topic --profile $PROFILE --region $regx --query 'Subscriptions[*].{Endpoint:Endpoint,Protocol:Protocol}' --output text --max-items $MAXITEMS 2> /dev/null)
if [[ $? -eq 255 ]]; then
# Permission error
export CAN_SNS_LIST_SUBS=0
ntopics=$(echo $TOPICS_LIST | wc -w )
textNotice "Region $regx / $ntopics Topics / Subscriptions NO_PERMISSION" "$regx"
break;
fi
if [[ "Z" != "Z${CHECK_TOPIC_LIST}" ]]; then
printf '%s\n' "$CHECK_TOPIC_LIST" | while IFS= read -r dest ; do
textNotice "Region $regx / Topic $TOPIC_SHORT / Suscription $dest" "$regx"
done
else
textWarn "Region $regx / Topic $TOPIC_SHORT / Suscription NONE NONE" "$regx"
fi
done
elif [[ $CAN_SNS_LIST_SUBS -eq 0 ]]; then
textNotice "Region $regx has $ntopics topics - unable to list subscribers" "$regx"
# break
else
textOK "Region $regx has 0 topics" "$regx"
fi
done
}
 
check41(){
ID41="4.1"
TITLE41="Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)"
textTitle "$ID41" "$TITLE41"
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=22" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`22` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text)
if [[ $SG_LIST ]];then
for SG in $SG_LIST;do
textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx"
done
else
textOK "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0" "$regx"
fi
done
}
 
check42(){
ID42="4.2"
TITLE42="Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)"
textTitle "$ID42" "$TITLE42"
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=3389" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`3389` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text)
if [[ $SG_LIST ]];then
for SG in $SG_LIST;do
textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx"
done
else
textOK "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0" "$regx"
fi
done
}
 
check43(){
ID43="4.3"
TITLE43="Ensure VPC Flow Logging is Enabled in all VPCs (Scored)"
textTitle "$ID43" "$TITLE43"
for regx in $REGIONS; do
CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text)
if [[ $CHECK_FL ]];then
for FL in $CHECK_FL;do
textOK "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx" "$regx"
done
else
textWarn "No VPCFlowLog has been found in Region $regx" "$regx"
fi
done
}
 
check44(){
ID44="4.4"
TITLE44="Ensure the default security group of every VPC restricts all traffic (Scored)"
textTitle "$ID44" "$TITLE44"
for regx in $REGIONS; do
CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0)
if [[ $CHECK_SGDEFAULT ]];then
textWarn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" "$regx"
else
textOK "No Default Security Groups open to 0.0.0.0 found in Region $regx" "$regx"
fi
done
}
 
check45(){
#set -xe
ID45="4.5"
TITLE45="Ensure routing tables for VPC peering are \"least access\" (Not Scored)"
textTitle "$ID45" "$TITLE45"
textNotice "Looking for VPC peering in all regions... "
for regx in $REGIONS; do
LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId')
if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then
textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS - review routing tables" "$regx"
#LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs --profile $PROFILE --region $regx --query 'Vpcs[*].VpcId' --output text)
#aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" --profile $PROFILE --region $regx
# for vpc in $LIST_OF_VPCS; do
# VPCS_WITH_PEERING=$($AWSCLI ec2 describe-route-tables --filter "Name=vpc-id,Values=$vpc" --profile $PROFILE --region $regx --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" |grep GatewayId|grep pcx-)
# done
#echo $VPCS_WITH_PEERING
else
textOK "$regx: No VPC peering found" "$regx"
fi
done
}
 
extra71(){
# set -x
ID71="7.1"
TITLE71="Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark)"
textTitle "$ID71" "$TITLE71" "0"
 
ADMIN_GROUPS=''
AWS_GROUPS=$($AWSCLI --profile $PROFILE iam list-groups --output text --query 'Groups[].GroupName')
for grp in $AWS_GROUPS; do
# aws --profile onlinetraining iam list-attached-group-policies --group-name Administrators --query 'AttachedPolicies[].PolicyArn' | grep 'arn:aws:iam::aws:policy/AdministratorAccess'
# list-attached-group-policies
CHECK_ADMIN_GROUP=$($AWSCLI --profile $PROFILE iam list-attached-group-policies --group-name $grp --output json --query 'AttachedPolicies[].PolicyArn' | grep 'arn:aws:iam::aws:policy/AdministratorAccess')
if [[ $CHECK_ADMIN_GROUP ]]; then
ADMIN_GROUPS="$ADMIN_GROUPS $grp"
textNotice "$grp group provides administrative access"
ADMIN_USERS=$($AWSCLI --profile $PROFILE iam get-group --group-name $grp --output json --query 'Users[].UserName' | grep '"' | cut -d'"' -f2 )
for auser in $ADMIN_USERS; do
# users in group are Administrators
# users
# check for user MFA device in credential report
USER_MFA_ENABLED=$( cat $TEMP_REPORT_FILE | grep "^$auser," | cut -d',' -f8)
if [[ "true" == $USER_MFA_ENABLED ]]; then
textOK "$auser / MFA Enabled / admin via group $grp"
else
textWarn "$auser / MFA DISABLED / admin via group $grp"
fi
done
else
textNotice "$grp group provides non-administrative access"
fi
done
# set +x
}
 
extra72(){
#set -x
ID72="7.2"
TITLE72="Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark)"
textTitle "$ID72" "$TITLE72" "0"
textNotice "Looking for EBS Snapshots in all regions... "
for regx in $REGIONS; do
LIST_OF_EBS_SNAPSHOTS=$($AWSCLI ec2 describe-snapshots --profile $PROFILE --region $regx --owner-ids $ACCOUNT_NUM --output text --query 'Snapshots[*].{ID:SnapshotId}')
for snapshot in $LIST_OF_EBS_SNAPSHOTS; do
SNAPSHOT_IS_PUBLIC=$($AWSCLI ec2 describe-snapshot-attribute --profile $PROFILE --region $regx --output text --snapshot-id $snapshot --attribute createVolumePermission --query "CreateVolumePermissions[?Group=='all']")
if [[ $SNAPSHOT_IS_PUBLIC ]];then
textWarn "$regx: $snapshot is currently Public!" "$regx"
else
textOK "$regx: $snapshot is not Public" "$regx"
fi
done
done
 
}
 
extra73(){
#set -x
ID73="7.3"
TITLE73="Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)"
textTitle "$ID73" "$TITLE73" "0"
textNotice "Looking for S3 Buckets in all regions... "
ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' --profile $PROFILE --region $REGION --output text)
for bucket in $ALL_BUCKETS_LIST; do
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket --profile $PROFILE --region $REGION --output text)
if [[ "None" == $BUCKET_LOCATION ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ "EU" == $BUCKET_LOCATION ]]; then
BUCKET_LOCATION="eu-west-1"
fi
CHECK_BUCKET_ALLUSERS_PERMISSIONS=$($AWSCLI s3api get-bucket-acl --profile $PROFILE --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_ALLUSERS_PERMISSIONS_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_ALLUSERS_PERMISSIONS)
CHECK_BUCKET_AUTHUSERS_PERMISSIONS=$($AWSCLI s3api get-bucket-acl --profile $PROFILE --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_AUTHUSERS_PERMISSIONS_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_AUTHUSERS_PERMISSIONS)
if [[ $CHECK_BUCKET_ALLUSERS_PERMISSIONS ]];then
textWarn "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_PERMISSIONS_SINGLE_LINE" "$regx"
else
if [[ $CHECK_BUCKET_AUTHUSERS_PERMISSIONS ]];then
textWarn "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_PERMISSIONS_SINGLE_LINE" "$regx"
fi
textOK "$BUCKET_LOCATION: $bucket bucket is not open" "$regx"
fi
done
}
 
 
callCheck(){
if [[ $CHECKNUMBER ]];then
case "$CHECKNUMBER" in
check11 ) check11;;
check12 ) check12;;
check13 ) check13;;
check14 ) check14;;
check15 ) check15;;
check16 ) check16;;
check17 ) check17;;
check18 ) check18;;
check19 ) check19;;
check110 ) check110;;
check111 ) check111;;
check112 ) check112;;
check113 ) check113;;
check114 ) check114;;
check115 ) check115;;
check116 ) check116;;
check117 ) check117;;
check118 ) check118;;
check119 ) check119;;
check120 ) check120;;
check121 ) check121;;
check122 ) check122;;
check123 ) check123;;
check124 ) check124;;
check21 ) check21;;
check22 ) check22;;
check23 ) check23;;
check24 ) check24;;
check25 ) check25;;
check26 ) check26;;
check27 ) check27;;
check28 ) check28;;
check31 ) check31;;
check32 ) check32;;
check33 ) check33;;
check34 ) check34;;
check35 ) check35;;
check36 ) check36;;
check37 ) check37;;
check38 ) check38;;
check39 ) check39;;
check310 ) check310;;
check311 ) check311;;
check312 ) check312;;
check313 ) check313;;
check314 ) check314;;
check315 ) check315;;
check41 ) check41;;
check42 ) check42;;
check43 ) check43;;
check44 ) check44;;
check45 ) check45;;
extra71 ) extra71;;
extra72 ) extra72;;
extra73 ) extra73;;
## Groups of Checks
check1 )
check11;check12;check13;check14;check15;check16;check17;check18;
check19;check110;check111;check112;check113;check114;check115;
check116;check117;check118;check119;check120;check121;check122;
check123;check124;
;;
check2 )
check21;check22;check23;check24;check25;check26;check27;check28
;;
check3 )
check31;check32;check33;check34;check35;check36;check37;check38;
check39;check310;check311;check312;check313;check314;check315
;;
check4 )
check41;check42;check43;check44;check45
;;
level1 )
check11;check12;check13;check14;check15;check16;check17;check18;
check19;check110;check111;check112;check113;check115;check116;check117;
check118;check119;check120;check122;check123;check124;check21;check23;
check24;check25;check26;check31;check32;check33;check34;check35;
check38;check312;check313;check314;check315;check41;check42
;;
level2 )
check11;check12;check13;check14;check15;check16;check17;check18;
check19;check110;check111;check112;check113;check114;check115;check116;
check117;check118;check119;check120;check121;check122;check123;check124;
check21;check22;check23;check24;check25;check26;check27;check28;check31;
check32;check33;check34;check35;check36;check37;check38;check39;
check310;check311;check312;check313;check314;check315;check41;check42;
check43;check44;check45
;;
extras )
extra71;extra72;extra73
;;
* )
textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n";
esac
cleanTemp
exit
fi
}
 
 
### All functions defined above ... run the workflow
 
if [[ $MODE != "csv" ]]; then
prowlerBanner
printCurrentDate
printColorsCode
getWhoami
else
getWhoami
printCsvHeader
fi
genCredReport
saveReport
 
 
callCheck
 
TITLE1="Identity and Access Management ****************************************"
textTitle "1" "$TITLE1"
check11
check12
check13
check14
check15
check16
check17
check18
check19
check110
check111
check112
check113
check114
check115
check116
check117
check118
check119
check120
check121
check122
check123
check124
 
TITLE2="Logging ***************************************************************"
textTitle "2" "$TITLE2"
check21
check22
check23
check24
check25
check26
check27
check28
 
TITLE3="Monitoring ************************************************************"
textTitle "3" "$TITLE3"
# 3 Monitoring check commands / Mostly covered by SecurityMonkey
check31
check32
check33
check34
check35
check36
check37
check38
check39
check310
check311
check312
check313
check314
check315
 
TITLE4="Networking ************************************************************"
textTitle "4" "$TITLE4"
check41
check42
check43
check44
check45
 
TITLE7="Extras ************************************************************"
textTitle "7" "$TITLE7"
extra71
extra72
extra73
 
if [[ $MODE != "csv" ]]; then
infoReferenceLong
fi
cleanTemp