From 66c59ea1f707341fb9efad2719e3065a86554686 Mon Sep 17 00:00:00 2001 From: Jerome Caffet <15911786+jcaffet@users.noreply.github.com> Date: Sun, 6 Oct 2019 18:29:21 +0200 Subject: [PATCH 01/38] add extra761 EBS default encryption --- checks/check_extra761 | 29 +++++++++++++++++++++++++++++ groups/group7_extras | 4 ++-- groups/group9_gdpr | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 checks/check_extra761 diff --git a/checks/check_extra761 b/checks/check_extra761 new file mode 100644 index 0000000000..39990d7da4 --- /dev/null +++ b/checks/check_extra761 @@ -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 --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 +} diff --git a/groups/group7_extras b/groups/group7_extras index 2d51b26034..6372a65813 100644 --- a/groups/group7_extras +++ b/groups/group7_extras @@ -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' # Extras 759 and 760 (lambda variables and code secrets finder are not included) -# to run detect-secrets use `./prowler -g secrets` \ No newline at end of file +# to run detect-secrets use `./prowler -g secrets` diff --git a/groups/group9_gdpr b/groups/group9_gdpr index 7df4c6ecaf..858e4ab647 100644 --- a/groups/group9_gdpr +++ b/groups/group9_gdpr @@ -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 From 64e38dd843566d08d8be24665c0f9cb489d086e8 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 19:58:49 -0500 Subject: [PATCH 02/38] bring in megaprowler code --- util/Audit_Exec_Role.yaml | 68 +++++++ util/Audit_Pipeline.yaml | 411 ++++++++++++++++++++++++++++++++++++++ util/config | 32 +++ util/megaprowler.sh | 190 ++++++++++++++++++ 4 files changed, 701 insertions(+) create mode 100644 util/Audit_Exec_Role.yaml create mode 100644 util/Audit_Pipeline.yaml create mode 100644 util/config create mode 100644 util/megaprowler.sh diff --git a/util/Audit_Exec_Role.yaml b/util/Audit_Exec_Role.yaml new file mode 100644 index 0000000000..ba9129a5dd --- /dev/null +++ b/util/Audit_Exec_Role.yaml @@ -0,0 +1,68 @@ +--- +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" + - !Sub "arn:aws:iam::${AuditorAccountId}:role${AuditRolePathName}" + Action: + - "sts:AssumeRole" + 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" diff --git a/util/Audit_Pipeline.yaml b/util/Audit_Pipeline.yaml new file mode 100644 index 0000000000..a2fb2f29c4 --- /dev/null +++ b/util/Audit_Pipeline.yaml @@ -0,0 +1,411 @@ +--- +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 + 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 + ProwlerResultsBucket: + 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 "${ProwlerResultsBucket.Arn}/*" + - Effect: Allow + Action: + - s3:ListBucket + - s3:HeadBucket + - s3:GetBucketLocation + - s3:GetBucketAcl + Resource: + - !Sub "${ProwlerResultsBucket.Arn}" + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "${ProwlerResultsBucket.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: + Name: "ProwlerResults" + Type: "S3" + Location: !Ref ProwlerResultsBucket + 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 "${ProwlerResultsBucket.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/config + - /bin/bash prowler/util/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: + - out/**/* + discard-paths: no + + + + + 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 ProwlerResultsBucket \ No newline at end of file diff --git a/util/config b/util/config new file mode 100644 index 0000000000..1b110bd957 --- /dev/null +++ b/util/config @@ -0,0 +1,32 @@ +#!/bin/bash +########### CODEBUILD CONFIGURATION ################## + +## 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 "," "|") + 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' diff --git a/util/megaprowler.sh b/util/megaprowler.sh new file mode 100644 index 0000000000..4fe063710d --- /dev/null +++ b/util/megaprowler.sh @@ -0,0 +1,190 @@ +#!/bin/bash + +# source the configuration data from "config" in this directory +if [[ -f $(dirname $0)/config ]]; then + . $(dirname $0)/config +else + echo "CONFIG file missing - $(dirname $0)/config" + exit -1 +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 -1 +fi +if [[ -z $AUDIT_ROLE ]]; then + echo "Missing audit role from config file" + exit -1 +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 $(which aws) ]]; then + aws --version +else + echo "AWS CLI is not in PATH ... giving up" + exit 4 +fi + +if [[ -x $(which 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 [[ Ec2InstanceMetadata ]] + +if [[ -f ${PROWLER} && -x ${PROWLER} ]]; then + PROWLER_VERSION=$(${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 $(which 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=' &' + 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 +export 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}]" >> $AWS_MASTERS_CREDENTIALS_FILE + echo "role_arn = arn:aws:iam::${org}:role${AUDIT_ROLE}" >> $AWS_MASTERS_CREDENTIALS_FILE + echo "credential_source = ${CREDSOURCE}" >> $AWS_MASTERS_CREDENTIALS_FILE + 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 + 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}]" >> $AWS_TARGETS_CREDENTIALS_FILE + echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" >> $AWS_TARGETS_CREDENTIALS_FILE + echo "credential_source = ${CREDSOURCE}" >> $AWS_TARGETS_CREDENTIALS_FILE + 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}]" >> $AWS_TARGETS_CREDENTIALS_FILE + echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" >> $AWS_TARGETS_CREDENTIALS_FILE + echo "credential_source = ${CREDSOURCE}" >> $AWS_TARGETS_CREDENTIALS_FILE + 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) + + ${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 +${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 \ No newline at end of file From d0af7f439fe062f15faf5799e1dad30fd8837f5d Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 20:10:30 -0500 Subject: [PATCH 03/38] remove 'out' from artifact storage path --- util/Audit_Pipeline.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/Audit_Pipeline.yaml b/util/Audit_Pipeline.yaml index a2fb2f29c4..a6935635fa 100644 --- a/util/Audit_Pipeline.yaml +++ b/util/Audit_Pipeline.yaml @@ -295,8 +295,9 @@ Resources: - find /tmp/ -name prowler\* | xargs -I % cp % ${OUTDIAG} || true artifacts: files: - - out/**/* + - '**/*' discard-paths: no + base-directory: out From b5ccdad3dcb7c9a32c24243d4f43d056d934716a Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 20:21:07 -0500 Subject: [PATCH 04/38] change bucket resource name cleans up auto-generated bucket name --- util/Audit_Pipeline.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/util/Audit_Pipeline.yaml b/util/Audit_Pipeline.yaml index a6935635fa..d4fec59be3 100644 --- a/util/Audit_Pipeline.yaml +++ b/util/Audit_Pipeline.yaml @@ -54,6 +54,8 @@ Parameters: 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 @@ -120,7 +122,7 @@ Parameters: Resources: # S3 Bucket for Results, Config - ProwlerResultsBucket: + ProwlerResults: Type: "AWS::S3::Bucket" Properties: # BucketName: !Sub "audit-results-${AWS::AccountId}" @@ -178,7 +180,7 @@ Resources: - s3:GetObject - s3:GetObjectVersion Resource: - - !Sub "${ProwlerResultsBucket.Arn}/*" + - !Sub "${ProwlerResults.Arn}/*" - Effect: Allow Action: - s3:ListBucket @@ -186,7 +188,7 @@ Resources: - s3:GetBucketLocation - s3:GetBucketAcl Resource: - - !Sub "${ProwlerResultsBucket.Arn}" + - !Sub "${ProwlerResults.Arn}" - Effect: Allow Action: - logs:CreateLogGroup @@ -194,7 +196,7 @@ Resources: - logs:PutLogEvents Resource: - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" - - !Sub "${ProwlerResultsBucket.Arn}" + - !Sub "${ProwlerResults.Arn}" - Effect: Allow Action: - ssm:GetParameters @@ -227,7 +229,7 @@ Resources: Artifacts: Name: "ProwlerResults" Type: "S3" - Location: !Ref ProwlerResultsBucket + Location: !Ref ProwlerResults Path: "prowler" NamespaceType: NONE Packaging: NONE @@ -240,7 +242,7 @@ Resources: StreamName: "codebuild_runs" S3Logs: Status: DISABLED - # Location: !Sub "${ProwlerResultsBucket.Arn}/codebuild_run_logs" + # Location: !Sub "${ProwlerResults.Arn}/codebuild_run_logs" EncryptionDisabled: False BadgeEnabled: False Tags: @@ -409,4 +411,4 @@ Resources: Outputs: ResultsBucket: Description: S3 Bucket with Prowler Results, Logs, Configs - Value: !Ref ProwlerResultsBucket \ No newline at end of file + Value: !Ref ProwlerResults From 70de0231140e2ce8a7fc407375fa0b42b3c3c0ef Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 20:30:59 -0500 Subject: [PATCH 05/38] more output structure cleanup --- util/Audit_Pipeline.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/Audit_Pipeline.yaml b/util/Audit_Pipeline.yaml index d4fec59be3..c8b7ef8c98 100644 --- a/util/Audit_Pipeline.yaml +++ b/util/Audit_Pipeline.yaml @@ -226,8 +226,8 @@ Resources: PrivilegedMode: False Image: "aws/codebuild/standard:2.0-1.12.0" ImagePullCredentialsType: "CODEBUILD" - Artifacts: - Name: "ProwlerResults" + Artifacts: # s3://stack-prowlerresults-randomness/prowler/results/... + Name: "results" Type: "S3" Location: !Ref ProwlerResults Path: "prowler" From e0a77b3e46133ead3e21ecde083f08d6e5cf4f73 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 21:12:24 -0500 Subject: [PATCH 06/38] cleanup using shellcheck --- util/config | 9 ++--- util/megaprowler.sh | 84 +++++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/util/config b/util/config index 1b110bd957..12c354927b 100644 --- a/util/config +++ b/util/config @@ -1,25 +1,26 @@ #!/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 "," " ") + ORG_MASTERS=$(echo "${PROWL_MASTER_ACCOUNTS}" | tr "," " ") fi if [ "none" == "${PROWL_STANDALONE_ACCOUNTS}" ]; then STANDALONE_ACCOUNTS="" else - STANDALONE_ACCOUNTS=$(echo ${PROWL_STANDALONE_ACCOUNTS} | tr "," " ") + 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 "," "|") + skip_inside=$(echo "${PROWL_SKIP_ACCOUNTS}" | tr "," "|") + # shellcheck disable=SC2116 SKIP_ACCOUNTS_REGEX=$(echo "(${skip_inside})" ) fi diff --git a/util/megaprowler.sh b/util/megaprowler.sh index 4fe063710d..7dbd48470e 100644 --- a/util/megaprowler.sh +++ b/util/megaprowler.sh @@ -1,11 +1,14 @@ #!/bin/bash +BASEDIR=$(dirname "${0}") # source the configuration data from "config" in this directory -if [[ -f $(dirname $0)/config ]]; then - . $(dirname $0)/config +if [[ -f "${BASEDIR}/config" ]]; then + # shellcheck disable=SC1090 + . "${BASEDIR}/config" + else - echo "CONFIG file missing - $(dirname $0)/config" - exit -1 + echo "CONFIG file missing - ${BASEDIR}/config" + exit 255 fi ## Check Environment variables which are set by config @@ -19,11 +22,11 @@ fi if [[ -z $CHECKGROUP ]]; then echo "Missing check group from config file" - exit -1 + exit 255 fi if [[ -z $AUDIT_ROLE ]]; then echo "Missing audit role from config file" - exit -1 + exit 255 fi ## ======================================================================================== @@ -43,14 +46,14 @@ fi ## Check Requirements -if [[ -x $(which aws) ]]; then +if [[ -x $(command -v aws) ]]; then aws --version else echo "AWS CLI is not in PATH ... giving up" exit 4 fi -if [[ -x $(which jq) ]]; then +if [[ -x $(command -v jq) ]]; then jq --version else echo "JQ is not in PATH ... giving up" @@ -62,10 +65,9 @@ if [[ -z $CREDSOURCE ]]; then echo "No source for base credentials ... giving up" exit 5 fi -# if [[ Ec2InstanceMetadata ]] if [[ -f ${PROWLER} && -x ${PROWLER} ]]; then - PROWLER_VERSION=$(${PROWLER} -V) + ${PROWLER} -V else echo "Unable to execute prowler from ${PROWLER}" exit 3 @@ -79,10 +81,10 @@ STAMP=$(date -u +%Y%m%dT%H%M%SZ) ## Create output subdirs OUTDATA="${OUTBASE}/data/${DAYPATH}" OUTLOGS="${OUTBASE}/logs/${DAYPATH}" -mkdir -p ${OUTDATA} ${OUTLOGS} +mkdir -p "${OUTDATA}" "${OUTLOGS}" -if [[ -x $(which parallel) ]]; then +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 @@ -102,15 +104,15 @@ ALL_ACCOUNTS="" # Create a temporary credential file -export AWS_MASTERS_CREDENTIALS_FILE=$(mktemp -t prowler.masters-XXXXXX) +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 +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 +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 @@ -118,34 +120,38 @@ 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}]" >> $AWS_MASTERS_CREDENTIALS_FILE - echo "role_arn = arn:aws:iam::${org}:role${AUDIT_ROLE}" >> $AWS_MASTERS_CREDENTIALS_FILE - echo "credential_source = ${CREDSOURCE}" >> $AWS_MASTERS_CREDENTIALS_FILE - echo "" >> $AWS_MASTERS_CREDENTIALS_FILE + { + 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' ) + 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 - ORG_ACCOUNTS=$( cat ${OUTLOGS}/${STAMP}-${org_id}-account-list.json | jq -r '.Accounts[].Id' | tr "\n" " ") + aws --output json --profile "audit_${org}" organizations list-accounts > "${OUTLOGS}/${STAMP}-${org_id}-account-list.json" + 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 + 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}]" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "credential_source = ${CREDSOURCE}" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "" >> $AWS_TARGETS_CREDENTIALS_FILE + { + 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 @@ -155,10 +161,12 @@ 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}]" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "credential_source = ${CREDSOURCE}" >> $AWS_TARGETS_CREDENTIALS_FILE - echo "" >> $AWS_TARGETS_CREDENTIALS_FILE + { + 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 @@ -170,11 +178,11 @@ fi export AWS_SHARED_CREDENTIALS_FILE=${AWS_TARGETS_CREDENTIALS_FILE} ## visit each target account -NUM_ACCOUNTS=$(grep -cE '^\[' ${AWS_TARGETS_CREDENTIALS_FILE}) +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) +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) ${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 @@ -187,4 +195,4 @@ 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 \ No newline at end of file +rm "$AWS_MASTERS_CREDENTIALS_FILE" "$AWS_TARGETS_CREDENTIALS_FILE" \ No newline at end of file From 70304dc2a248488c62bb2eaa70fd3d180484a8c6 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 21:16:17 -0500 Subject: [PATCH 07/38] suppress remaining shell check warnings --- util/megaprowler.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/megaprowler.sh b/util/megaprowler.sh index 7dbd48470e..c77adddf14 100644 --- a/util/megaprowler.sh +++ b/util/megaprowler.sh @@ -95,6 +95,7 @@ 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 @@ -136,6 +137,7 @@ for org in $ORG_MASTERS ; do # 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}" @@ -184,10 +186,12 @@ for member in $(grep -E '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}" | tr -d '][') ; 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}" From 64667ea9d0aced4303ddf5680b0dacae2232d1e6 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Fri, 11 Oct 2019 21:46:20 -0500 Subject: [PATCH 08/38] grant codebuild the ability to assume audit role --- util/Audit_Exec_Role.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/util/Audit_Exec_Role.yaml b/util/Audit_Exec_Role.yaml index ba9129a5dd..673defaa2e 100644 --- a/util/Audit_Exec_Role.yaml +++ b/util/Audit_Exec_Role.yaml @@ -26,14 +26,19 @@ Resources: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - - - Effect: "Allow" + - 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" - - !Sub "arn:aws:iam::${AuditorAccountId}:role${AuditRolePathName}" Action: - "sts:AssumeRole" + - Effect: "Allow" + Principal: + Service: + - "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + # TODO: restrict to only AuditorAccount only Policies: - PolicyName: "ProwlerPolicyAdditions" PolicyDocument: From 7e28f85247a2ead8dacc68f666e9e42467c965e2 Mon Sep 17 00:00:00 2001 From: Jerome Caffet <15911786+jcaffet@users.noreply.github.com> Date: Sun, 13 Oct 2019 08:02:18 +0200 Subject: [PATCH 09/38] add cli options --- checks/check_extra761 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra761 b/checks/check_extra761 index 39990d7da4..a74f76de3e 100644 --- a/checks/check_extra761 +++ b/checks/check_extra761 @@ -19,7 +19,7 @@ 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 --query 'EbsEncryptionByDefault') + 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 From 40a2ea6c90b4ef4f3d166203def602ff0c18c4ac Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Sun, 13 Oct 2019 19:05:57 +0200 Subject: [PATCH 10/38] fixed region for extra757 and extra758 --- checks/check_extra757 | 6 +++--- checks/check_extra758 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/checks/check_extra757 b/checks/check_extra757 index 162c7fc579..0320081a9a 100644 --- a/checks/check_extra757 +++ b/checks/check_extra757 @@ -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 } diff --git a/checks/check_extra758 b/checks/check_extra758 index 93f1d16e75..1c402aa563 100644 --- a/checks/check_extra758 +++ b/checks/check_extra758 @@ -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 } From 27305365efc67c0d768720c46524f4c52bbbd761 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 14 Oct 2019 18:10:34 +0200 Subject: [PATCH 11/38] Rename util/Audit_Exec_Role.yaml to util/multi-account/Audit_Exec_Role.yaml --- util/{ => multi-account}/Audit_Exec_Role.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/{ => multi-account}/Audit_Exec_Role.yaml (100%) diff --git a/util/Audit_Exec_Role.yaml b/util/multi-account/Audit_Exec_Role.yaml similarity index 100% rename from util/Audit_Exec_Role.yaml rename to util/multi-account/Audit_Exec_Role.yaml From ffcfef02a6c8e69dca4a268fce2310099337138a Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 14 Oct 2019 18:10:57 +0200 Subject: [PATCH 12/38] Rename util/Audit_Pipeline.yaml to util/multi-account/Audit_Pipeline.yaml --- util/{ => multi-account}/Audit_Pipeline.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/{ => multi-account}/Audit_Pipeline.yaml (100%) diff --git a/util/Audit_Pipeline.yaml b/util/multi-account/Audit_Pipeline.yaml similarity index 100% rename from util/Audit_Pipeline.yaml rename to util/multi-account/Audit_Pipeline.yaml From 81cc85a8fc8f827fae7a24e9292b821d6f63c422 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 14 Oct 2019 18:11:24 +0200 Subject: [PATCH 13/38] Rename util/config to util/multi-account/config --- util/{ => multi-account}/config | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/{ => multi-account}/config (100%) diff --git a/util/config b/util/multi-account/config similarity index 100% rename from util/config rename to util/multi-account/config From 38c0b601414003812ff92bb4c4f531c6a0b8a8ec Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 14 Oct 2019 18:11:46 +0200 Subject: [PATCH 14/38] Rename util/megaprowler.sh to util/multi-account/megaprowler.sh --- util/{ => multi-account}/megaprowler.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename util/{ => multi-account}/megaprowler.sh (99%) diff --git a/util/megaprowler.sh b/util/multi-account/megaprowler.sh similarity index 99% rename from util/megaprowler.sh rename to util/multi-account/megaprowler.sh index c77adddf14..6c4a90f1bb 100644 --- a/util/megaprowler.sh +++ b/util/multi-account/megaprowler.sh @@ -199,4 +199,4 @@ 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" \ No newline at end of file +rm "$AWS_MASTERS_CREDENTIALS_FILE" "$AWS_TARGETS_CREDENTIALS_FILE" From ddb498320ac7ecf38662b4bea0972803df43cf3d Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Mon, 14 Oct 2019 15:39:33 -0500 Subject: [PATCH 15/38] bring in quoting nits --- util/multi-account/megaprowler.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/multi-account/megaprowler.sh b/util/multi-account/megaprowler.sh index 6c4a90f1bb..623b642e2a 100644 --- a/util/multi-account/megaprowler.sh +++ b/util/multi-account/megaprowler.sh @@ -197,6 +197,6 @@ ${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 +# 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" From d4fad174160d1ca4c62aa6d936fa71a04e4e2833 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Mon, 14 Oct 2019 15:42:09 -0500 Subject: [PATCH 16/38] update pipeline commands to use multi-account path --- util/multi-account/Audit_Pipeline.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/multi-account/Audit_Pipeline.yaml b/util/multi-account/Audit_Pipeline.yaml index c8b7ef8c98..acd0c21652 100644 --- a/util/multi-account/Audit_Pipeline.yaml +++ b/util/multi-account/Audit_Pipeline.yaml @@ -287,8 +287,8 @@ Resources: # - /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/config - - /bin/bash prowler/util/megaprowler.sh out + - 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: From 77b3a9b4d9afc6b7aead898f27e9ba132736254c Mon Sep 17 00:00:00 2001 From: Ricardo Oliveira Date: Tue, 15 Oct 2019 11:12:59 +0100 Subject: [PATCH 17/38] unsetting excluded_checks --- prowler | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/prowler b/prowler index 7f92e7b1e2..fb60a32f41 100755 --- a/prowler +++ b/prowler @@ -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 From 826cc00a7ca9c39a61a9a4f34d868c9297c2c0d6 Mon Sep 17 00:00:00 2001 From: Ricardo Oliveira Date: Tue, 15 Oct 2019 10:43:58 +0100 Subject: [PATCH 18/38] replacing git clone with ADD as to not cache layer indefinetely --- util/Dockerfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/util/Dockerfile b/util/Dockerfile index 116bc1f4f4..ae0cb0ccc0 100644 --- a/util/Dockerfile +++ b/util/Dockerfile @@ -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} From 2c531a2ffcbfb029bc1df8144bf68f0ee4065142 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Mon, 21 Oct 2019 09:28:00 -0500 Subject: [PATCH 19/38] add check for unsupported lambda runtimes --- checks/check_extra762 | 44 +++++++++++++++++++++++++++++++++++++++++++ groups/group7_extras | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 checks/check_extra762 diff --git a/checks/check_extra762 b/checks/check_extra762 new file mode 100644 index 0000000000..b622f0d3f6 --- /dev/null +++ b/checks/check_extra762 @@ -0,0 +1,44 @@ +#!/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)' + + textInfo "Looking for deprecated runtimes used by Lambda functions across all regions... " + textInfo "This check may take a while depending on the number of functions. " + 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 + # obselete runtime found + textFail "Obselete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" + else + textPass "Supported ${runtime} runtime found in Lambda function ${fname}" "$regx" + fi + done + else + textInfo "$regx: No Lambda functions found" "$regx" + fi + done +} diff --git a/groups/group7_extras b/groups/group7_extras index 6372a65813..b186e032b1 100644 --- a/groups/group7_extras +++ b/groups/group7_extras @@ -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' +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` From e683ea538445fd21b908b906f44b6b5bb157670e Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Mon, 21 Oct 2019 09:38:16 -0500 Subject: [PATCH 20/38] fix over-quoting bug --- checks/check_extra762 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra762 b/checks/check_extra762 index b622f0d3f6..410c4ea038 100644 --- a/checks/check_extra762 +++ b/checks/check_extra762 @@ -25,7 +25,7 @@ extra762(){ textInfo "Looking for deprecated runtimes used by Lambda functions across all regions... " textInfo "This check may take a while depending on the number of functions. " 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" "%") + 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) From 23a7c7f393ae56de403553555017460d486c5aae Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Mon, 21 Oct 2019 18:07:56 -0500 Subject: [PATCH 21/38] fix spelling error in message --- checks/check_extra762 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra762 b/checks/check_extra762 index 410c4ea038..eaa4faad75 100644 --- a/checks/check_extra762 +++ b/checks/check_extra762 @@ -32,7 +32,7 @@ extra762(){ runtime=$(echo "$lambdafunction" | cut -d'%' -f2) if echo "$lambdafunction" | grep -Eq $OBSOLETE ; then # obselete runtime found - textFail "Obselete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" + textFail "Obsolete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" else textPass "Supported ${runtime} runtime found in Lambda function ${fname}" "$regx" fi From 827b1fdb3bedd64662f5b0968daa1062f91498dc Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Tue, 22 Oct 2019 08:12:00 -0500 Subject: [PATCH 22/38] add region info to textFail,textPass output --- checks/check_extra762 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/checks/check_extra762 b/checks/check_extra762 index eaa4faad75..5228d6e046 100644 --- a/checks/check_extra762 +++ b/checks/check_extra762 @@ -32,9 +32,9 @@ extra762(){ runtime=$(echo "$lambdafunction" | cut -d'%' -f2) if echo "$lambdafunction" | grep -Eq $OBSOLETE ; then # obselete runtime found - textFail "Obsolete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" + textFail "$regx: Obsolete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" else - textPass "Supported ${runtime} runtime found in Lambda function ${fname}" "$regx" + textPass "$regx: Supported ${runtime} runtime found in Lambda function ${fname}" "$regx" fi done else From be0bc7aa6561f2ff261707060c6f3078c43fa458 Mon Sep 17 00:00:00 2001 From: "Mr. Secure" Date: Tue, 22 Oct 2019 10:35:48 -0500 Subject: [PATCH 23/38] extra 7.62 - output cleanup - remove warnings about long execution - update pass/fail text to help split on ':' for CSV post-processing --- checks/check_extra762 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/checks/check_extra762 b/checks/check_extra762 index 5228d6e046..b54cd5ac52 100644 --- a/checks/check_extra762 +++ b/checks/check_extra762 @@ -22,8 +22,6 @@ extra762(){ # 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)' - textInfo "Looking for deprecated runtimes used by Lambda functions across all regions... " - textInfo "This check may take a while depending on the number of functions. " 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 @@ -31,10 +29,9 @@ extra762(){ fname=$(echo "$lambdafunction" | cut -d'%' -f1) runtime=$(echo "$lambdafunction" | cut -d'%' -f2) if echo "$lambdafunction" | grep -Eq $OBSOLETE ; then - # obselete runtime found - textFail "$regx: Obsolete ${runtime} runtime found in Lambda funtion ${fname}" "$regx" + textFail "$regx: Obsolete runtime: ${runtime} used by: ${fname}" "$regx" else - textPass "$regx: Supported ${runtime} runtime found in Lambda function ${fname}" "$regx" + textPass "$regx: Supported runtime: ${runtime} used by: ${fname}" "$regx" fi done else From 5252518d97b081259279092fd010756f4174f2ff Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 13:38:36 +0200 Subject: [PATCH 24/38] extra73 --- checks/check_extra73 | 80 ++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index 7ca96c7f77..5612f04bf1 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -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 POSSIBLE, 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,58 @@ 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_IGNOREPUBLICACL=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.IgnorePublicAcls 2>/dev/null) + BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.BlockPublicPolicy 2>/dev/null) + BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null) + BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query 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 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 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 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 + # Look 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 + BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$($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)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) + S3_FINDING_POLICY="$bucket bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" + else + textPass "$BUCKET_LOCATION: $bucket bucket policy with conditions: $CHECK_BUCKET_ALLUSERS_POLICY_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: ALLUSERS_ACL: $S3_FINDING_ALLUSERS_ACL | AUTHUSERS_ACL: $S3_FINDING_AUTHUSERS_ACL | BUCKET_POLICY: $S3_FINDING_POLICY" "$BUCKET_LOCATION" fi done } From df5def48d9f958d1a9df680c7ba0ebfc2e0a9c57 Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 13:45:20 +0200 Subject: [PATCH 25/38] comments_and_fix --- checks/check_extra73 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index 5612f04bf1..172bbf0779 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -74,7 +74,7 @@ extra73(){ BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null) BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query 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 is public blocked (public-access-block)" "$BUCKET_LOCATION" + textPass "$BUCKET_LOCATION: $bucket bucket is public blocked (public-access-block)" "$BUCKET_LOCATION" else ## ACL # check if AllUsers is in the ACL as Grantee @@ -95,10 +95,11 @@ extra73(){ fi fi if [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET != "True" ]] && [[ $BUCKET_POLICY_STATUS == "True" ]]; then - # Look Statement Allow, Principal * and No Condition + # Here comes the magic: Look 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=$($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)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) S3_FINDING_POLICY="$bucket bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" From 5cadd0c2f28bfaa6363415cf9cd5c0122bc321e0 Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 14:03:08 +0200 Subject: [PATCH 26/38] remove_unused_variable --- checks/check_extra73 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index 172bbf0779..c1fcfc6181 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -104,7 +104,7 @@ extra73(){ | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) S3_FINDING_POLICY="$bucket bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" else - textPass "$BUCKET_LOCATION: $bucket bucket policy with conditions: $CHECK_BUCKET_ALLUSERS_POLICY_CONDITIONS" "$BUCKET_LOCATION" + textPass "$BUCKET_LOCATION: $bucket bucket policy with conditions" "$BUCKET_LOCATION" fi fi else From bb69f514566e19ac24ef3fc3a3401bce1961a6f2 Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 14:03:49 +0200 Subject: [PATCH 27/38] comment --- checks/check_extra73 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index c1fcfc6181..bb86a63b22 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -95,7 +95,7 @@ extra73(){ fi fi if [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET != "True" ]] && [[ $BUCKET_POLICY_STATUS == "True" ]]; then - # Here comes the magic: Look Statement Allow, Principal * and No Condition + # 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 From 7aa15732751bc9a349cb4bfb7771260c7d67cbed Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 14:06:29 +0200 Subject: [PATCH 28/38] comments --- checks/check_extra73 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index bb86a63b22..2be418110a 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -48,7 +48,7 @@ extra73(){ for bucket in $ALL_BUCKETS_LIST; do - # 3 DIFFERENT POSSIBLE, Let's show only 1 finding all together + # 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" From 529fc6421dc31fe7f6325a79303d546e4aa94773 Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Wed, 23 Oct 2019 15:04:22 +0200 Subject: [PATCH 29/38] better_output --- checks/check_extra73 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index 2be418110a..e662c93884 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -88,10 +88,10 @@ extra73(){ 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 bucket ACL is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" + 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 bucket ACL is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" + 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 @@ -102,7 +102,7 @@ extra73(){ # Let's do more magic and identify who can do what BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$($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)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) - S3_FINDING_POLICY="$bucket bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" + 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 @@ -113,7 +113,7 @@ extra73(){ fi fi if [[ $S3_FINDING_ALLUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_AUTHUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_POLICY != "Ok" ]] ; then - textFail "$BUCKET_LOCATION: ALLUSERS_ACL: $S3_FINDING_ALLUSERS_ACL | AUTHUSERS_ACL: $S3_FINDING_AUTHUSERS_ACL | BUCKET_POLICY: $S3_FINDING_POLICY" "$BUCKET_LOCATION" + 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 } From d026ed5cac6a87e332fa6a0a7dcb4ea5f4127f1e Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 13:22:26 +0200 Subject: [PATCH 30/38] improve_extra727 --- checks/check_extra727 | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/checks/check_extra727 b/checks/check_extra727 index 0be47f2c25..ab8e4daf67 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -18,6 +18,45 @@ CHECK_TYPE_extra727="EXTRA" CHECK_ALTERNATE_check727="extra727" extra727(){ + for regx in $REGIONS; do + 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 + SQS_POLICY=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy --output text) + if [[ "$SQS_POLICY" != "None" ]]; then + SQS_POLICY_ALLOW_ALL=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ + | jq '.Policy | 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=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ + | jq '.Policy | 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=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ + | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) + 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 without public access" "$regx" + fi + else + textPass "$regx: SQS $queue queue without policy" "$regx" + fi + done + else + textInfo "$regx: No SQS queues found" "$regx" + fi + done + + + + + + + + + + for regx in $REGIONS; do LIST_SQS=$($AWSCLI sqs list-queues $PROFILE_OPT --region $regx --query QueueUrls --output text |grep -v ^None) if [[ $LIST_SQS ]]; then From 63898690c8f785ee37dd89ce2db7f524e26f64fd Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 13:25:18 +0200 Subject: [PATCH 31/38] remove_old_check --- checks/check_extra727 | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/checks/check_extra727 b/checks/check_extra727 index ab8e4daf67..b8bf354f09 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -47,36 +47,4 @@ extra727(){ textInfo "$regx: No SQS queues found" "$regx" fi done - - - - - - - - - - - for regx in $REGIONS; do - 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" - else - textPass "$regx: SQS queue $queue seems correct" "$regx" - fi - fi - done - else - textInfo "$regx: No SQS queues found" "$regx" - fi - done } From 508a9354b7ae8fac1928b1eff2cd2dbc8000fb2b Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 23:28:58 +0200 Subject: [PATCH 32/38] fix jq array --- checks/check_extra727 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra727 b/checks/check_extra727 index b8bf354f09..e5571716a1 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -31,7 +31,7 @@ extra727(){ | jq '.Policy | 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=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ - | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) + | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | 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" From 9b81fc0ac737d5070d4ba468a022c154b42542cc Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 23:30:34 +0200 Subject: [PATCH 33/38] fix jq array --- checks/check_extra73 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index e662c93884..6ef96e7d99 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -101,7 +101,7 @@ extra73(){ 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=$($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)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + .Action + "]"' ) + | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | 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" From 6a52ebe4921e6eb1d0d77fa6270f9aa3fe78a7e0 Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 23:54:04 +0200 Subject: [PATCH 34/38] reduce_api_calls --- checks/check_extra73 | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/checks/check_extra73 b/checks/check_extra73 index 6ef96e7d99..3b0efc1abf 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -69,11 +69,12 @@ extra73(){ else # PUBLIC BLOCK # https://docs.aws.amazon.com/cli/latest/reference/s3api/get-public-access-block.html - BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.IgnorePublicAcls 2>/dev/null) - BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.BlockPublicPolicy 2>/dev/null) - BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null) - BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION --output text --query 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 + 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 @@ -86,7 +87,7 @@ extra73(){ ## 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 || $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 @@ -94,14 +95,14 @@ extra73(){ 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 + 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=$($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)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' ) + 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" From fb45fa0c0308112ddb39f6978b8969493844d8cc Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Thu, 24 Oct 2019 23:56:02 +0200 Subject: [PATCH 35/38] reduce_api_calls --- checks/check_extra727 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/checks/check_extra727 b/checks/check_extra727 index e5571716a1..a88ba0a24e 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -30,8 +30,8 @@ extra727(){ SQS_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ | jq '.Policy | 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=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ - | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)' | jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' ) + 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" From 61ef02ec5020a3adc272ddf7260d49bc9aaf535a Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Fri, 25 Oct 2019 16:42:59 +0200 Subject: [PATCH 36/38] reduce_api_calls --- checks/check_extra727 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/checks/check_extra727 b/checks/check_extra727 index a88ba0a24e..5e14e2b5c3 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -22,13 +22,13 @@ 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 - SQS_POLICY=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy --output text) - if [[ "$SQS_POLICY" != "None" ]]; then - SQS_POLICY_ALLOW_ALL=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ - | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")') + 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=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes \ - | jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)') + 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) + "]"' ) From 8bb1529c2ab7fd400eb37724bcfa9f01ad0a644f Mon Sep 17 00:00:00 2001 From: gabrielsoltz Date: Fri, 25 Oct 2019 16:46:36 +0200 Subject: [PATCH 37/38] jq_improvements --- checks/check_extra731 | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/checks/check_extra731 b/checks/check_extra731 index 5fa5544404..911108ab29 100644 --- a/checks/check_extra731 +++ b/checks/check_extra731 @@ -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 } From 4f4591dc420594b418ef984d1e22155768351bd7 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 29 Oct 2019 23:36:39 +0100 Subject: [PATCH 38/38] Added more install details and docker run --- README.md | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 70e8771655..3405705be3 100644 --- a/README.md +++ b/README.md @@ -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 , 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 , 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