From aa16bf4084282f4d9be7a9892dcbbd82ca61c7bf Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Thu, 28 Apr 2022 17:04:44 +0200 Subject: [PATCH] feat(dynamodb_allowlist): Support DynamoDB tables ARN for allowlist input (#1118) * feat(dynamodb_allowlist): Support dynamodb tables arn for allowlist input. * feat(allowlist): Include logging messages for input file * fix(allowlist): Modify DynamoDB key name Co-authored-by: sergargar Co-authored-by: Pepe Fagoaga --- README.md | 11 +++++- include/allowlist | 87 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index a0087c22..9a0170d3 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ - [Advanced Usage](#advanced-usage) - [Security Hub integration](#security-hub-integration) - [CodeBuild deployment](#codebuild-deployment) -- [Allowlist or remove FAIL from resources](#allowlist-or-allowlist-or-remove-a-fail-from-resources) +- [Allowlist](#allowlist-or-remove-a-fail-from-resources) - [Fix](#how-to-fix-every-fail) - [Troubleshooting](#troubleshooting) - [Extras](#extras) @@ -495,6 +495,15 @@ Sometimes you may find resources that are intentionally configured in a certain S3 URIs are also supported as allowlist file, e.g. `s3://bucket/prefix/allowlist_sample.txt` >Make sure that the used credentials have s3:GetObject permissions in the S3 path where the allowlist file is located. +DynamoDB table ARNs are also supported as allowlist file, e.g. `arn:aws:dynamodb:us-east-1:111111222222:table/allowlist` +>Make sure that the table has `account_id` as partition key and `rule` as sort key, and that the used credentials have dynamodb:Scan permissions in the table. +>

image

+ +>The field `account_id` can contains either an account ID or an `*` (which applies to all the accounts that use this table as a whitelist). As in the traditional allowlist file, the `rule` field must contain `checkID:resourcename` pattern. +>

image

+ + + Allowlist option works along with other options and adds a `WARNING` instead of `INFO`, `PASS` or `FAIL` to any output format except for `json-asff`. ## How to fix every FAIL diff --git a/include/allowlist b/include/allowlist index fc67c67b..fbeaa0ad 100644 --- a/include/allowlist +++ b/include/allowlist @@ -12,32 +12,71 @@ # specific language governing permissions and limitations under the License. allowlist(){ - # check if the file is an S3 URI - if grep -q -E "^s3://([^/]+)/(.*?([^/]+))$" <<< "$ALLOWLIST_FILE"; then - # download s3 object - local S3_ALLOWLIST_FILE=allowlist_s3_file.txt - echo -e "$NOTICE Downloading allowlist from S3 URI $ALLOWLIST_FILE ..." - if ! $AWSCLI s3 cp $ALLOWLIST_FILE $S3_ALLOWLIST_FILE $PROFILE_OPT > /dev/null 2>&1; then - echo "$BAD FAIL! Access Denied trying to download allowlist from the S3 URI, please make sure it is correct and/or you have permissions to get the S3 object.$NORMAL" - EXITCODE=1 - exit $EXITCODE - fi - echo -e "$OK Success! Allowlist was downloaded, starting Prowler...$NORMAL" - # ignore lines starting with # (comments) - # ignore inline comments: check1:foo # inline comment - ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "$S3_ALLOWLIST_FILE") | sed 's/[[:space:]]*#.*$//g') - # remove temporary file - rm -f "$S3_ALLOWLIST_FILE" + # Check if the file is an S3 URI + if grep -q -E "^s3://([^/]+)/(.*?([^/]+))$" <<< "${ALLOWLIST_FILE}"; then + allowlist_S3 + # Check if the file is a DynamoDB ARN + elif grep -q -E "^arn:aws:dynamodb:\w+(?:-\w+)+:\d{12}:table\/[A-Za-z0-9]+$" <<< "${ALLOWLIST_FILE}"; then + allowlist_DynamoDB else - # Check if input allowlist file exists - if [[ -f "$ALLOWLIST_FILE" ]]; then - # ignore lines starting with # (comments) - # ignore inline comments: check1:foo # inline comment - ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "$ALLOWLIST_FILE") | sed 's/[[:space:]]*#.*$//g') - else - echo "$BAD FAIL! $ALLOWLIST_FILE does not exist, please input a valid allowlist file.$NORMAL" + # Check if the file is a DynamoDB ARN + allowlist_Textfile + fi +} + +allowlist_S3() { + # download s3 object + local S3_ALLOWLIST_FILE=allowlist_s3_file.txt + echo -e "${NOTICE} Downloading allowlist from S3 URI ${ALLOWLIST_FILE} ...${NORMAL}" + if ! "${AWSCLI}" s3 cp "${ALLOWLIST_FILE}" "${S3_ALLOWLIST_FILE}" ${PROFILE_OPT} > /dev/null 2>&1; then + echo "${BAD} FAIL! Access Denied trying to download allowlist from the S3 URI, please make sure it is correct and/or you have permissions to get the S3 object.${NORMAL}" + EXITCODE=1 + exit "${EXITCODE}" + fi + echo -e "${OK} Success! Allowlist was downloaded, starting Prowler...${NORMAL}" + # ignore lines starting with # (comments) + # ignore inline comments: check1:foo # inline comment + ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "${S3_ALLOWLIST_FILE}") | sed 's/[[:space:]]*#.*$//g') + # remove temporary file + rm -f "${S3_ALLOWLIST_FILE}" +} + +allowlist_DynamoDB() { + echo -e "${NOTICE} Getting allowlist from DynamoDB table ${ALLOWLIST_FILE} ...${NORMAL}" + DYNAMO_REGION=$(cut -d ":" -f 4 <<< "${ALLOWLIST_FILE}") + DYNAMO_TABLE=$(cut -d "/" -f 2 <<< "${ALLOWLIST_FILE}") + DYNAMO_ITEMS=$(${AWSCLI} dynamodb execute-statement --statement "SELECT rule FROM ${DYNAMO_TABLE} WHERE account_id=""'""${ACCOUNT_NUM}""'"" or account_id='*'" ${PROFILE_OPT} --region ${DYNAMO_REGION} --output json 2>&1 ) + if grep -q -E 'AccessDenied|UnauthorizedOperation|ResourceNotFoundException' <<< "${DYNAMO_ITEMS}"; then + echo "${BAD} FAIL! Access Denied trying to get allowlist from the DynamoDB, please make sure it is correct and/or you have permissions to scan the table ${DYNAMO_TABLE}.${NORMAL}" + EXITCODE=1 + exit ${EXITCODE} + fi + if [[ $(jq '."Items" | length' <<< "${DYNAMO_ITEMS}") -eq 0 ]]; then + echo "${NOTICE} No allowed resources were found for account ${ACCOUNT_NUM}, starting Prowler...${NORMAL}" + else + # Convert elements to allowlist file + ALLOWLIST=$(jq -r '.Items[].rule.S' <<< "${DYNAMO_ITEMS}") + if grep -q "null" <<< "${ALLOWLIST}"; then + echo "${BAD} FAIL! No rule key found in DynamoDB table, please make sure the table has rule as a sort key and account_id as the partition key...${NORMAL}" EXITCODE=1 - exit $EXITCODE + exit ${EXITCODE} + else + echo "${OK} Success! Allowed resources were added for account ${ACCOUNT_NUM}, starting Prowler...${NORMAL}" fi fi +} + +allowlist_Textfile() { + echo -e "${NOTICE} Getting allowlist from input file ${ALLOWLIST_FILE} ...${NORMAL}" + # Check if input allowlist file exists + if [[ -f "${ALLOWLIST_FILE}" ]]; then + # ignore lines starting with # (comments) + # ignore inline comments: check1:foo # inline comment + ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "${ALLOWLIST_FILE}") | sed 's/[[:space:]]*#.*$//g') + echo -e "${OK} Success! Allowlist was downloaded, starting Prowler...${NORMAL}" + else + echo "${BAD} FAIL! ${ALLOWLIST_FILE} does not exist, please input a valid allowlist file.${NORMAL}" + EXITCODE=1 + exit ${EXITCODE} + fi } \ No newline at end of file