mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-10 13:34:16 +00:00
Merge pull request #6 from bridgecrewio/merge_from_tony
Merge from tony
This commit is contained in:
27 files changed
+463
-317
No files matched your search
-14
@@ -1,14 +0,0 @@
|
||||
FROM toniblyx/prowler
|
||||
MAINTAINER Bridgecrew <www.bridgecrew.io>
|
||||
|
||||
USER root
|
||||
|
||||
WORKDIR "./prowler"
|
||||
COPY "checks" "checks"
|
||||
COPY "./run.sh" "."
|
||||
|
||||
RUN chown -R prowler /prowler/
|
||||
|
||||
USER prowler
|
||||
|
||||
ENTRYPOINT ["./run.sh"]
|
||||
@@ -6,6 +6,7 @@
|
||||
- [Features](#features)
|
||||
- [Requirements and Installation](#requirements-and-installation)
|
||||
- [Usage](#usage)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Fix](#fix)
|
||||
- [Screenshots](#screenshots)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
@@ -63,6 +64,11 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
|
||||
|
||||
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.
|
||||
|
||||
- Make sure jq is installed (example below with "apt" but use a valid package manager for your OS):
|
||||
```sh
|
||||
sudo apt install jq
|
||||
```
|
||||
|
||||
- Previous steps, from your workstation:
|
||||
|
||||
```sh
|
||||
@@ -70,7 +76,7 @@ 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 or declare AWS variables properly:
|
||||
- Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly (or intance profile):
|
||||
|
||||
```sh
|
||||
aws configure
|
||||
@@ -88,7 +94,7 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
|
||||
arn:aws:iam::aws:policy/SecurityAudit
|
||||
```
|
||||
|
||||
> In some cases you may need more list or get permissions in some services, look at the Troubleshooting section for a more comprehensive policy if you find issues with the default SecurityAudit policy.
|
||||
> Additional permissions needed: to make sure Prowler can scan all services included in the group *Extras*, make sure you attach also the custom policy [prowler-additions-policy.json](https://github.com/toniblyx/prowler/blob/master/iam/prowler-additions-policy.json) to the role you are using.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -222,9 +228,50 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
|
||||
-b do not print Prowler banner
|
||||
-V show version number & exit
|
||||
-s show scoring report
|
||||
-x specify external directory with custom checks (i.e. /my/own/checks, files must start by check)
|
||||
-q suppress info messages and passing test output
|
||||
-A account id for the account where to assume a role, requires -R and -T
|
||||
(i.e.: 123456789012)
|
||||
-R role name to assume in the account, requires -A and -T
|
||||
(i.e.: ProwlerRole)
|
||||
-T session durantion given to that role credentials in seconds, default 1h (3600) recommended 12h, requires -R and -T
|
||||
(i.e.: 43200)
|
||||
-h this help
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Assume Role:
|
||||
|
||||
Prowler uses the AWS CLI underneath so it uses the same authentication methods. However, there are few ways to run Prowler against multiple accounts using IAM Assume Role feature depending on eachg use case. You can just set up your custom profile inside `~/.aws/config` with all needed information about the role to assume then call it with `./prowler -p your-custom-profile`. Additionally you can use `-A 123456789012` and `-R RemoteRoleToAssume` and Prowler will get those temporary credentials using `aws sts assume-role`, set them up as environment variables and run against that given account.
|
||||
|
||||
```
|
||||
./prowler -A 123456789012 -R ProwlerRole
|
||||
```
|
||||
|
||||
> *NOTE 1 about Session Duration*: By default it gets credentials valid for 1 hour (3600 seconds). Depending on the mount of checks you run and the size of your infrastructure, Prowler may require more than 1 hour to finish. Use option `-T <seconds>` to allow up to 12h (43200 seconds). To allow more than 1h you need to modify *"Maximum CLI/API session duration"* for that particular role, read more [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session).
|
||||
|
||||
> *NOTE 2 about Session Duration*: Bear in mind that if you are using roles assumed by role chaining there is a hard limit of 1 hour so consider not using role chaining if possible, read more about that, in foot note 1 below the table [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html).
|
||||
|
||||
For example, if you want to get only the fails in CSV format from all checks regarding RDS without banner from the AWS Account 123456789012 assuming the role RemoteRoleToAssume and set a fixed session duration of 1h:
|
||||
|
||||
```
|
||||
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -b -M cvs -q -g rds
|
||||
```
|
||||
|
||||
### Custom folder for custom checks
|
||||
|
||||
Flag `-x /my/own/checks` will include any check in that particular directory. To see how to write checks see [Add Custom Checks](#add-custom-checks) section.
|
||||
|
||||
### Show or log only FAILs
|
||||
|
||||
In order to remove noise and get only FAIL findings there is a `-q` flag that makes Prowler to show and log only FAILs. It can be combined with any other option.
|
||||
|
||||
```
|
||||
./prowler -q -M csv -b
|
||||
```
|
||||
|
||||
|
||||
## How to fix every FAIL
|
||||
|
||||
Check your report and fix the issues following all specific guidelines per check in <https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf>
|
||||
|
||||
+7
-3
@@ -17,10 +17,14 @@ CHECK_ALTERNATE_check203="check23"
|
||||
check23(){
|
||||
# "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
|
||||
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --region $REGION)
|
||||
if [[ $CLOUDTRAILBUCKET ]];then
|
||||
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_OPT --region $REGION --output text)
|
||||
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then
|
||||
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' $PROFILE_OPT --region $REGION --output text 2>&1)
|
||||
if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Acl for $bucket"
|
||||
continue
|
||||
fi
|
||||
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
|
||||
textFail "check your $bucket CloudTrail bucket ACL and Policy!"
|
||||
else
|
||||
textPass "Bucket $bucket is set correctly"
|
||||
|
||||
+9
-6
@@ -20,16 +20,20 @@ check26(){
|
||||
CLOUDTRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].Name' --output text| tr '\011' '\012' | awk -F: '{print $1}')
|
||||
ACCOUNT_ID=$($AWSCLI sts get-caller-identity --output json $PROFILE_OPT --region $REGION --query "Account" | tr -d '"')
|
||||
|
||||
if [[ $CLOUDTRAILS ]];then
|
||||
if [[ $CLOUDTRAILS ]]; then
|
||||
for trail in $CLOUDTRAILS; do
|
||||
CLOUDTRAIL_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $5 }' | head -n 1)
|
||||
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].[Name, S3BucketName]' --output text | tr '\011' ':' | grep "$trail" | awk -F: '{ print $2 }' )
|
||||
|
||||
if [[ $CLOUDTRAILBUCKET ]];then
|
||||
if [[ $CLOUDTRAILBUCKET ]]; then
|
||||
bucket=$CLOUDTRAILBUCKET
|
||||
if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_NUM" ];then
|
||||
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None)
|
||||
if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then
|
||||
if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_NUM" ]; then
|
||||
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text 2>&1)
|
||||
if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Logging for $bucket"
|
||||
continue
|
||||
fi
|
||||
if [[ $CLOUDTRAILBUCKET_LOGENABLED != "null" ]]; then
|
||||
textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail"
|
||||
else
|
||||
textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail"
|
||||
@@ -37,7 +41,6 @@ check26(){
|
||||
else
|
||||
textInfo "CloudTrail S3 bucket $bucket for trail $trail is not in current account"
|
||||
fi
|
||||
|
||||
else
|
||||
textFail "CloudTrail bucket not found!"
|
||||
fi
|
||||
|
||||
@@ -36,10 +36,10 @@ extra716(){
|
||||
else
|
||||
textPass "$regx: $domain is in a VPC" "$regx"
|
||||
fi
|
||||
rm -f $TEMP_POLICY_FILE
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No Elasticsearch Service domain found" "$regx"
|
||||
fi
|
||||
rm -fr $TEMP_POLICY_FILE
|
||||
done
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra723="7.23"
|
||||
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots are public (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots and Cluster Snapshots are public (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra723="NOT_SCORED"
|
||||
CHECK_TYPE_extra723="EXTRA"
|
||||
CHECK_ALTERNATE_check723="extra723"
|
||||
|
||||
+18
-7
@@ -29,12 +29,23 @@ extra726(){
|
||||
for checkid in $TA_CHECKS_ID; do
|
||||
TA_CHECKS_NAME=$($AWSCLI support describe-trusted-advisor-checks --language en $PROFILE_OPT --region us-east-1 --query "checks[?id==\`$checkid\`].{name:name}[*]" --output text)
|
||||
QUERY_TA_CHECK_RESULT=$($AWSCLI support describe-trusted-advisor-check-result --check-id $checkid --language en $PROFILE_OPT --region us-east-1 --query 'result.status' --output text)
|
||||
if [[ $(echo $QUERY_TA_CHECK_RESULT | grep ok) ]]; then
|
||||
textPass "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
|
||||
elif [[ $(echo $QUERY_TA_CHECK_RESULT | grep warning) ]]; then
|
||||
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
|
||||
else
|
||||
textFail "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
|
||||
fi
|
||||
# Possible results - https://docs.aws.amazon.com/cli/latest/reference/support/describe-trusted-advisor-check-result.html
|
||||
case "$QUERY_TA_CHECK_RESULT" in
|
||||
"ok")
|
||||
textPass "Trusted Advisor check $TA_CHECKS_NAME is in ok state $QUERY_TA_CHECK_RESULT"
|
||||
;;
|
||||
"error")
|
||||
textFail "Trusted Advisor check $TA_CHECKS_NAME is in error state $QUERY_TA_CHECK_RESULT"
|
||||
;;
|
||||
"warning")
|
||||
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in warning state $QUERY_TA_CHECK_RESULT"
|
||||
;;
|
||||
"not_available")
|
||||
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in not_available state $QUERY_TA_CHECK_RESULT"
|
||||
;;
|
||||
"*")
|
||||
textFail "Trusted Advisor check $TA_CHECKS_NAME is in unknown state $QUERY_TA_CHECK_RESULT"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
+110
-136
@@ -10,6 +10,7 @@
|
||||
# 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_extra73="7.3,7.03"
|
||||
CHECK_TITLE_extra73="[extra73] Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra73="NOT_SCORED"
|
||||
@@ -18,46 +19,65 @@ CHECK_ALTERNATE_extra703="extra73"
|
||||
CHECK_ALTERNATE_check73="extra73"
|
||||
CHECK_ALTERNATE_check703="extra73"
|
||||
|
||||
# Improved and simplified check on Nov 18th 2018 due to a new bucket attribute
|
||||
# called PolicyStatus, not available in all regions yet.
|
||||
|
||||
# extra73(){
|
||||
# ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --region $REGION --output text)
|
||||
# for bucket in $ALL_BUCKETS_LIST; do
|
||||
# BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --region $REGION --output text 2>&1)
|
||||
# if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
|
||||
# textFail "Access Denied Trying to Get Bucket Location for $bucket"
|
||||
# continue
|
||||
# fi
|
||||
# if [[ "None" == $BUCKET_LOCATION ]]; then
|
||||
# BUCKET_LOCATION="us-east-1"
|
||||
# fi
|
||||
# if [[ "EU" == $BUCKET_LOCATION ]]; then
|
||||
# BUCKET_LOCATION="eu-west-1"
|
||||
# fi
|
||||
# Verified with AWS support that if get-bucket-acl doesn't return a grant
|
||||
# for All and get-bucket-policy-status returns IsPublic false or bad request
|
||||
# (no policy) then the bucket can be considered not public - though
|
||||
# individual objects may still be. If in addition put-public-access-block is
|
||||
# used to set IgnorePublicAcls and RestrictPublicBuckets to true then that
|
||||
# causes Amazon S3 to ignore all public ACLs on a bucket and any objects that
|
||||
# it contains.
|
||||
#
|
||||
# BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status --bucket $bucket --query PolicyStatus.IsPublic --output text | grep False)
|
||||
# if [[ $BUCKET_POLICY_STATUS ]];then
|
||||
# textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
|
||||
# else
|
||||
# textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
|
||||
# fi
|
||||
# done
|
||||
# }
|
||||
|
||||
# This check does not address legacy ACLs or policies that would give
|
||||
# public access if not blocked at account or bucket level, instead it tries
|
||||
# to reward the use of more broadly restrictive controls with quicker and less
|
||||
# computational intensive checks.
|
||||
#
|
||||
# If we are assembling an inventory then maybe that is not what we want but
|
||||
# for day to day usage that is probably desirable.
|
||||
|
||||
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)
|
||||
|
||||
#
|
||||
# If public ACLs disabled at account level then look no further
|
||||
#
|
||||
ACCOUNT_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3control get-public-access-block $PROFILE_OPT --region $REGION --account-id $ACCOUNT_NUM --output json 2>&1)
|
||||
if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Public Access Block for $bucket"
|
||||
return
|
||||
fi
|
||||
if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
|
||||
ACCOUNTIGNOREPUBLICACLS=""
|
||||
ACCOUNTRESTRICTPUBLICBUCKETS=""
|
||||
else
|
||||
ACCOUNTIGNOREPUBLICACLS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
|
||||
ACCOUNTRESTRICTPUBLICBUCKETS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
|
||||
fi
|
||||
if [[ $ACCOUNTIGNOREPUBLICACLS == "true" && $ACCOUNTRESTRICTPUBLICBUCKETS == "true" ]]; then
|
||||
textPass "All S3 public access blocked at account level"
|
||||
return
|
||||
fi
|
||||
|
||||
#
|
||||
# Otherwise start to iterate bucket
|
||||
#
|
||||
ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --output text 2>&1)
|
||||
if [[ $(echo "$ALL_BUCKETS_LIST" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to List Buckets"
|
||||
return
|
||||
fi
|
||||
if [[ "$ALL_BUCKETS_LIST" == "" ]]; then
|
||||
textInfo "No buckets found"
|
||||
return
|
||||
fi
|
||||
|
||||
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
|
||||
#
|
||||
# LOCATION - requests referencing buckets created after March 20, 2019
|
||||
# must be made to S3 endpoints in the same region as the bucket was
|
||||
# created.
|
||||
#
|
||||
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --output text 2>&1)
|
||||
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Location for $bucket"
|
||||
@@ -70,111 +90,65 @@ extra73(){
|
||||
BUCKET_LOCATION="eu-west-1"
|
||||
fi
|
||||
|
||||
# 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
|
||||
textInfo "$BUCKET_LOCATION: $bucket have an explicit Deny. Not possible to get ACL." "$bucket" "$BUCKET_LOCATION"
|
||||
#
|
||||
# If public ACLs disabled at bucket level then look no further
|
||||
#
|
||||
BUCKET_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3api get-public-access-block $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
|
||||
if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Public Access Block for $bucket"
|
||||
continue
|
||||
fi
|
||||
if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
|
||||
BUCKETIGNOREPUBLICACLS=""
|
||||
BUCKETRESTRICTPUBLICBUCKETS=""
|
||||
else
|
||||
# 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
|
||||
## 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
|
||||
BUCKETIGNOREPUBLICACLS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
|
||||
BUCKETRESTRICTPUBLICBUCKETS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
|
||||
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"
|
||||
if [[ $BUCKETIGNOREPUBLICACLS == "true" && $BUCKETRESTRICTPUBLICBUCKETS == "true" ]]; then
|
||||
textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
|
||||
continue
|
||||
fi
|
||||
|
||||
#
|
||||
# Check for public ACL grants
|
||||
#
|
||||
BUCKET_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
|
||||
if [[ $(echo "$BUCKET_ACL" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Acl for $bucket"
|
||||
continue
|
||||
fi
|
||||
|
||||
ALLUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AllUsers"))')
|
||||
if [[ $ALLUSERS_ACL != "" ]]; then
|
||||
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
|
||||
continue
|
||||
fi
|
||||
|
||||
AUTHENTICATEDUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AuthenticatedUsers"))')
|
||||
if [[ $AUTHENTICATEDUSERS_ACL != "" ]]; then
|
||||
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
|
||||
continue
|
||||
fi
|
||||
|
||||
#
|
||||
# Check for public access in policy
|
||||
#
|
||||
BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query PolicyStatus.IsPublic --output text 2>&1)
|
||||
if [[ $(echo "$BUCKET_POLICY_STATUS" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Policy Status for $bucket"
|
||||
continue
|
||||
fi
|
||||
if [[ $(echo "$BUCKET_POLICY_STATUS" | grep NoSuchBucketPolicy) ]]; then
|
||||
BUCKET_POLICY_STATUS="False"
|
||||
fi
|
||||
|
||||
if [[ $BUCKET_POLICY_STATUS != "" && $BUCKET_POLICY_STATUS != "False" ]]; then
|
||||
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
|
||||
continue
|
||||
fi
|
||||
|
||||
textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
# Then implementation below makes pararel checks but can reach AWS API limits
|
||||
# and eventually doesn't work as expected
|
||||
|
||||
# extra73(){
|
||||
# textTitle "$ID73" "$TITLE73" "NOT_SCORED" "EXTRA"
|
||||
# textNotice "Looking for open S3 Buckets (ACLs and Policies) 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
|
||||
# extra73Thread $bucket &
|
||||
# done
|
||||
# wait
|
||||
# }
|
||||
# extra73Thread(){
|
||||
# bucket=$1
|
||||
# BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket --profile $PROFILE --region $REGION --output text 2>&1)
|
||||
# if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
|
||||
# textFail "Access Denied Trying to Get Bucket Location for $bucket"
|
||||
# return
|
||||
# fi
|
||||
# if [[ "None" == $BUCKET_LOCATION ]]; then
|
||||
# BUCKET_LOCATION="us-east-1"
|
||||
# fi
|
||||
# if [[ "EU" == $BUCKET_LOCATION ]]; then
|
||||
# BUCKET_LOCATION="eu-west-1"
|
||||
# fi
|
||||
# # check if AllUsers is in the ACL as Grantee
|
||||
# CHECK_BUCKET_ALLUSERS_ACL=$($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_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 $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_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 $PROFILE --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
|
||||
# textWarn "$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
|
||||
# textWarn "$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
|
||||
# textWarn "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$BUCKET_LOCATION"
|
||||
# fi
|
||||
# else
|
||||
# textOK "$BUCKET_LOCATION: $bucket bucket is not open" "$BUCKET_LOCATION"
|
||||
# fi
|
||||
# rm -fr $TEMP_POLICY_FILE
|
||||
# }
|
||||
@@ -42,15 +42,15 @@ extra734(){
|
||||
TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX)
|
||||
|
||||
# get bucket policy
|
||||
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_SSE_POLICY_FILE 2> /dev/null
|
||||
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_SSE_POLICY_FILE 2>&1
|
||||
if [[ $(grep AccessDenied $TEMP_SSE_POLICY_FILE) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Policy for $bucket"
|
||||
rm -fr $TEMP_SSE_POLICY_FILE
|
||||
rm -f $TEMP_SSE_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
if [[ $(grep NoSuchBucketPolicy $TEMP_SSE_POLICY_FILE) ]]; then
|
||||
textFail "No bucket policy for $bucket"
|
||||
rm -fr $TEMP_SSE_POLICY_FILE
|
||||
rm -f $TEMP_SSE_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -58,14 +58,14 @@ extra734(){
|
||||
CHECK_BUCKET_SSE_POLICY_PRESENT=$(cat $TEMP_SSE_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}/*" '.Statement[]|select(.Effect=="Deny" and ((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*") and .Action=="s3:PutObject" and .Resource==$arn and .Condition.StringEquals."s3:x-amz-server-side-encryption" != null)')
|
||||
if [[ $CHECK_BUCKET_SSE_POLICY_PRESENT == "" ]]; then
|
||||
textFail "Bucket $bucket does not enforce encryption!"
|
||||
rm -fr $TEMP_SSE_POLICY_FILE
|
||||
rm -f $TEMP_SSE_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
CHECK_BUCKET_SSE_POLICY_VALUE=$(echo "$CHECK_BUCKET_SSE_POLICY_PRESENT" | jq -r '.Condition.StringNotEquals."s3:x-amz-server-side-encryption"')
|
||||
|
||||
textPass "Bucket $bucket has S3 bucket policy to enforce encryption with $CHECK_BUCKET_SSE_POLICY_VALUE"
|
||||
|
||||
rm -fr $TEMP_SSE_POLICY_FILE
|
||||
rm -f $TEMP_SSE_POLICY_FILE
|
||||
done
|
||||
|
||||
else
|
||||
|
||||
@@ -43,6 +43,8 @@ extra741(){
|
||||
rm -f $EC2_USERDATA_FILE
|
||||
else
|
||||
textFail "$regx: Potential secret found in $instance" "$regx"
|
||||
# delete file to not leave trace, user must look at the instance User Data
|
||||
rm -f $EC2_USERDATA_FILE
|
||||
fi
|
||||
else
|
||||
mv $EC2_USERDATA_FILE $EC2_USERDATA_FILE.gz ; gunzip $EC2_USERDATA_FILE.gz
|
||||
@@ -56,12 +58,11 @@ extra741(){
|
||||
fi
|
||||
else
|
||||
textPass "$regx: No secrets found in $instance User Data or it is empty" "$regx"
|
||||
rm -f $EC2_USERDATA_FILE
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No EC2 instances found" "$regx"
|
||||
fi
|
||||
done
|
||||
# rm -rf $SECRETS_TEMP_FOLDER
|
||||
rm -rf $SECRETS_TEMP_FOLDER
|
||||
}
|
||||
@@ -45,12 +45,12 @@ extra742(){
|
||||
rm -f $CFN_OUTPUTS_FILE
|
||||
fi
|
||||
else
|
||||
textInfo "$regx: CloudFormation stack $stack has not Outputs" "$regx"
|
||||
textInfo "$regx: CloudFormation stack $stack has no Outputs" "$regx"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No CloudFormation stacks found" "$regx"
|
||||
fi
|
||||
done
|
||||
# rm -rf $SECRETS_TEMP_FOLDER
|
||||
rm -rf $SECRETS_TEMP_FOLDER
|
||||
}
|
||||
@@ -39,15 +39,16 @@ extra759(){
|
||||
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
|
||||
else
|
||||
textFail "$regx: Potential secret found in Lambda function $lambdafunction variables" "$regx"
|
||||
# delete file to not leave trace, user must look at the function
|
||||
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
|
||||
fi
|
||||
else
|
||||
textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx"
|
||||
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No Lambda functions found" "$regx"
|
||||
fi
|
||||
done
|
||||
# rm -rf $SECRETS_TEMP_FOLDER
|
||||
rm -rf $SECRETS_TEMP_FOLDER
|
||||
}
|
||||
@@ -44,13 +44,12 @@ extra760(){
|
||||
else
|
||||
textFail "$regx: Potential secret found in Lambda function $lambdafunction code" "$regx"
|
||||
# delete files to not leave trace, user must look at the function
|
||||
# rm -fr $LAMBDA_FUNCTION_FOLDER
|
||||
rm -fr $LAMBDA_FUNCTION_FOLDER
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No Lambda functions found" "$regx"
|
||||
rm -fr $LAMBDA_FUNCTION_FOLDER
|
||||
fi
|
||||
done
|
||||
# rm -fr $SECRETS_TEMP_FOLDER
|
||||
rm -fr $SECRETS_TEMP_FOLDER
|
||||
}
|
||||
@@ -26,10 +26,12 @@ extra764(){
|
||||
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_STP_POLICY_FILE 2>&1
|
||||
if [[ $(grep AccessDenied $TEMP_STP_POLICY_FILE) ]]; then
|
||||
textFail "Access Denied Trying to Get Bucket Policy for $bucket"
|
||||
rm -f $TEMP_STP_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
if [[ $(grep NoSuchBucketPolicy $TEMP_STP_POLICY_FILE) ]]; then
|
||||
textFail "No bucket policy for $bucket"
|
||||
rm -f $TEMP_STP_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
+26
-14
@@ -19,7 +19,6 @@
|
||||
# --region <value> \
|
||||
# --repository-name <value> \
|
||||
# --image-scanning-configuration scanOnPush=true
|
||||
|
||||
|
||||
CHECK_ID_extra765="7.65"
|
||||
CHECK_TITLE_extra765="[extra765] Check if ECR image scan on push is enabled (Not Scored) (Not part of CIS benchmark)"
|
||||
@@ -28,19 +27,32 @@ CHECK_TYPE_extra765="EXTRA"
|
||||
CHECK_ALTERNATE_check765="extra765"
|
||||
|
||||
extra765(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
|
||||
if [[ $LIST_ECR_REPOS ]]; then
|
||||
for region in $REGIONS; do
|
||||
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
|
||||
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to describe ECR repositories"
|
||||
continue
|
||||
fi
|
||||
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
|
||||
for repo in $LIST_ECR_REPOS; do
|
||||
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text|grep True)
|
||||
if [[ $SCAN_ENABLED ]];then
|
||||
textPass "$regx: ECR repository $repo has scan on push enabled" "$regx"
|
||||
else
|
||||
textFail "$regx: ECR repository $repo has scan on push disabled!" "$regx"
|
||||
fi
|
||||
done
|
||||
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
|
||||
case "$SCAN_ENABLED" in
|
||||
"True")
|
||||
textPass "$region: ECR repository $repo has scan on push enabled" "$region"
|
||||
;;
|
||||
"False")
|
||||
textFail "$region: ECR repository $repo has scan on push disabled!" "$region"
|
||||
;;
|
||||
"None")
|
||||
textInfo "$region: ECR repository $repo hs no scanOnPush status, newer awscli needed" "$region"
|
||||
;;
|
||||
"*")
|
||||
textInfo "$region: ECR repository $repo has unknown scanOnPush status \"$SCAN_ENABLED\"" "$region"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No ECR repositories found" "$regx"
|
||||
fi
|
||||
done
|
||||
textInfo "$region: No ECR repositories found" "$region"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
# Remediation:
|
||||
#
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#configuring-instance-metadata-service
|
||||
#
|
||||
# aws ecr put-image-scanning-configuration \
|
||||
# --region <value> \
|
||||
# --repository-name <value> \
|
||||
# --image-scanning-configuration scanOnPush=true
|
||||
|
||||
|
||||
CHECK_ID_extra766="7.66"
|
||||
CHECK_TITLE_extra766="[extra766] Check if ECR image scan on push is enabled (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra766="NOT_SCORED"
|
||||
CHECK_TYPE_extra766="EXTRA"
|
||||
CHECK_ALTERNATE_check766="extra766"
|
||||
|
||||
extra766(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
|
||||
if [[ $LIST_ECR_REPOS ]]; then
|
||||
for repo in $LIST_ECR_REPOS; do
|
||||
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text|grep True)
|
||||
if [[ $SCAN_ENABLED ]];then
|
||||
textPass "$regx: ECR repository $repo has scan on push enabled" "$regx"
|
||||
else
|
||||
textFail "$regx: ECR repository $repo has scan on push disabled!" "$regx"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No ECR repositories found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
+11
-2
@@ -10,6 +10,7 @@
|
||||
# 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_extra769="7.69"
|
||||
CHECK_TITLE_extra769="[extra769] Check if IAM Access Analyzer is enabled and its findings (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra769="NOT_SCORED"
|
||||
@@ -18,10 +19,18 @@ CHECK_ALTERNATE_check769="extra769"
|
||||
|
||||
extra769(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text)
|
||||
LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "argument command: Invalid choice") ]]; then
|
||||
textInfo "$regx: list-analyzers not supported, newer awscli needed" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "AccessDeniedException") ]]; then
|
||||
textFail "$regx: Access Denied trying to list-analyzers" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_ACCESS_ANALYZERS ]]; then
|
||||
for accessAnalyzerArn in $LIST_OF_ACCESS_ANALYZERS;do
|
||||
ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ')
|
||||
ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ')
|
||||
if [[ $ANALYZER_ACTIVE_FINDINGS_COUNT -eq 0 ]];then
|
||||
textPass "$regx: IAM Access Analyzer $accessAnalyzerArn has no active findings" "$regx"
|
||||
else
|
||||
|
||||
+34
-15
@@ -10,6 +10,7 @@
|
||||
# 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_extra77="7.7,7.07"
|
||||
CHECK_TITLE_extra77="[extra77] Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra77="NOT_SCORED"
|
||||
@@ -20,20 +21,38 @@ CHECK_ALTERNATE_check707="extra77"
|
||||
|
||||
extra77(){
|
||||
# "Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)"
|
||||
textInfo "Looking for ECR repos in all regions... "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query 'repositories[*].{Name:repositoryName}' --output text)
|
||||
for ecr_repo in $LIST_OF_ECR_REPOS; do
|
||||
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
|
||||
$AWSCLI ecr get-repository-policy --repository-name $ecr_repo $PROFILE_OPT --region $regx --output text > $TEMP_POLICY_FILE 2> /dev/null
|
||||
# check if the policy has Principal as *
|
||||
CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | 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_ECR_REPO_ALLUSERS_POLICY ]];then
|
||||
textFail "$regx: $ecr_repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx"
|
||||
else
|
||||
textPass "$regx: $ecr_repo is not open" "$regx"
|
||||
fi
|
||||
done
|
||||
rm -fr $TEMP_POLICY_FILE
|
||||
for region in $REGIONS; do
|
||||
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
|
||||
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
|
||||
textFail "Access Denied Trying to describe ECR repositories"
|
||||
continue
|
||||
fi
|
||||
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
|
||||
for repo in $LIST_ECR_REPOS; do
|
||||
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
|
||||
$AWSCLI ecr get-repository-policy $PROFILE_OPT --region $region --repository-name $repo --query "policyText" --output text > $TEMP_POLICY_FILE 2>&1
|
||||
if [[ $(grep AccessDenied $TEMP_POLICY_FILE) ]]; then
|
||||
textFail "$region: $repo Access Denied for get-repository-policy"
|
||||
rm -f $TEMP_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html - "By default, only the repository owner has access to a repository."
|
||||
if [[ $(grep RepositoryPolicyNotFoundException $TEMP_POLICY_FILE) ]]; then
|
||||
textPass "$region: $repo is not open" "$region"
|
||||
rm -f $TEMP_POLICY_FILE
|
||||
continue
|
||||
fi
|
||||
# check if the policy has Principal as *
|
||||
CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | jq '.Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")))')
|
||||
if [[ $CHECK_ECR_REPO_ALLUSERS_POLICY ]]; then
|
||||
textFail "$region: $repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$region"
|
||||
else
|
||||
textPass "$region: $repo is not open" "$region"
|
||||
fi
|
||||
rm -f $TEMP_POLICY_FILE
|
||||
done
|
||||
else
|
||||
textInfo "$region: No ECR repositories found" "$region"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra772="7.72"
|
||||
CHECK_TITLE_extra772="[extra772] Check if elastic IPs are unused (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra772="NOT_SCORED"
|
||||
CHECK_TYPE_extra772="EXTRA"
|
||||
CHECK_ALTERNATE_check772="extra772"
|
||||
|
||||
extra772(){
|
||||
for region in $REGIONS; do
|
||||
EIP_DUMP=$($AWSCLI ec2 describe-addresses ${PROFILE_OPT} --region $region)
|
||||
EIP_LIST=$(echo $EIP_DUMP | jq -r '.Addresses[].AllocationId')
|
||||
if [[ $EIP_LIST ]]; then
|
||||
for eip in $EIP_LIST; do
|
||||
ASSOCIATION_ID=$(echo $EIP_DUMP | jq -r --arg i "$eip" '.Addresses[]|select(.AllocationId==$i)|.AssociationId')
|
||||
if [[ "$ASSOCIATION_ID" == "null" ]]; then
|
||||
textFail "$region: EIP $eip is unused" $region
|
||||
else
|
||||
textPass "$region: EIP $eip is used" $region
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$region: No Elastic IPs found" $region
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra773="7.73"
|
||||
CHECK_TITLE_extra773="[extra773] Check if CloudFront distributions are using WAF (Not Scored) (Not part of CIS benchmark)"
|
||||
CHECK_SCORED_extra773="NOT_SCORED"
|
||||
CHECK_TYPE_extra773="EXTRA"
|
||||
CHECK_ALTERNATE_check773="extra773"
|
||||
|
||||
extra773(){
|
||||
# "Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)"
|
||||
LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None")
|
||||
if [[ $LIST_OF_DISTRIBUTIONS ]]; then
|
||||
for dist in $LIST_OF_DISTRIBUTIONS; do
|
||||
WEB_ACL_ID=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.WebACLId' --output text)
|
||||
if [[ $WEB_ACL_ID ]]; then
|
||||
textPass "CloudFront distribution $dist is using AWS WAF web ACL $WEB_ACL_ID"
|
||||
else
|
||||
textFail "CloudFront distribution $dist is not using AWS WAF web ACL"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "No CloudFront distributions found"
|
||||
fi
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import boto3
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
|
||||
sts = boto3.client('sts')
|
||||
aws_lambda = boto3.client('lambda', region_name='us-west-2')
|
||||
|
||||
aws_account_id = sys.argv[1]
|
||||
|
||||
if aws_account_id is None:
|
||||
raise ValueError('Must provide an AWS account ID')
|
||||
|
||||
account_details_response = aws_lambda.invoke(
|
||||
FunctionName='customers-api',
|
||||
Payload=json.dumps({"path": "/invoke/getTenantDetails", "body": {"awsAccountId": sys.argv[1]}, "headers": {"Content-Type": "application/json"}}).encode('utf-8')
|
||||
)
|
||||
|
||||
account_details = json.loads(json.loads(account_details_response['Payload'].read())['body'])
|
||||
|
||||
print('Got account details, assuming role')
|
||||
|
||||
temporary_creds = sts.assume_role(RoleArn=account_details['cross_account_role_arn'], ExternalId=account_details['external_id'], RoleSessionName='prowler')['Credentials']
|
||||
|
||||
os.environ['AWS_ACCESS_KEY_ID'] = temporary_creds['AccessKeyId']
|
||||
os.environ['AWS_SECRET_ACCESS_KEY'] = temporary_creds['SecretAccessKey']
|
||||
@@ -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,extra761,extra762,extra763,extra764,extra765,extra766,extra767,extra768,extra769,extra770,extra771'
|
||||
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,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773'
|
||||
|
||||
# Extras 759 and 760 (lambda variables and code secrets finder are not included)
|
||||
# to run detect-secrets use `./prowler -g secrets`
|
||||
@@ -30,10 +30,12 @@
|
||||
"dax:describeparameters",
|
||||
"dax:describesubnetgroups",
|
||||
"dax:describetable",
|
||||
"dax:listtables",
|
||||
"dax:listtables",
|
||||
"devicefarm:list*",
|
||||
"discovery:list*",
|
||||
"dms:list*",
|
||||
"ds:ListAuthorizedApplications",
|
||||
"ds:DescribeRoles",
|
||||
"dynamodb:describebackup",
|
||||
"dynamodb:describeglobaltablesettings",
|
||||
"dynamodb:describelimits",
|
||||
@@ -50,6 +52,11 @@
|
||||
"gamelift:list*",
|
||||
"glacier:list*",
|
||||
"importexport:listjobs",
|
||||
"lambda:GetAccountSettings",
|
||||
"lambda:GetFunctionConfiguration",
|
||||
"lambda:GetLayerVersionPolicy",
|
||||
"lambda:GetPolicy",
|
||||
"lambda:List*",
|
||||
"lex:getbotaliases",
|
||||
"lex:getbotchannelassociations",
|
||||
"lex:getbots",
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/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.
|
||||
|
||||
# both variables are mandatory to be set together
|
||||
if [[ $ACCOUNT_TO_ASSUME ]]; then
|
||||
if [[ -z $ROLE_TO_ASSUME ]]; then
|
||||
echo "$OPTRED ERROR!$OPTNORMAL - Both Account ID (-A) and IAM Role to assume (-R) must be set"
|
||||
exit 1
|
||||
fi
|
||||
# if not session duration set with -T, then will be 1h.
|
||||
# In some cases you will need more than 1h.
|
||||
if [[ -z $SESSION_DURATION_TO_ASSUME ]]; then
|
||||
SESSION_DURATION_TO_ASSUME="3600"
|
||||
fi
|
||||
|
||||
# temporary file where to store credentials
|
||||
TEMP_STS_ASSUMED_FILE=$(mktemp -t prowler.sts_assumed-XXXXXX)
|
||||
|
||||
# assume role command
|
||||
$AWSCLI $PROFILE_OPT sts assume-role --role-arn arn:aws:iam::$ACCOUNT_TO_ASSUME:role/$ROLE_TO_ASSUME \
|
||||
--role-session-name ProwlerAssessmentSession \
|
||||
--duration-seconds $SESSION_DURATION_TO_ASSUME > $TEMP_STS_ASSUMED_FILE
|
||||
|
||||
# if previous command fails exit with the given error from aws-cli
|
||||
# this is likely to be due to session duration limit of 1h in case
|
||||
# of assume role chaining:
|
||||
# "The requested DurationSeconds exceeds the 1 hour session limit
|
||||
# for roles assumed by role chaining."
|
||||
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html
|
||||
if [[ $? != 0 ]];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The profile shouldn't be used for CLI
|
||||
PROFILE=""
|
||||
PROFILE_OPT=""
|
||||
|
||||
# set env variables with assumed role credentials
|
||||
export AWS_ACCESS_KEY_ID=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.AccessKeyId')
|
||||
export AWS_SECRET_ACCESS_KEY=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.SecretAccessKey')
|
||||
export AWS_SESSION_TOKEN=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.SessionToken')
|
||||
rm -fr $TEMP_STS_ASSUMED_FILE
|
||||
fi
|
||||
+10
-2
@@ -13,6 +13,10 @@
|
||||
|
||||
# Output formatting functions
|
||||
textPass(){
|
||||
if [[ "$QUIET" == 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
PASS_COUNTER=$((PASS_COUNTER+1))
|
||||
if [[ "$MODE" == "csv" ]]; then
|
||||
if [[ $2 ]]; then
|
||||
@@ -55,6 +59,10 @@ textPass(){
|
||||
}
|
||||
|
||||
textInfo(){
|
||||
if [[ "$QUIET" == 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "csv" ]]; then
|
||||
if [[ $2 ]]; then
|
||||
REPREGION=$2
|
||||
@@ -68,7 +76,7 @@ textInfo(){
|
||||
else
|
||||
REPREGION=$REGION
|
||||
fi
|
||||
jq -c \
|
||||
jq -M -c \
|
||||
--arg PROFILE "$PROFILE" \
|
||||
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
|
||||
--arg TITLE_TEXT "$TITLE_TEXT" \
|
||||
@@ -111,7 +119,7 @@ textFail(){
|
||||
else
|
||||
REPREGION=$REGION
|
||||
fi
|
||||
jq -c \
|
||||
jq -M -c \
|
||||
--arg PROFILE "$PROFILE" \
|
||||
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
|
||||
--arg TITLE_TEXT "$TITLE_TEXT" \
|
||||
|
||||
@@ -40,6 +40,7 @@ FILTERREGION=""
|
||||
MAXITEMS=100
|
||||
MONOCHROME=0
|
||||
MODE="text"
|
||||
QUIET=0
|
||||
SEP=','
|
||||
KEEPCREDREPORT=0
|
||||
EXITCODE=0
|
||||
@@ -75,12 +76,19 @@ USAGE:
|
||||
-V show version number & exit
|
||||
-s show scoring report
|
||||
-x specify external directory with custom checks (i.e. /my/own/checks, files must start by "check")
|
||||
-q suppress info messages and passing test output
|
||||
-A account id for the account where to assume a role, requires -R and -T
|
||||
(i.e.: 123456789012)
|
||||
-R role name to assume in the account, requires -A and -T
|
||||
(i.e.: ProwlerRole)
|
||||
-T session durantion given to that role credentials in seconds, default 1h (3600) recommended 12h, requires -R and -T
|
||||
(i.e.: 43200)
|
||||
-h this help
|
||||
"
|
||||
exit
|
||||
}
|
||||
|
||||
while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
|
||||
while getopts ":hlLkqp:r:c:g:f:m:M:E:enbVsx:A:R:T:" OPTION; do
|
||||
case $OPTION in
|
||||
h )
|
||||
usage
|
||||
@@ -140,6 +148,18 @@ while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
|
||||
x )
|
||||
EXTERNAL_CHECKS_PATH=$OPTARG
|
||||
;;
|
||||
q )
|
||||
QUIET=1
|
||||
;;
|
||||
A )
|
||||
ACCOUNT_TO_ASSUME=$OPTARG
|
||||
;;
|
||||
R )
|
||||
ROLE_TO_ASSUME=$OPTARG
|
||||
;;
|
||||
T )
|
||||
SESSION_DURATION_TO_ASSUME=$OPTARG
|
||||
;;
|
||||
: )
|
||||
echo ""
|
||||
echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument"
|
||||
@@ -157,6 +177,9 @@ while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
|
||||
esac
|
||||
done
|
||||
|
||||
# Clean up any temp files when prowler quits unexpectedly
|
||||
trap "{ rm -f /tmp/prowler*.policy.*; }" EXIT
|
||||
|
||||
. $PROWLER_DIR/include/colors
|
||||
. $PROWLER_DIR/include/os_detector
|
||||
. $PROWLER_DIR/include/aws_profile_loader
|
||||
@@ -170,6 +193,7 @@ done
|
||||
. $PROWLER_DIR/include/python_detector
|
||||
. $PROWLER_DIR/include/secrets_detector
|
||||
. $PROWLER_DIR/include/check3x
|
||||
. $PROWLER_DIR/include/assume_role
|
||||
|
||||
# Get a list of all available AWS Regions
|
||||
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \
|
||||
@@ -189,12 +213,12 @@ for checks in $(ls $PROWLER_DIR/checks/check*|grep -v check_sample); do
|
||||
. "$checks"
|
||||
done
|
||||
|
||||
# include checks if external folder is specified
|
||||
if [[ $EXTERNAL_CHECKS_PATH ]]; then
|
||||
# include checks if external folder is specified
|
||||
if [[ $EXTERNAL_CHECKS_PATH ]]; then
|
||||
for checks in $(ls $EXTERNAL_CHECKS_PATH/check*); do
|
||||
. "$checks"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Function to show the title of the check
|
||||
# using this way instead of arrays to keep bash3 (osx) and bash4(linux) compatibility
|
||||
@@ -287,7 +311,7 @@ execute_group_by_id() {
|
||||
done
|
||||
}
|
||||
|
||||
# Function to execute all checks in all groups except extras if -e is invoked
|
||||
# Function to execute all checks in all groups except extras if -e is invoked
|
||||
execute_all() {
|
||||
for i in "${!GROUP_TITLE[@]}"; do
|
||||
if [[ $EXTRAS ]]; then
|
||||
@@ -304,7 +328,7 @@ show_all_titles() {
|
||||
MAIN_GROUPS=(1 2 3 4 7)
|
||||
for i in "${MAIN_GROUPS[@]}"; do
|
||||
show_group_title $i
|
||||
# Display the title of the checks in groups 1,2,3,4 and 7
|
||||
# Display the title of the checks in groups 1,2,3,4 and 7
|
||||
# Any other group has checks in these groups
|
||||
IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$i]}
|
||||
for j in ${CHECKS[@]}; do
|
||||
@@ -415,6 +439,13 @@ if [[ $CHECK_ID ]];then
|
||||
exit $EXITCODE
|
||||
fi
|
||||
|
||||
if [[ $ACCOUNT_TO_ASSUME ]]; then
|
||||
# unset env variables with assumed role credentials
|
||||
unset AWS_ACCESS_KEY_ID
|
||||
unset AWS_SECRET_ACCESS_KEY
|
||||
unset AWS_SESSION_TOKEN
|
||||
fi
|
||||
|
||||
|
||||
execute_all
|
||||
scoring
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir ~/.aws
|
||||
cat << AWS_CREDS > ~/.aws/credentials
|
||||
[${ACCOUNT_ID}]
|
||||
credential_source = EcsContainer
|
||||
role_arn = ${ROLE_ARN}
|
||||
external_id = ${EXTERNAL_ID}
|
||||
|
||||
AWS_CREDS
|
||||
|
||||
echo "Running prowler on ${ACCOUNT_ID}"
|
||||
./prowler -p "${ACCOUNT_ID}" "${CHECKS}" -M json > output.json
|
||||
|
||||
echo "Results:"
|
||||
cat output.json
|
||||
|
||||
echo "Uploading result to s3://${BUCKET}/prowler/${ACCOUNT_ID}/output.json"
|
||||
aws s3api put-object --bucket "${BUCKET}" --key prowler/"${ACCOUNT_ID}"/output.json --body output.json
|
||||
Reference in new issue
Block a user