Merge pull request #3 from toniblyx/master

Updates from fork
This commit is contained in:
Nimrod Kor
2019-10-31 16:10:42 +02:00
committed by GitHub
16 changed files with 918 additions and 67 deletions
+21 -4
View File
@@ -55,13 +55,13 @@ With Prowler you can:
This script has been written in bash using AWS-CLI and it works in Linux and OSX.
- Make sure your AWS-CLI is installed on your workstation, and other components needed, with Python pip already installed:
- Make sure the latest version of AWS-CLI is installed on your workstation, and other components needed, with Python pip already installed:
```sh
pip install awscli ansi2html detect-secrets
```
AWS-CLI can be also installed it using "brew", "apt", "yum" or manually from <https://aws.amazon.com/cli/>, but `ansi2html` and `detect-secrets` has to be installed using `pip`
AWS-CLI can be also installed it using "brew", "apt", "yum" or manually from <https://aws.amazon.com/cli/>, but `ansi2html` and `detect-secrets` has to be installed using `pip`. You will need to install `jq` to get more accuracy in some checks.
- Previous steps, from your workstation:
@@ -70,13 +70,19 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
cd prowler
```
- Make sure you have properly configured your AWS-CLI with a valid Access Key and Region:
- Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly:
```sh
aws configure
```
or
```sh
export AWS_ACCESS_KEY_ID="ASXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXX"
export AWS_SESSION_TOKEN="XXXXXXXXX"
```
- Make sure your Secret and Access Keys are associated to a user with proper permissions to do all checks. To make sure add SecurityAuditor default policy to your user. Policy ARN is
- Those credentials must be associated to a user or role with proper permissions to do all checks. To make sure add SecurityAuditor default policy to your user. Policy ARN is
```sh
arn:aws:iam::aws:policy/SecurityAudit
@@ -94,6 +100,12 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
Use `-l` to list all available checks and group of checks (sections)
If you want to avoid installing dependences run it using Docker:
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest
```
1. For custom AWS-CLI profile and region, use the following: (it will use your custom profile and run checks over all regions when needed):
```sh
@@ -105,6 +117,11 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
```sh
./prowler -c check310
```
With Docker:
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest "-c check310"
```
or multiple checks separated by comma:
```sh
./prowler -c check310,check722
+17 -10
View File
@@ -22,18 +22,25 @@ extra727(){
LIST_SQS=$($AWSCLI sqs list-queues $PROFILE_OPT --region $regx --query QueueUrls --output text |grep -v ^None)
if [[ $LIST_SQS ]]; then
for queue in $LIST_SQS; do
# check if the policy has Principal as *
SQS_TO_CHECK=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy --output text | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ || /Condition/ && !skip { print } { skip = /Deny/} ')
PUBLIC_SQS_WCONDITION=$(echo $SQS_TO_CHECK|grep Condition)
if [[ $PUBLIC_SQS_WCONDITION ]]; then
textPass "$regx: SQS queue $queue has a Condition" "$regx"
else
PUBLIC_SQS=$(echo $SQS_TO_CHECK|grep \"Principal|grep \*)
if [[ $PUBLIC_SQS ]]; then
textFail "$regx: SQS queue $queue seems to be public (Principal: \"*\")" "$regx"
SQS_POLICY=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy)
if [[ "$SQS_POLICY" != "null" ]]; then
SQS_POLICY_ALLOW_ALL=$(echo $SQS_POLICY \
| jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
if [[ $SQS_POLICY_ALLOW_ALL ]]; then
SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$(echo $SQS_POLICY \
| jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
if [[ $SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
| jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
textFail "$regx: SQS $queue queue policy with public access: $SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" "$regx"
else
textInfo "$regx: SQS $queue queue policy with public access but has a Condition" "$regx"
fi
else
textPass "$regx: SQS queue $queue seems correct" "$regx"
textPass "$regx: SQS $queue queue without public access" "$regx"
fi
else
textPass "$regx: SQS $queue queue without policy" "$regx"
fi
done
else
+57 -25
View File
@@ -45,7 +45,15 @@ CHECK_ALTERNATE_check703="extra73"
extra73(){
textInfo "Looking for open S3 Buckets (ACLs and Policies) in all regions... "
ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --output text)
for bucket in $ALL_BUCKETS_LIST; do
# 3 Different problems, let's show only 1 finding all together
S3_FINDING_ALLUSERS_ACL="Ok"
S3_FINDING_AUTHUSERS_ACL="Ok"
S3_FINDING_POLICY="Ok"
# LOCATION
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --output text)
if [[ "None" == $BUCKET_LOCATION ]]; then
BUCKET_LOCATION="us-east-1"
@@ -53,36 +61,60 @@ extra73(){
if [[ "EU" == $BUCKET_LOCATION ]]; then
BUCKET_LOCATION="eu-west-1"
fi
# Check Explicit Deny and Avoid Error
# EXPLICIT DENY
CHEK_FOR_EXPLICIT_DENY=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output text 2>&1)
if [[ $(echo "$CHEK_FOR_EXPLICIT_DENY" | grep AccessDenied) ]] ; then
textPass "$BUCKET_LOCATION: bucket have an explicit Deny. Not possible to get ACL." "$BUCKET_LOCATION"
textInfo "$BUCKET_LOCATION: $bucket have an explicit Deny. Not possible to get ACL." "$bucket" "$BUCKET_LOCATION"
else
# check if AllUsers is in the ACL as Grantee
CHECK_BUCKET_ALLUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_ALLUSERS_ACL)
# check if AuthenticatedUsers is in the ACL as Grantee, they will have access with sigened URL only
CHECK_BUCKET_AUTHUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_AUTHUSERS_ACL)
# to prevent error NoSuchBucketPolicy first clean the output controlling stderr
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX)
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output text --query Policy > $TEMP_POLICY_FILE 2> /dev/null
# check if the S3 policy has Principal as *
CHECK_BUCKET_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'|awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep ^\"Principal|grep \*)
if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY ]];then
if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then
textFail "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION"
fi
if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then
textFail "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION"
fi
if [[ $CHECK_BUCKET_ALLUSERS_POLICY ]];then
textFail "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$BUCKET_LOCATION"
fi
# PUBLIC BLOCK
# https://docs.aws.amazon.com/cli/latest/reference/s3api/get-public-access-block.html
BUCKET_PUBLIC_BLOCK=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION 2>/dev/null)
BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null)
BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicPolicy 2>/dev/null)
BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null)
BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.RestrictPublicBuckets 2>/dev/null)
if [[ $BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET == "true" ]]; then
textPass "$BUCKET_LOCATION: $bucket bucket is public blocked (public-access-block)" "$BUCKET_LOCATION"
else
textPass "$BUCKET_LOCATION: $bucket bucket is not open" "$BUCKET_LOCATION"
## ACL
# check if AllUsers is in the ACL as Grantee
CHECK_BUCKET_ALLUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_ALLUSERS_ACL)
# check if AuthenticatedUsers is in the ACL as Grantee, they will have access with sigened URL only
CHECK_BUCKET_AUTHUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers']" --output text |grep -v GRANTEE)
CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_AUTHUSERS_ACL)
## POLICY
BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query PolicyStatus.IsPublic --output text 2>/dev/null)
if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY == "True" ]]; then
if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL ]] && [[ $BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL != "true" ]];then
if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then
S3_FINDING_ALLUSERS_ACL="bucket ACL is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE"
fi
if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then
S3_FINDING_AUTHUSERS_ACL="bucket ACL is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE"
fi
fi
if [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET != "true" ]] && [[ $BUCKET_POLICY_STATUS == "True" ]]; then
# Here comes the magic: Find Statement Allow, Principal * and No Condition
BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$($AWSCLI s3api get-bucket-policy $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket \
| jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
if [[ $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
# Let's do more magic and identify who can do what
BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
| jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
S3_FINDING_POLICY="bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS"
else
textPass "$BUCKET_LOCATION: $bucket bucket policy with conditions" "$BUCKET_LOCATION"
fi
fi
else
textPass "$BUCKET_LOCATION: $bucket bucket is not open" "$bucket" "$BUCKET_LOCATION"
fi
fi
rm -fr $TEMP_POLICY_FILE
fi
if [[ $S3_FINDING_ALLUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_AUTHUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_POLICY != "Ok" ]] ; then
textFail "$BUCKET_LOCATION: (bucket: $bucket) ALLUSERS_ACL: $S3_FINDING_ALLUSERS_ACL | AUTHUSERS_ACL: $S3_FINDING_AUTHUSERS_ACL | BUCKET_POLICY: $S3_FINDING_POLICY" "$BUCKET_LOCATION"
fi
done
}
+14 -11
View File
@@ -22,23 +22,26 @@ extra731(){
LIST_SNS=$($AWSCLI sns list-topics $PROFILE_OPT --region $regx --query Topics --output text |grep -v ^None)
if [[ $LIST_SNS ]]; then
for topic in $LIST_SNS; do
# check if the policy has Principal as *
SNS_TO_CHECK=$($AWSCLI sns get-topic-attributes --topic-arn $topic $PROFILE_OPT --region $regx --query Attributes.Policy --output text | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ || /Condition/ && !skip { print } { skip = /Deny/}')
PUBLIC_SNS_WCONDITION=$(echo $SNS_TO_CHECK|grep Condition)
SHORT_TOPIC=$(echo $topic| cut -d: -f6)
if [[ $PUBLIC_SNS_WCONDITION ]]; then
textPass "$regx: SNS topic $SHORT_TOPIC has a Condition" "$regx"
else
PUBLIC_SNS=$(echo $SNS_TO_CHECK|grep \"Principal|grep \*)
if [[ $PUBLIC_SNS ]]; then
textFail "$regx: SNS topic $SHORT_TOPIC seems to be public (Principal: \"*\")" "$regx"
SNS_POLICY=$($AWSCLI sns get-topic-attributes --topic-arn $topic $PROFILE_OPT --region $regx --query Attributes.Policy 2>/dev/null)
SNS_POLICY_ALLOW_ALL=$(echo $SNS_POLICY \
| jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
if [[ $SNS_POLICY_ALLOW_ALL ]]; then
SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$(echo $SNS_POLICY \
| jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
if [[ $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
| jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
textFail "$regx: SNS topic policy with public access: $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" "$SHORT_TOPIC" "$regx"
else
textPass "$regx: SNS topic $SHORT_TOPIC seems correct" "$regx"
textPass "$regx: SNS topic policy with public access but has a Condition" "$SHORT_TOPIC" "$regx"
fi
else
textPass "$regx: SNS topic without public access" "$SHORT_TOPIC" "$regx"
fi
done
else
textInfo "$regx: No SNS topics found" "$regx"
textInfo "$regx: No SNS topic found" "$SHORT_TOPIC" "$regx"
fi
done
}
+3 -3
View File
@@ -28,13 +28,13 @@ extra757(){
do
EC2_ID=$(echo "$ec2_instace" | awk '{print $1}')
LAUNCH_DATE=$(echo "$ec2_instace" | awk '{print $2}')
textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE"
textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE" "$regx"
done <<< "$INSTACES_OLD_THAN_AGE"
else
textPass "All Instances newer than 6 months"
textPass "$regx: All Instances newer than 6 months" "$regx"
fi
else
textInfo "No EC2 Instances Found"
textInfo "$regx: No EC2 Instances Found" "$regx"
fi
done
}
+3 -3
View File
@@ -28,13 +28,13 @@ extra758(){
do
EC2_ID=$(echo "$ec2_instace" | awk '{print $1}')
LAUNCH_DATE=$(echo "$ec2_instace" | awk '{print $2}')
textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE"
textFail "$regx: EC2 Instance $EC2_ID running before than $OLDAGE" "$regx"
done <<< "$INSTACES_OLD_THAN_AGE"
else
textPass "All Instances newer than 12 months"
textPass "$regx: All Instances newer than 12 months" "$regx"
fi
else
textInfo "No EC2 Instances Found"
textInfo "$regx: No EC2 Instances Found" "$regx"
fi
done
}
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra761="7.61"
CHECK_TITLE_extra761="[extra761] Check if EBS Default Encryption is activated (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra761="NOT_SCORED"
CHECK_TYPE_extra761="EXTRA"
CHECK_ALTERNATE_check761="extra761"
extra761(){
textInfo "Looking for EBS Default Encryption activation in all regions... "
for regx in $REGIONS; do
EBS_DEFAULT_ENCRYPTION=$($AWSCLI ec2 get-ebs-encryption-by-default $PROFILE_OPT --region $regx --query 'EbsEncryptionByDefault')
if [[ $EBS_DEFAULT_ENCRYPTION == "true" ]];then
textPass "$regx: EBS Default Encryption is activated" "$regx"
else
textFail "$regx: EBS Default Encryption is not activated" "$regx"
fi
done
}
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra762="7.62"
CHECK_TITLE_extra762="[extra762] Find obsolete Lambda runtimes (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra762="NOT_SCORED"
CHECK_TYPE_extra762="EXTRA"
CHECK_ALTERNATE_check762="extra762"
extra762(){
# regex to match OBSOLETE runtimes in string functionName%runtime
# https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
OBSOLETE='%(nodejs4.3|nodejs4.3-edge|nodejs6.10|nodejs8.10|dotnetcore1.0|dotnetcore2.0)'
for regx in $REGIONS; do
LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --output text --query 'Functions[*].{R:Runtime,N:FunctionName}' | tr "\t" "%")
if [[ $LIST_OF_FUNCTIONS ]]; then
for lambdafunction in $LIST_OF_FUNCTIONS;do
fname=$(echo "$lambdafunction" | cut -d'%' -f1)
runtime=$(echo "$lambdafunction" | cut -d'%' -f2)
if echo "$lambdafunction" | grep -Eq $OBSOLETE ; then
textFail "$regx: Obsolete runtime: ${runtime} used by: ${fname}" "$regx"
else
textPass "$regx: Supported runtime: ${runtime} used by: ${fname}" "$regx"
fi
done
else
textInfo "$regx: No Lambda functions found" "$regx"
fi
done
}
+2 -2
View File
@@ -15,7 +15,7 @@ GROUP_ID[7]='extras'
GROUP_NUMBER[7]='7.0'
GROUP_TITLE[7]='Extras - [extras] **********************************************'
GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758'
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762'
# Extras 759 and 760 (lambda variables and code secrets finder are not included)
# to run detect-secrets use `./prowler -g secrets`
# to run detect-secrets use `./prowler -g secrets`
+1 -1
View File
@@ -15,7 +15,7 @@ GROUP_ID[9]='gdpr'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='GDPR Readiness - ONLY AS REFERENCE - [gdpr] ********************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra740'
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra740,extra761'
# Resources:
# https://d1.awsstatic.com/whitepapers/compliance/GDPR_Compliance_on_AWS.pdf
+2 -4
View File
@@ -252,12 +252,10 @@ execute_group() {
NEW_CHECKS=()
IFS=',' read -ra EXCLUDED_CHECKS <<< "${2},"
for exc in ${EXCLUDED_CHECKS[@]} ; do
for i in ${CHECKS[@]} ; do
[[ ${i} != ${exc} ]] && NEW_CHECKS+=(${i})
for i in ${!CHECKS[@]} ; do
[[ ${CHECKS[i]} = ${exc} ]] && unset CHECKS[i]
done
done
CHECKS=("${NEW_CHECKS[@]}")
unset NEW_CHECKS
unset EXCLUDED_CHECKS
fi
for i in ${CHECKS[@]}; do
+6 -4
View File
@@ -5,11 +5,13 @@ ARG USERID=34000
RUN addgroup -g ${USERID} ${USERNAME} && \
adduser -s /bin/sh -G ${USERNAME} -D -u ${USERID} ${USERNAME} && \
apk --update --no-cache add python3 bash curl git jq file && \
apk --update --no-cache add python3 bash curl jq file && \
pip3 install --upgrade pip && \
pip install awscli ansi2html boto3 detect-secrets &&\
git clone https://github.com/toniblyx/prowler/ &&\
chown -R prowler /prowler/
pip install awscli ansi2html boto3 detect-secrets
ADD . /prowler
RUN chown -R prowler /prowler/
USER ${USERNAME}
+73
View File
@@ -0,0 +1,73 @@
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Prowler Auditing Role - in Control Tower pick AWSControlTowerStackSetRole for IAM role and AWSControlTowerExecution for execution
Parameters:
AuditorAccountId:
Default: 987600001234
Description: AWS Account ID where the audit tooling executes
Type: Number
AuditRolePathName:
Default: '/audit/prowler/XA_AuditRole_Prowler'
Description: Path for role name in audit tooling account
Type: String
Resources:
XAAuditRole:
Type: "AWS::IAM::Role"
Properties: # /audit/prowler/XA_AuditRole_Prowler
RoleName: XA_AuditRole_Prowler
Path: "/audit/prowler/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/SecurityAudit
- arn:aws:iam::aws:policy/AWSOrganizationsReadOnlyAccess
- arn:aws:iam::aws:policy/IAMReadOnlyAccess
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS: # TODO: review permissions to see if this can be narrowed down - code build only perhaps
- !Sub "arn:aws:iam::${AuditorAccountId}:root"
Action:
- "sts:AssumeRole"
- Effect: "Allow"
Principal:
Service:
- "codebuild.amazonaws.com"
Action:
- "sts:AssumeRole"
# TODO: restrict to only AuditorAccount only
Policies:
- PolicyName: "ProwlerPolicyAdditions"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "ProwlerPolicyAdditions"
Effect: "Allow"
Resource: "*"
Action:
- "acm:describecertificate"
- "acm:listcertificates"
- "es:describeelasticsearchdomainconfig"
- "logs:DescribeLogGroups"
- "logs:DescribeMetricFilters"
- "ses:getidentityverificationattributes"
- "sns:listsubscriptionsbytopic"
- "guardduty:ListDetectors"
- "guardduty:GetDetector"
- "S3:GetEncryptionConfiguration"
- "trustedadvisor:Describe*"
- "cloudtrail:GetEventSelectors"
- "apigateway:GET"
- "support:*"
Metadata:
cfn_nag:
rules_to_suppress:
- id: W28
reason: "the role name is intentionally static"
- id: W11
reason: "the policy grants read/view/audit access only, to all resources, by design"
- id: F3
reason: "Support does not allow or deny access to individual actions"
+414
View File
@@ -0,0 +1,414 @@
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Prowler Auditing Tools Stack
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Organizations and Accounts"
Parameters:
- pOrgMasterAccounts
- pOrgExcludedAccounts
- pStandAloneAccounts
- Label:
default: "Check Group and Execution"
Parameters:
- pProwlerCheckGroup
- pAuditEveryXHours
- Label:
default: "Advanced"
Parameters:
- pTimeoutMinutes
- pAuditRolePathName
- pCustomProwlerRepo
- pCustomProwlerCloneArgs
ParameterLabels:
pOrgMasterAccounts:
default: "Organization Master Accounts"
pOrgExcludedAccounts:
default: "Excluded Organiztion Members"
pStandAloneAccounts:
default: "Stand-alone Accounts"
pProwlerCheckGroup:
default: "Prowler Check Group"
pAuditEveryXHours:
default: "Perform Audit every X hours"
pTimeoutMinutes:
default: "Permit Audit to run for X minutes"
pAuditRolePathName:
default: "Custom audit role path"
pCustomProwlerRepo:
default: "Custom git repo location for prowler"
pCustomProwlerCloneArgs:
default: "Custom arguments to git clone --depth 1"
Parameters:
pAuditEveryXHours:
Default: 24
Type: Number
Description: Number of hours between prowler audit runs.
MinValue: 2
MaxValue: 168
pTimeoutMinutes:
Default: 30
Type: Number
Description: Timeout for running prowler across the fleet
MinValue: 5
MaxValue: 480
pAuditRolePathName:
Default: '/audit/prowler/XA_AuditRole_Prowler'
Type: String
Description: Role path and name which prowler will assume in the target accounts (Audit_Exec_Role.yaml)
# TODO: Validation: begins with "/" and does NOT end with "/"
pOrgMasterAccounts:
Description: Comma Separated list of Organization Master Accounts, or 'none'
Default: 'none'
Type: String
MinLength: 4
AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
pOrgExcludedAccounts: # Comma Separated list of Org Member Accounts to EXCLUDE
Description: Comma Separated list of Skipped Organization Member Accounts, or 'none'
Default: 'none'
Type: String
MinLength: 4
AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
pStandAloneAccounts: # Comma Separated list of Stand-Alone Accounts
Description: Comma Separated list of Stand-alone Accounts, or 'none'
Default: 'none'
Type: String
MinLength: 4
AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$
ConstraintDescription: comma separated list 12-digit account numbers, or 'none'
pProwlerCheckGroup:
Default: 'cislevel1'
Type: String
Description: Which group of checks should prowler run
AllowedValues:
- 'group1'
- 'group2'
- 'group3'
- 'group4'
- 'cislevel1'
- 'cislevel2'
- 'extras'
- 'forensics-ready'
- 'gdpr'
- 'hipaa'
- 'secrets'
- 'apigateway'
- 'rds'
pCustomProwlerRepo:
Type: String
Default: 'https://github.com/toniblyx/prowler.git'
MinLength: 10
pCustomProwlerCloneArgs:
Type: String
Default: '--branch master'
MinLength: 0
##### TODO
# pResultsBucket: # if specified, use an existing bucket for the data
# pEnableAthena:
# Default: false
# Type: Boolean
# Description: Set to true to enable creation of Athena/QuickSight resources
#### TODO
# Conditions:
# cUseAthena: False
Resources:
# S3 Bucket for Results, Config
ProwlerResults:
Type: "AWS::S3::Bucket"
Properties:
# BucketName: !Sub "audit-results-${AWS::AccountId}"
Tags:
- Key: "data-type"
Value: "it-audit:sensitive"
- Key: "data-public"
Value: "NO"
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
# LoggingConfiguration:
# TODO: Enable BucketLogging - requires more parameters
DeletionPolicy: "Retain"
Metadata:
cfn_nag:
rules_to_suppress:
- id: W35
reason: "Bucket logging requires additional configuration not yet supported by this template"
# Policy to allow assuming the XA_AuditRole_Prowler in target accounts
ProwlerAuditManagerRole:
Type: AWS::IAM::Role
Properties:
RoleName: AuditManagerRole_Prowler
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: AssumeRole-XA_AuditRole_Prowler
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
- !Sub "arn:aws:iam::*:role${pAuditRolePathName}"
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
Resource:
- !Sub "${ProwlerResults.Arn}/*"
- Effect: Allow
Action:
- s3:ListBucket
- s3:HeadBucket
- s3:GetBucketLocation
- s3:GetBucketAcl
Resource:
- !Sub "${ProwlerResults.Arn}"
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*"
- !Sub "${ProwlerResults.Arn}"
- Effect: Allow
Action:
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/audit/prowler/config/*"
Metadata:
cfn_nag:
rules_to_suppress:
- id: W28
reason: "the role name is intentionally static"
- id: W11
reason: "not sure where the violation of w11 is"
## Code Build Job
ProwlerBuildProject:
Type: "AWS::CodeBuild::Project"
Properties:
Name: PerformProwlerAudit
Description: "Run Prowler audit on accounts in targeted organizations"
QueuedTimeoutInMinutes: 480
TimeoutInMinutes: !Ref pTimeoutMinutes
ServiceRole: !Ref ProwlerAuditManagerRole
EncryptionKey: !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/aws/s3"
Environment:
Type: "LINUX_CONTAINER"
ComputeType: "BUILD_GENERAL1_MEDIUM"
PrivilegedMode: False
Image: "aws/codebuild/standard:2.0-1.12.0"
ImagePullCredentialsType: "CODEBUILD"
Artifacts: # s3://stack-prowlerresults-randomness/prowler/results/...
Name: "results"
Type: "S3"
Location: !Ref ProwlerResults
Path: "prowler"
NamespaceType: NONE
Packaging: NONE
OverrideArtifactName: False
EncryptionDisabled: False
LogsConfig: # S3/logs/pipeline/
CloudWatchLogs:
Status: ENABLED
GroupName: "audit/prowler"
StreamName: "codebuild_runs"
S3Logs:
Status: DISABLED
# Location: !Sub "${ProwlerResults.Arn}/codebuild_run_logs"
EncryptionDisabled: False
BadgeEnabled: False
Tags:
- Key: "data-type"
Value: "it-audit:sensitive"
- Key: "data-public"
Value: "NO"
Cache:
Type: "NO_CACHE"
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
env:
parameter-store:
PROWL_CHECK_GROUP: /audit/prowler/config/check_group
PROWL_MASTER_ACCOUNTS: /audit/prowler/config/orgmaster_accounts
PROWL_STANDALONE_ACCOUNTS: /audit/prowler/config/standalone_accounts
PROWL_SKIP_ACCOUNTS: /audit/prowler/config/skip_accounts
PROWL_AUDIT_ROLE: /audit/prowler/config/audit_role
PROWLER_REPO: /audit/prowler/config/gitrepo
PROWLER_CLONE_ARGS: /audit/prowler/config/gitcloneargs
phases:
install:
runtime-versions:
python: 3.7
commands:
- aws --version
- git clone --depth 1 $PROWLER_REPO $PROWLER_CLONE_ARGS
pre_build:
commands:
- env | grep PROWL_
- export OUTBASE=$(date -u +"out/diagnostics/%Y/%m/%d")
- export STAMP=$(date -u +"%Y%m%dT%H%M%SZ")
- mkdir -p $OUTBASE || true
- prowler/prowler -V
- aws sts get-caller-identity > ${OUTBASE}/${STAMP}-caller-id.json
build:
commands:
#### Run Prowler against this account, but don't fail the build
# - export PROWLER_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account')
# - /bin/bash prowler/prowler -g cislevel1 -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.cislevel1.csv || /bin/true
# - /bin/bash prowler/prowler -g forensics-ready -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.forensics-ready.csv || /bin/true
#### Run Prowler targeting all accounts in the configured organizations
- test -f prowler/util/multi-account/config
- /bin/bash prowler/util/multi-account/megaprowler.sh out
finally:
- ps axuwww | grep -E 'parallel|sem|prowler'
post_build:
commands:
- echo "attempting to collect any prowler credential reports ..."
- find /tmp/ -name prowler\* | xargs -I % cp % ${OUTDIAG} || true
artifacts:
files:
- '**/*'
discard-paths: no
base-directory: out
ProwlerAuditTriggerRole:
Type: AWS::IAM::Role
Properties:
# RoleName: Let cloudformation create this
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: AssumeRole-XA_AuditRole_Prowler
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- codebuild:StartBuild
Resource:
- !GetAtt ProwlerBuildProject.Arn
ProwlerAuditTrigger:
Type: AWS::Events::Rule
Properties:
Description: !Sub "Execute Prowler audit every ${pAuditEveryXHours} hours"
Name: "ScheduledProwler"
RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn
## Other ways to define scheduling
# ScheduleExpression: "cron(MM HH ? * * *)"
# ScheduleExpression: "cron(45 15 ? * * *)"
# ScheduleExpression: !Sub "rate( ${pAuditEveryXHours} hours)"
ScheduleExpression: !Sub "rate(${pAuditEveryXHours} hours)"
State: ENABLED
Targets:
- Arn: !GetAtt ProwlerBuildProject.Arn
Id: 'ScheduledProwler'
RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn
ProwlerConfigCheckGroup:
Type: AWS::SSM::Parameter
Properties:
Description: "Name of the prowler check group to use"
Name: "/audit/prowler/config/check_group"
Type: "String"
Value: !Ref pProwlerCheckGroup
ProwlerConfigMasterAccounts:
Type: AWS::SSM::Parameter
Properties:
Description: "List of organization master accounts"
Name: "/audit/prowler/config/orgmaster_accounts"
Type: "String"
Value: !Ref pOrgMasterAccounts
ProwlerConfigStandAloneAccounts:
Type: AWS::SSM::Parameter
Properties:
Description: "List of stand-alone accounts"
Name: "/audit/prowler/config/standalone_accounts"
Type: "String"
Value: !Ref pStandAloneAccounts
ProwlerConfigSkipAccounts:
Type: AWS::SSM::Parameter
Properties:
Description: "List of skipped organization member accounts"
Name: "/audit/prowler/config/skip_accounts"
Type: "String"
Value: !Ref pOrgExcludedAccounts
ProwlerConfigAuditRole:
Type: AWS::SSM::Parameter
Properties:
Description: "Role used to audit target accounts"
Name: "/audit/prowler/config/audit_role"
Type: "String"
Value: !Ref pAuditRolePathName
ProwlerConfigGitRepo:
Type: AWS::SSM::Parameter
Properties:
Description: "Git repository where prowler is gathered"
Name: "/audit/prowler/config/gitrepo"
Type: "String"
Value: !Ref pCustomProwlerRepo
ProwlerConfigGitCloneArgs:
Type: AWS::SSM::Parameter
Properties:
Description: "Git clone arguments"
Name: "/audit/prowler/config/gitcloneargs"
Type: "String"
Value: !Ref pCustomProwlerCloneArgs
# -- Conditional "cUseAthena"
# Athena
# QuickSight
# ???
Outputs:
ResultsBucket:
Description: S3 Bucket with Prowler Results, Logs, Configs
Value: !Ref ProwlerResults
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
########### CODEBUILD CONFIGURATION ##################
# shellcheck disable=SC2034
## Collect environment parameters set by buildspec
CHECKGROUP=${PROWL_CHECK_GROUP}
if [ "none" == "${PROWL_MASTER_ACCOUNTS}" ]; then
ORG_MASTERS=""
else
ORG_MASTERS=$(echo "${PROWL_MASTER_ACCOUNTS}" | tr "," " ")
fi
if [ "none" == "${PROWL_STANDALONE_ACCOUNTS}" ]; then
STANDALONE_ACCOUNTS=""
else
STANDALONE_ACCOUNTS=$(echo "${PROWL_STANDALONE_ACCOUNTS}" | tr "," " ")
fi
if [ "none" == "${PROWL_SKIP_ACCOUNTS}" ]; then
SKIP_ACCOUNTS_REGEX='^$'
else
skip_inside=$(echo "${PROWL_SKIP_ACCOUNTS}" | tr "," "|")
# shellcheck disable=SC2116
SKIP_ACCOUNTS_REGEX=$(echo "(${skip_inside})" )
fi
AUDIT_ROLE=${PROWL_AUDIT_ROLE}
# Adjust if you clone prowler from somewhere other than the default location
PROWLER='prowler/prowler'
# Change this if you want to ensure it breaks in code build
CREDSOURCE='EcsContainer'
+202
View File
@@ -0,0 +1,202 @@
#!/bin/bash
BASEDIR=$(dirname "${0}")
# source the configuration data from "config" in this directory
if [[ -f "${BASEDIR}/config" ]]; then
# shellcheck disable=SC1090
. "${BASEDIR}/config"
else
echo "CONFIG file missing - ${BASEDIR}/config"
exit 255
fi
## Check Environment variables which are set by config
if [[ "${ORG_MASTERS}X" == "X" && "${STANDALONE_ACCOUNTS}X" == "X" ]]; then
echo "No audit targets specified. Failing."
exit 15
fi
if [[ -z $SKIP_ACCOUNTS_REGEX ]]; then
SKIP_ACCOUNTS_REGEX=""
fi
if [[ -z $CHECKGROUP ]]; then
echo "Missing check group from config file"
exit 255
fi
if [[ -z $AUDIT_ROLE ]]; then
echo "Missing audit role from config file"
exit 255
fi
## ========================================================================================
## Check Arguments
if [ $# -lt 1 ]; then
echo "NEED AN OUTPUT DIRECTORY"
exit 2
else
if [[ -d $1 && -w $1 ]]; then
OUTBASE=$1
else
echo "Output directory missing or write-protected"
exit 1
fi
fi
## Check Requirements
if [[ -x $(command -v aws) ]]; then
aws --version
else
echo "AWS CLI is not in PATH ... giving up"
exit 4
fi
if [[ -x $(command -v jq) ]]; then
jq --version
else
echo "JQ is not in PATH ... giving up"
exit 4
fi
# Ensure AWS Credentials are present in environment
if [[ -z $CREDSOURCE ]]; then
echo "No source for base credentials ... giving up"
exit 5
fi
if [[ -f ${PROWLER} && -x ${PROWLER} ]]; then
${PROWLER} -V
else
echo "Unable to execute prowler from ${PROWLER}"
exit 3
fi
## Preflight checks complete
DAYPATH=$(date -u +%Y/%m/%d)
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
## Create output subdirs
OUTDATA="${OUTBASE}/data/${DAYPATH}"
OUTLOGS="${OUTBASE}/logs/${DAYPATH}"
mkdir -p "${OUTDATA}" "${OUTLOGS}"
if [[ -x $(command -v parallel) ]]; then
# Note: the "standard" codebuild container includes parallel
echo "Using GNU sem/parallel, with NCPU+4 jobs"
parallel --citation > /dev/null 2> /dev/null
PARALLEL_START="parallel --semaphore --fg --id p_${STAMP} --jobs +4 --env AWS_SHARED_CREDENTIALS_FILE"
PARALLEL_START_SUFFIX=''
PARALLEL_END="parallel --semaphore --wait --id p_${STAMP}"
else
echo "Consider installing GNU Parallel to avoid punishing your system"
PARALLEL_START=''
PARALLEL_START_SUFFIX=' &'
# shellcheck disable=SC2089
PARALLEL_END="echo 'WAITING BLINDLY FOR PROCESSES TO COMPLETE'; wait ; sleep 30 ; wait"
fi
echo "Execution Timestamp: ${STAMP}"
ALL_ACCOUNTS=""
# Create a temporary credential file
AWS_MASTERS_CREDENTIALS_FILE=$(mktemp -t prowler.masters-XXXXXX)
echo "Preparing Credentials ${AWS_MASTERS_CREDENTIALS_FILE} ( ${CREDSOURCE} )"
echo "# Master Credentials ${STAMP}" >> "${AWS_MASTERS_CREDENTIALS_FILE}"
echo "" >> "${AWS_MASTERS_CREDENTIALS_FILE}"
AWS_TARGETS_CREDENTIALS_FILE=$(mktemp -t prowler.targets-XXXXXX)
echo "Preparing Credentials ${AWS_TARGETS_CREDENTIALS_FILE} ( ${CREDSOURCE} )"
echo "# Target Credentials ${STAMP}" >> "${AWS_TARGETS_CREDENTIALS_FILE}"
echo "" >> "${AWS_TARGETS_CREDENTIALS_FILE}"
## Visit the Organization Master accounts & build a list of all member accounts
export AWS_SHARED_CREDENTIALS_FILE=$AWS_MASTERS_CREDENTIALS_FILE
for org in $ORG_MASTERS ; do
echo -n "Preparing organization $org "
# create credential profile
{
echo "[audit_${org}]"
echo "role_arn = arn:aws:iam::${org}:role${AUDIT_ROLE}"
echo "credential_source = ${CREDSOURCE}"
echo ""
} >> "${AWS_MASTERS_CREDENTIALS_FILE}"
# Get the Organization ID to use for output paths, collecting info, etc
org_id=$(aws --output json --profile "audit_${org}" organizations describe-organization | jq -r '.Organization.Id' )
echo "( $org_id )"
ORG_ID_LIST="${ORG_ID_LIST} ${org_id}"
# Build the list of all accounts in the organizations
aws --output json --profile "audit_${org}" organizations list-accounts > "${OUTLOGS}/${STAMP}-${org_id}-account-list.json"
# shellcheck disable=SC2002
ORG_ACCOUNTS=$( cat "${OUTLOGS}/${STAMP}-${org_id}-account-list.json" | jq -r '.Accounts[].Id' | tr "\n" " ")
ALL_ACCOUNTS="${ALL_ACCOUNTS} ${ORG_ACCOUNTS}"
# Add the Org's Accounts (including master) to the TARGETS_CREDENTIALS file
for target in $ORG_ACCOUNTS ; do
if echo "$target" | grep -qE "${SKIP_ACCOUNTS_REGEX}"; then
echo " skipping account ${target} ( ${org_id} )"
continue
fi
# echo " ${org_id}_${target}"
{
echo "[${org_id}_${target}]"
echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}"
echo "credential_source = ${CREDSOURCE}"
echo ""
} >> "${AWS_TARGETS_CREDENTIALS_FILE}"
done
done
# Prepare credentials for standalone accounts
if [[ "" != "${STANDALONE_ACCOUNTS}" ]] ; then
# mkdir -p ${OUTBASE}/data/standalone/${DAYPATH} ${OUTBASE}/logs/standalone/${DAYPATH}
for target in $STANDALONE_ACCOUNTS ; do
echo "Preparing account ${target} ( standalone )"
{
echo "[standalone_${target}]"
echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}"
echo "credential_source = ${CREDSOURCE}"
echo ""
} >> "${AWS_TARGETS_CREDENTIALS_FILE}"
done
ALL_ACCOUNTS="${ALL_ACCOUNTS} ${STANDALONE_ACCOUNTS}"
fi
# grep -E '^\[' $AWS_MASTERS_CREDENTIALS_FILE $AWS_TARGETS_CREDENTIALS_FILE
# Switch to Target Credential Set
export AWS_SHARED_CREDENTIALS_FILE=${AWS_TARGETS_CREDENTIALS_FILE}
## visit each target account
NUM_ACCOUNTS=$(grep -cE '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}")
echo "Launching ${CHECKGROUP} audit of ${NUM_ACCOUNTS} accounts"
for member in $(grep -E '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}" | tr -d '][') ; do
ORG_ID=$(echo "$member" | cut -d'_' -f1)
ACCOUNT_NUM=$(echo "$member" | cut -d'_' -f2)
# shellcheck disable=SC2086
${PARALLEL_START} "${PROWLER} -p ${member} -n -M csv -g ${CHECKGROUP} 2> ${OUTLOGS}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.log > ${OUTDATA}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.csv ; echo \"${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP} finished\" " ${PARALLEL_START_SUFFIX}
done
echo -n "waiting for parallel threads to complete - " ; date
# shellcheck disable=SC2090
${PARALLEL_END}
echo "Completed ${CHECKGROUP} audit with stamp ${STAMP}"
# mkdir -p ${OUTBASE}/logs/debug/${DAYPATH}
# cp "$AWS_MASTERS_CREDENTIALS_FILE" "${OUTLOGS}/${STAMP}-master_creds.txt"
# cp "$AWS_TARGETS_CREDENTIALS_FILE" "${OUTLOGS}/${STAMP}-target_creds.txt"
rm "$AWS_MASTERS_CREDENTIALS_FILE" "$AWS_TARGETS_CREDENTIALS_FILE"