├── version ├── src ├── cis-1.13.1-api-keys.sh ├── cis-1.14.1-api-keys.sh ├── cis-1.15.1-api-keys.sh ├── utility-list-gcloud-configuration.sh ├── cis-3.7.1-rdp-firewall-rules.sh ├── cis-3.2.1-legacy-networks.sh ├── cis-1.12.1-api-keys.sh ├── cis-4.2.1-compute-instance-default-service-account-full-access.sh ├── utility-list-organization-ids.sh ├── common-constants.inc ├── utility-list-folders.sh ├── cis-1.1.3-organization-iam-policy.sh ├── cis-2.1.3-organization-cloud-logging-policy.sh ├── cis-1.4.1-user-managed-service-keys.sh ├── utility-list-organization-users.sh ├── cis-1.1.4-list-organization-policies.sh ├── cis-1.7.1-service-account-user-managed-external-keys.sh ├── utility-list-organization-admins.sh ├── cis-1.1.2-folder-iam-policy.sh ├── cis-1.16.1-list-organization-essential-contacts.sh ├── cis-2.1.2-folder-cloud-logging-policy.sh ├── utility-list-project-ids.sh ├── standard-menu.inc ├── cis-1.10.1-cloud-key-rotation-period.sh ├── cis-1.9.1-vulnerable-cloud-kms-cryptokeys.sh ├── cis-1.1.1-project-iam-policy.sh ├── cis-1.8.1-service-account-roles.sh ├── cis-2.1.1-project-cloud-logging-policy.sh ├── cis-1.11.1-kms-separation-of-duties.sh ├── cis-1.5.1-service-account-privileges.sh ├── cis-1.6.1-users-with-project-level-service-permissions.sh ├── cis-3.1.1-default-networks.sh ├── utility-run-all-scripts.sh ├── utility-list-projects.sh ├── cis-1.18.1-secrets-exposed-in-cloud-functions.sh ├── utility-list-project-buckets.sh ├── functions.inc ├── cis-4.8.1-shielded-vms.sh ├── utility-list-accounts-with-privileges.sh ├── cis-3.8.2-firewall-logs.sh ├── cis-6.6.1-cloud-sql-public-ips.sh ├── utility-list-cloud-run-services.sh ├── cis-4.3.1-project-wide-ssh-keys.sh ├── utility-list-projects-pam-enabled.sh ├── cis-5.1.1-publicly-accessible-cloud-storage.sh ├── cis-3.8.3-cloud-nat-service-logs.sh ├── utility-list-gke-clusters.sh ├── cis-4.6.1-ip-forwarding-enabled.sh ├── cis-4.9.3-gke-cluster-public-ips.sh ├── cis-2.3.1-project-cloud-logging-buckets.sh ├── cis-4.1.2-app-engine-default-service-account.sh ├── cis-4.1.1-compute-instance-default-service-account.sh ├── cis-4.9.1-compute-instance-public-ips.sh ├── cis-4.5.1-enabled-serial-ports.sh ├── cis-3.8.1-vpc-flow-logs.sh ├── cis-2.2.3-organization-cloud-logging-sinks.sh ├── cis-4.9.2-project-public-ips.sh ├── cis-2.2.1-project-cloud-logging-sinks.sh ├── cis-3.8.4-vpc-dns-policy-logs.sh ├── utility-list-project-owners.sh ├── cis-2.2.2-folder-cloud-logging-sinks.sh ├── cis-4.4.1-project-level-oslogin.sh ├── cis-3.6.1-ssh-firewall-rules.sh ├── cis-3.9.4-cloud-run-load-balancers.sh ├── utility-who-created-firewall-rule.sh ├── cis-3.9.1-load-balancer-tls-policy.sh └── cis-3.9.3-load-balancer-logging.sh ├── SECURITY.md ├── ISSUES_TEMPLATE.md ├── CONTRIBUTING.md ├── .tools ├── git.sh ├── push-feature-branch.sh ├── create-feature-branch.sh ├── push-development-branch.sh └── push-main-branch.sh └── README.md /version: -------------------------------------------------------------------------------- 1 | 1.1.11 2 | -------------------------------------------------------------------------------- /src/cis-1.13.1-api-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gcloud services api-keys list 4 | -------------------------------------------------------------------------------- /src/cis-1.14.1-api-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gcloud services api-keys list 4 | -------------------------------------------------------------------------------- /src/cis-1.15.1-api-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gcloud services api-keys list 4 | -------------------------------------------------------------------------------- /src/utility-list-gcloud-configuration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gcloud config list 4 | 5 | -------------------------------------------------------------------------------- /src/cis-3.7.1-rdp-firewall-rules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Use cis-3.6.1-ssh-firewall-rules.sh" 3 | -------------------------------------------------------------------------------- /src/cis-3.2.1-legacy-networks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Use cis-3.1.1-default-networks.sh for this use case" 4 | -------------------------------------------------------------------------------- /src/cis-1.12.1-api-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | gcloud services api-keys list; 6 | -------------------------------------------------------------------------------- /src/cis-4.2.1-compute-instance-default-service-account-full-access.sh: -------------------------------------------------------------------------------- 1 | Use cis-4.1.1-compute-instance-default-service-account.sh 2 | -------------------------------------------------------------------------------- /src/utility-list-organization-ids.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2 4 | -------------------------------------------------------------------------------- /src/common-constants.inc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly BLANK_LINE="" 4 | readonly SLEEP_SECONDS=1 5 | 6 | # Get the script name without the "./" prefix 7 | SCRIPT_NAME=$(basename "$0") 8 | 9 | # Construct the error log file name with the script name and current date/time 10 | readonly ERROR_LOG_FILE="errors-${SCRIPT_NAME%.*}-$(date +"%Y-%m-%d-%H-%M-%S").log" -------------------------------------------------------------------------------- /src/utility-list-folders.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2); 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | gcloud resource-manager folders list --organization $ORGANIZATION_ID 9 | echo $BLANK_LINE; 10 | sleep $SLEEP_SECONDS; 11 | done; 12 | -------------------------------------------------------------------------------- /src/cis-1.1.3-organization-iam-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | echo "IAM Policy for Organization $ORGANIZATION_IDS"; 9 | echo $BLANK_LINE; 10 | gcloud organizations get-iam-policy $ORGANIZATION_ID; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/cis-2.1.3-organization-cloud-logging-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | echo "IAM Policy for Organization $ORGANIZATION_IDS" 9 | echo $BLANK_LINE; 10 | gcloud organizations get-iam-policy $ORGANIZATION_ID; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/cis-1.4.1-user-managed-service-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare SERVICE_ACCOUNTS=$(gcloud iam service-accounts list --format="flattened(email)" | grep email | cut -d " " -f 2) 6 | 7 | for SERVICE_ACCOUNT in $SERVICE_ACCOUNTS; do 8 | echo "Service Account $SERVICE_ACCOUNT"; 9 | echo $BLANK_LINE; 10 | gcloud iam service-accounts keys list --iam-account=$SERVICE_ACCOUNT --managed-by=user; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/utility-list-organization-users.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | echo "IAM Policy for Organization $ORGANIZATION_IDS" 9 | echo $BLANK_LINE; 10 | gcloud organizations get-iam-policy $ORGANIZATION_ID | grep user: | cut -d ":" -f 2 |sort -u; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/cis-1.1.4-list-organization-policies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | echo "Organization Policy for Organization $ORGANIZATION_IDS"; 9 | echo $BLANK_LINE; 10 | gcloud resource-manager org-policies list --organization=$ORGANIZATION_ID | grep "SET"; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/cis-1.7.1-service-account-user-managed-external-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare SERVICE_ACCOUNT_EMAILS=$(gcloud iam service-accounts list --format="flattened(email)" | grep email | cut -d " " -f 2) 6 | 7 | for SERVICE_ACCOUNT_EMAIL in $SERVICE_ACCOUNT_EMAILS; do 8 | echo "Service Account Keys for $SERVICE_ACCOUNT_EMAIL"; 9 | echo $BLANK_LINE; 10 | gcloud iam service-accounts keys list --iam-account "$SERVICE_ACCOUNT_EMAIL" --format=json 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/utility-list-organization-admins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 8 | echo "Organization Admins for $ORGANIZATION_IDS" 9 | echo $BLANK_LINE; 10 | gcloud organizations get-iam-policy $ORGANIZATION_ID | grep -B 1 'roles/resourcemanager.organizationAdmin' | grep 'user' | cut -d : -f2; 11 | echo $BLANK_LINE; 12 | sleep $SLEEP_SECONDS; 13 | done; 14 | -------------------------------------------------------------------------------- /src/cis-1.1.2-folder-iam-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ORGANIZATIONAL_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 6 | 7 | for ORGANIZATION_ID in $ORGANIZATIONAL_IDS; do 8 | echo "Working on Organizational ID $ORGANIZATION_ID" 9 | declare FOLDER_IDS=$(gcloud resource-manager folders list --organization $ORGANIZATION_ID) 10 | 11 | for FOLDER_ID in $FOLDER_IDS; do 12 | echo "Working on Folder $FOLDER_ID" 13 | echo $BLANK_LINE; 14 | gcloud resource-manager folders get-iam-policy $FOLDER_ID; 15 | echo $BLANK_LINE; 16 | sleep $SLEEP_SECONDS; 17 | done; 18 | done; 19 | -------------------------------------------------------------------------------- /src/cis-1.16.1-list-organization-essential-contacts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare ORGANIZATION_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 7 | 8 | for ORGANIZATION_ID in $ORGANIZATION_IDS; do 9 | if ! api_enabled essentialcontacts.googleapis.com; then 10 | echo "Essential Contacts API is not enabled for Organization $ORGANIZATION_ID" 11 | continue; 12 | fi 13 | 14 | echo "Essential Contacts for Organization $ORGANIZATION_IDS"; 15 | echo $BLANK_LINE; 16 | gcloud essential-contacts list --organization=$ORGANIZATION_ID; 17 | echo $BLANK_LINE; 18 | sleep $SLEEP_SECONDS; 19 | done; 20 | -------------------------------------------------------------------------------- /src/cis-2.1.2-folder-cloud-logging-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare ORGANIZATIONAL_IDS=$(gcloud organizations list --format="flattened(ID)" | grep id | cut -d " " -f 2 | cut -d "/" -f 2) 7 | 8 | for ORGANIZATION_ID in $ORGANIZATIONAL_IDS; do 9 | echo "Working on Organizational ID $ORGANIZATION_ID" 10 | declare FOLDER_IDS=$(gcloud resource-manager folders list --organization $ORGANIZATION_ID) 11 | 12 | for FOLDER_ID in $FOLDER_IDS; do 13 | echo "Working on Folder $FOLDER_ID" 14 | echo $BLANK_LINE; 15 | gcloud resource-manager folders get-iam-policy $FOLDER_ID; 16 | echo $BLANK_LINE; 17 | sleep $SLEEP_SECONDS; 18 | done; 19 | done; 20 | -------------------------------------------------------------------------------- /src/utility-list-project-ids.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to display help menu 4 | display_help() { 5 | cat << EOF 6 | Usage: $0 [options] 7 | 8 | Description: 9 | This script retrieves a list of project IDs from Google Cloud Platform using 10 | the 'gcloud' command-line tool. 11 | 12 | Options: 13 | -h, --help Display this help menu 14 | EOF 15 | exit 0 16 | } 17 | 18 | # Parse command line options 19 | while [[ $# -gt 0 ]]; do 20 | case "$1" in 21 | -h|--help) 22 | display_help 23 | ;; 24 | *) 25 | echo "Error: Unknown option '$1'" 26 | display_help 27 | ;; 28 | esac 29 | done 30 | 31 | # Retrieve list of project IDs from gcloud 32 | gcloud projects list --format="value(PROJECT_ID)" 33 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | If you discover a security issue in this project, please report it by opening an issue. **Do not create a public issue.** Instead, contact us at webpwnized at Gm4il d0t c0m with a detailed description of the vulnerability and steps to reproduce it. 5 | 6 | ## Supported Versions 7 | - We support the `main` branch and the latest tagged release. 8 | - Older versions might not receive security updates, so always use the latest release. 9 | 10 | ## Security Practices 11 | - All scripts are reviewed periodically for secure coding practices. 12 | - Sensitive information such as credentials should never be committed to the repository. 13 | - Contributors are encouraged to follow the guidelines in [CONTRIBUTING.md](./CONTRIBUTING.md) to maintain a secure codebase. 14 | -------------------------------------------------------------------------------- /src/standard-menu.inc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare PROJECT_IDS=""; 4 | declare DEBUG="False"; 5 | declare CSV="False"; 6 | declare HELP=$(cat << EOL 7 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 8 | EOL 9 | ); 10 | 11 | for arg in "$@"; do 12 | shift 13 | case "$arg" in 14 | "--help") set -- "$@" "-h" ;; 15 | "--debug") set -- "$@" "-d" ;; 16 | "--csv") set -- "$@" "-c" ;; 17 | "--project") set -- "$@" "-p" ;; 18 | *) set -- "$@" "$arg" 19 | esac 20 | done 21 | 22 | while getopts "hdcp:" option 23 | do 24 | case "${option}" 25 | in 26 | p) 27 | PROJECT_ID=${OPTARG};; 28 | d) 29 | DEBUG="True";; 30 | c) 31 | CSV="True";; 32 | h) 33 | echo $HELP; 34 | exit 0;; 35 | esac; 36 | done; 37 | -------------------------------------------------------------------------------- /src/cis-1.10.1-cloud-key-rotation-period.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | if [[ $(gcloud services list --enabled | grep -c cloudkms) == 0 ]]; then 6 | echo "Cloud KMS not enabled."; 7 | exit 1; 8 | fi; 9 | 10 | declare LOCATIONS=$(gcloud kms locations list --format="flattened(locationId)" | grep location_id | cut -d " " -f2) 11 | 12 | for LOCATION in $LOCATIONS; do 13 | echo "Keysrings for Location $LOCATION" 14 | echo $BLANK_LINE; 15 | 16 | declare KEYRINGS=$(gcloud kms keyrings list --location="$LOCATION" --format="flattened()" | grep key_id | cut -d " " -f 2) 17 | 18 | for KEYRING in $KEYRINGS; do 19 | echo "Key rotation periods for Keyring $KEYRING" 20 | echo $BLANK_LINE; 21 | declare KEYS=$(gcloud kms keys list --keyring="$KEYRING" --location="$LOCATION" --format=json'(rotationPeriod)'); 22 | echo $BLANK_LINE; 23 | done; 24 | echo $BLANK_LINE; 25 | sleep $SLEEP_SECONDS; 26 | done; 27 | -------------------------------------------------------------------------------- /ISSUES_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Issue Description 2 | 3 | **Briefly describe the issue you are facing:** 4 | - What did you expect to happen? 5 | - What actually happened? 6 | 7 | ## Steps to Reproduce 8 | 9 | 1. Clearly list the steps needed to reproduce the issue. 10 | 2. Include any relevant commands, code snippets, or configuration details. 11 | 12 | ## Expected Behavior 13 | 14 | **Describe what you expected to see instead of what actually happened.** 15 | 16 | ## Actual Behavior 17 | 18 | **Describe what actually occurred, including any error messages or logs.** 19 | 20 | ## Environment Details 21 | 22 | - **OS/Platform:** (e.g., Ubuntu 20.04, macOS 13, Windows 10) 23 | - **Command-line tool versions (if applicable):** (e.g., `gcloud` version) 24 | - **Any related library or dependency versions:** 25 | 26 | ## Additional Context 27 | 28 | **Add any other context, configuration, or logs that might be relevant to the issue:** 29 | - Screenshots, if applicable 30 | - Specific GCP project/resource details (do not include sensitive info) 31 | - Links to related issues or discussions 32 | -------------------------------------------------------------------------------- /src/cis-1.9.1-vulnerable-cloud-kms-cryptokeys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | if ! api_enabled cloudkms.googleapis.com; then 7 | echo "Cloud KMS not enabled."; 8 | exit 1; 9 | fi 10 | 11 | declare LOCATIONS=$(gcloud kms locations list --format="flattened(locationId)" | grep location_id | cut -d " " -f2) 12 | 13 | for LOCATION in $LOCATIONS; do 14 | echo "Keyrings for Location $LOCATION" 15 | echo $BLANK_LINE; 16 | 17 | declare KEYRINGS=$(gcloud kms keyrings list --location=$LOCATION --format="flattened()" | grep key_id | cut -d " " -f 2) 18 | 19 | for KEYRING in $KEYRINGS; do 20 | echo "Keys for Keyring $KEYRING" 21 | echo $BLANK_LINE; 22 | declare KEYS=$(gcloud kms keys list --keyring=$KEYRING --location=$LOCATION --format=json | jq '.[].name' --format="flattened()" | grep key_id | cut -d " " -f 2); 23 | echo $BLANK_LINE; 24 | for KEY in $KEYS; do 25 | echo "Policy for Key $KEY" 26 | echo $BLANK_LINE; 27 | gcloud kms keys get-iam-policy $KEY --keyring=$KEYRING -- location=$LOCATION --format=json | jq '.bindings[].members[]'; 28 | echo $BLANK_LINE; 29 | done; 30 | echo $BLANK_LINE; 31 | done; 32 | echo $BLANK_LINE; 33 | sleep $SLEEP_SECONDS; 34 | done; 35 | -------------------------------------------------------------------------------- /src/cis-1.1.1-project-iam-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | 38 | if [[ $PROJECT_IDS == "" ]]; then 39 | declare PROJECT_IDS=$(get_projects); 40 | fi; 41 | 42 | for PROJECT_ID in $PROJECT_IDS; do 43 | 44 | # Get project details 45 | get_project_details $PROJECT_ID 46 | 47 | echo "IAM Policy for Project $PROJECT_ID" 48 | echo "Project Application: $PROJECT_APPLICATION"; 49 | echo "Project Owner: $PROJECT_OWNER"; 50 | echo $BLANK_LINE; 51 | gcloud projects get-iam-policy $PROJECT_ID; 52 | echo $BLANK_LINE; 53 | 54 | sleep $SLEEP_SECONDS; 55 | done; 56 | -------------------------------------------------------------------------------- /src/cis-1.8.1-service-account-roles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | if [[ $PROJECT_IDS == "" ]]; then 38 | declare PROJECT_IDS=$(get_projects); 39 | fi; 40 | 41 | for PROJECT_ID in $PROJECT_IDS; do 42 | 43 | # Get project details 44 | get_project_details $PROJECT_ID 45 | 46 | echo "IAM Policy for Project $PROJECT_ID" 47 | echo "Project Application: $PROJECT_APPLICATION"; 48 | echo "Project Owner: $PROJECT_OWNER"; 49 | echo $BLANK_LINE; 50 | gcloud projects get-iam-policy $PROJECT_ID; 51 | echo $BLANK_LINE; 52 | sleep $SLEEP_SECONDS; 53 | done; 54 | -------------------------------------------------------------------------------- /src/cis-2.1.1-project-cloud-logging-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | if [[ $PROJECT_IDS == "" ]]; then 38 | declare PROJECT_IDS=$(get_projects); 39 | fi; 40 | 41 | for PROJECT_ID in $PROJECT_IDS; do 42 | 43 | # Get project details 44 | get_project_details $PROJECT_ID 45 | 46 | echo "IAM Policy for Project $PROJECT_ID" 47 | echo "Project Application: $PROJECT_APPLICATION"; 48 | echo "Project Owner: $PROJECT_OWNER"; 49 | echo $BLANK_LINE; 50 | gcloud projects get-iam-policy $PROJECT_ID; 51 | echo $BLANK_LINE; 52 | sleep $SLEEP_SECONDS; 53 | done; 54 | -------------------------------------------------------------------------------- /src/cis-1.11.1-kms-separation-of-duties.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | if [[ $PROJECT_IDS == "" ]]; then 38 | declare PROJECT_IDS=$(get_projects); 39 | fi 40 | 41 | for PROJECT_ID in $PROJECT_IDS; do 42 | 43 | # Get project details 44 | get_project_details $PROJECT_ID 45 | 46 | echo "IAM Policy for Project $PROJECT_ID" 47 | echo "Project Application: $PROJECT_APPLICATION"; 48 | echo "Project Owner: $PROJECT_OWNER"; 49 | echo $BLANK_LINE; 50 | gcloud projects get-iam-policy $PROJECT_ID --format=json | jq ".bindings[] | select (.role | contains(\"$ROLE\"))"; 51 | echo $BLANK_LINE; 52 | sleep $SLEEP_SECONDS; 53 | done; 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | We welcome and appreciate contributions to this project. By contributing, you help make this toolkit more robust and useful for the community. 4 | 5 | ## How to Contribute 6 | 7 | 1. **Fork the Repository**: 8 | Create your own fork of the repository on GitHub. 9 | 10 | 2. **Create a Branch**: 11 | Create a feature branch for your changes: 12 | ```bash 13 | git checkout -b feature/your-feature-name 14 | ``` 15 | 16 | 3. **Make Your Changes**: 17 | - Follow secure coding practices. 18 | - Ensure code is wel-commented and easy to understand. 19 | - Keep commits small and focused. 20 | 21 | 4. **Run Tests and Audits**: 22 | If applicable, run any test suites (unit tests, lint checks) before pushing your changes. 23 | 24 | 5. **Submit a Pull Request (PR)**: 25 | Open a PR against the `main` branch. Include a clear, detailed description of what you changed and why. 26 | 27 | ## Code Standards 28 | - **Bash Scripting+*: Follow POSIX standards as much as possible, ensure all scripts are tested on `bash` shell. 29 | - **Security**: Do not hard-code credentials. Do not commit secret keys or tokens. 30 | - **Style**: Use descriptive variable and function names. 31 | - **Documentation**: Update `README.md` or other relevant documentation if your changes impact usage. 32 | 33 | ## Communication 34 | Please use issues for reporting bugs or requesting features. For questions and general discussion, feel free to opene a discussion on the repository or reach out through the provided channels. 35 | -------------------------------------------------------------------------------- /src/cis-1.5.1-service-account-privileges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | 38 | if [[ $PROJECT_IDS == "" ]]; then 39 | declare PROJECT_IDS=$(get_projects); 40 | fi; 41 | 42 | for PROJECT_ID in $PROJECT_IDS; do 43 | 44 | # Get project details 45 | get_project_details $PROJECT_ID 46 | 47 | echo "Project $PROJECT_ID" 48 | echo "Project Application: $PROJECT_APPLICATION"; 49 | echo "Project Owner: $PROJECT_OWNER"; 50 | 51 | declare ACCOUNTS=$(gcloud projects get-iam-policy $PROJECT_ID --format=json | jq -r '.bindings[] | .role, .members[]') 52 | for ACCOUNT in $ACCOUNTS; do 53 | if [[ $ACCOUNT =~ 'roles' ]]; then 54 | echo "" 55 | echo "" 56 | echo $ACCOUNT 57 | echo "----------------------------" 58 | else 59 | echo $ACCOUNT 60 | fi 61 | done; 62 | echo $BLANK_LINE; 63 | sleep $SLEEP_SECONDS; 64 | done; 65 | -------------------------------------------------------------------------------- /src/cis-1.6.1-users-with-project-level-service-permissions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | 38 | if [[ $PROJECT_IDS == "" ]]; then 39 | declare PROJECT_IDS=$(get_projects); 40 | fi; 41 | 42 | for PROJECT_ID in $PROJECT_IDS; do 43 | 44 | # Get project details 45 | get_project_details $PROJECT_ID 46 | 47 | echo "Users with Project Level Service Account Permissions for Project $PROJECT_ID" 48 | echo "Project Application: $PROJECT_APPLICATION"; 49 | echo "Project Owner: $PROJECT_OWNER"; 50 | echo "" 51 | echo "Project level service account user permissions" 52 | gcloud projects get-iam-policy $PROJECT_ID --format json | jq '.bindings[].role' | grep "roles/iam.serviceAccountUser" 53 | echo "" 54 | echo "Project level service account token creator permissions" 55 | gcloud projects get-iam-policy $PROJECT_ID --format json | jq '.bindings[].role' | grep "roles/iam.serviceAccountTokenCreator" 56 | echo "" 57 | echo "-------------------------------------------------------------------------------------------"; 58 | sleep $SLEEP_SECONDS; 59 | done; 60 | -------------------------------------------------------------------------------- /src/cis-3.1.1-default-networks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | 38 | if [[ $PROJECT_IDS == "" ]]; then 39 | declare PROJECT_IDS=$(get_projects); 40 | fi; 41 | 42 | for PROJECT_ID in $PROJECT_IDS; do 43 | set_project $PROJECT_ID; 44 | 45 | if ! api_enabled compute.googleapis.com; then 46 | echo "Compute Engine API is not enabled on Project $PROJECT_ID" 47 | continue; 48 | fi 49 | 50 | declare RESULTS=$(gcloud compute networks list --quiet --format="json" | tr [:upper:] [:lower:] | jq '.[]'); 51 | 52 | declare SUBNET_MODE=""; 53 | if [[ $RESULTS != "[]" ]]; then 54 | NETWORK_NAME=$(echo $RESULTS | jq '.name'); 55 | SUBNET_MODE=$(echo $RESULTS | jq '.x_gcloud_subnet_mode'); 56 | fi; 57 | 58 | if [[ $NETWORK_NAME == "default" ]]; then 59 | echo "VIOLATION: Default network $NETWORK_NAME detected for Project $PROJECT_ID"; 60 | echo $BLANK_LINE; 61 | elif [[ $SUBNET_MODE == "legacy" ]]; then 62 | echo "VIOLATION: Legacy network $NETWORK_NAME detected for Project $PROJECT_ID"; 63 | echo $BLANK_LINE; 64 | fi; 65 | sleep $SLEEP_SECONDS; 66 | done; 67 | -------------------------------------------------------------------------------- /src/utility-run-all-scripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source functions.inc 4 | 5 | PROJECT_IDS=""; 6 | 7 | print_help(){ 8 | echo "Usage: $0 [OPTION]..." 9 | echo "Run all CIS audit scripts for each project." 10 | echo "Example: $0 -p project-12345 project-54321" 11 | echo 12 | echo "Options:" 13 | echo " -p, --project projects to be audited; multiple projects are allowed; if no project provided, all projects will be audited" 14 | echo " -h, --help display this help and exit" 15 | } 16 | 17 | for arg in "$@"; do 18 | shift 19 | case "$arg" in 20 | "--help") set -- "$@" "-h" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | h) 33 | print_help 34 | exit 0;; 35 | esac; 36 | done; 37 | 38 | declare -a commands=("jq" "gcloud") 39 | for cmd in "${commands[@]}"; do 40 | if ! command -v $cmd &> /dev/null 41 | then 42 | echo "$cmd could not be found on this host and is required to run the script. Please install the missing tool and try again." 43 | exit 1 44 | fi 45 | done; 46 | 47 | if [[ $PROJECT_IDS == "" ]]; then 48 | declare PROJECT_IDS=$(get_projects); 49 | fi; 50 | 51 | FILENAME_PATTERN="(cis)-([0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2})-([a-zA-Z/-]*)" 52 | AUDIT_LOG_PREFIX=audit 53 | AUDIT_SCRIPTS=$(ls | grep -E $FILENAME_PATTERN) 54 | 55 | 56 | for PROJECT_ID in $PROJECT_IDS; do 57 | echo "---- Starting Audit for $PROJECT_ID ----" 58 | gcloud config set project $PROJECT_ID 2>/dev/null 59 | AUDIT_LOG="$AUDIT_LOG_PREFIX-$PROJECT_ID.log" 60 | 61 | for file in $AUDIT_SCRIPTS; 62 | do 63 | echo "Running $file" 64 | echo $file | sed -E "s/$FILENAME_PATTERN\.(sh)/------CIS \2,\3------/" >> $AUDIT_LOG 65 | ./$file -p $PROJECT_ID >> $AUDIT_LOG 66 | echo "-----------------------------------------" >> $AUDIT_LOG 67 | done; 68 | done; 69 | -------------------------------------------------------------------------------- /.tools/git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Purpose: Tag a Git commit with version and annotation, commit locally, and push to remote 3 | # Usage: ./git.sh 4 | # Description: This script tags a Git commit with the specified version and annotation, 5 | # commits locally, and pushes both the tag and commit to the remote repository. 6 | 7 | # Function to print messages with a timestamp 8 | print_message() { 9 | echo "" 10 | echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" 11 | } 12 | 13 | # Function to display help message 14 | show_help() { 15 | echo "Usage: $0 " 16 | echo "" 17 | echo "Options:" 18 | echo " -h, --help Display this help message." 19 | echo "" 20 | echo "Description:" 21 | echo "This script tags a Git commit with the specified version and annotation," 22 | echo "commits locally, and pushes both the tag and commit to the remote repository." 23 | exit 0 24 | } 25 | 26 | # Function to handle errors 27 | handle_error() { 28 | print_message "Error: $1" 29 | } 30 | 31 | # Parse options 32 | while [[ "$#" -gt 0 ]]; do 33 | case $1 in 34 | -h|--help) show_help ;; 35 | *) break ;; 36 | esac 37 | shift 38 | done 39 | 40 | # Check if exactly two arguments are passed 41 | if (( $# != 2 )); then 42 | handle_error "Incorrect number of arguments. Usage: $0 " 43 | fi 44 | 45 | # Assign arguments to variables 46 | VERSION=$1 47 | ANNOTATION=$2 48 | 49 | # Tagging, committing, and pushing operations 50 | print_message "Creating tag $VERSION with annotation \"$ANNOTATION\"" 51 | git tag -a "$VERSION" -m "$ANNOTATION" || handle_error "Failed to create tag" 52 | 53 | print_message "Committing version $VERSION to local branch" 54 | git commit -a -m "$VERSION $ANNOTATION" # || handle_error "Failed to commit changes" 55 | 56 | print_message "Pushing tag $VERSION to upstream" 57 | git push --tag || handle_error "Failed to push tag to upstream" 58 | 59 | print_message "Pushing version $VERSION to upstream" 60 | git push || handle_error "Failed to push changes to upstream" 61 | 62 | print_message "Script completed successfully" 63 | -------------------------------------------------------------------------------- /.tools/push-feature-branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Purpose: Push feature branch only 3 | # Usage: ./push-feature-branch.sh 4 | # Description: This script pushes the feature branch using 'git.sh'. 5 | 6 | # Function to print messages with a timestamp 7 | print_message() { 8 | echo "" 9 | echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" 10 | } 11 | 12 | # Function to display help message 13 | show_help() { 14 | echo "Usage: $0 " 15 | echo "" 16 | echo "Options:" 17 | echo " -h, --help Display this help message." 18 | echo "" 19 | echo "Description:" 20 | echo "This script pushes the feature branch using 'git.sh'." 21 | exit 0 22 | } 23 | 24 | # Function to handle errors 25 | handle_error() { 26 | print_message "Error: $1" 27 | exit 1 28 | } 29 | 30 | # Parse options 31 | while [[ "$#" -gt 0 ]]; do 32 | case $1 in 33 | -h|--help) show_help ;; 34 | *) break ;; 35 | esac 36 | shift 37 | done 38 | 39 | # Check if exactly three arguments are passed 40 | if (( $# != 3 )); then 41 | handle_error "Incorrect number of arguments. Usage: $0 " 42 | fi 43 | 44 | # Assign arguments to variables 45 | FEATURE_BRANCH=$1 46 | VERSION=$2 47 | ANNOTATION=$3 48 | 49 | # Verify 'git.sh' script exists and is executable 50 | GIT_SCRIPT="./git.sh" 51 | if [[ ! -x "$GIT_SCRIPT" ]]; then 52 | handle_error "'git.sh' script not found or not executable" 53 | fi 54 | 55 | # Check if the feature branch exists 56 | if ! git show-ref --verify --quiet refs/heads/"$FEATURE_BRANCH"; then 57 | handle_error "Feature branch '$FEATURE_BRANCH' does not exist. Create it using: 58 | 59 | git checkout -b $FEATURE_BRANCH 60 | git push -u origin $FEATURE_BRANCH" 61 | fi 62 | 63 | # Push feature branch 64 | print_message "Checking out feature branch: $FEATURE_BRANCH" 65 | git checkout "$FEATURE_BRANCH" || handle_error "Failed to checkout feature branch: $FEATURE_BRANCH" 66 | 67 | print_message "Pushing feature branch: $FEATURE_BRANCH" 68 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push feature branch using git.sh" 69 | 70 | # Show git status 71 | print_message "Git status" 72 | git status || handle_error "Failed to show git status" 73 | 74 | print_message "Script completed successfully" 75 | exit 0 -------------------------------------------------------------------------------- /src/utility-list-projects.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################################################ 4 | # Script Name: utility-list-projects.sh 5 | # Description: This script retrieves a list of projects from Google Cloud 6 | # Platform using the 'gcloud' command-line tool and displays 7 | # relevant details for each project, such as name, application, 8 | # and owner. It also supports a delay between project outputs. 9 | # Usage: ./utility-list-projects.sh [options] 10 | # Options: -h, --help Display this help menu 11 | ################################################################################ 12 | 13 | # Include common constants 14 | source ./common-constants.inc 15 | 16 | # Function to display help menu 17 | display_help() { 18 | cat << EOF 19 | Usage: $0 [options] 20 | 21 | Description: 22 | This script retrieves a list of projects from Google Cloud Platform using the 23 | 'gcloud' command-line tool and displays relevant details for each project, 24 | such as name, application, and owner. It also supports a delay between 25 | project outputs. 26 | 27 | Options: 28 | -h, --help Display this help menu 29 | EOF 30 | exit 0 31 | } 32 | 33 | # Parse command line options 34 | while [[ $# -gt 0 ]]; do 35 | case "$1" in 36 | -h|--help) 37 | display_help 38 | ;; 39 | *) 40 | echo "Error: Unknown option '$1'" 41 | display_help 42 | ;; 43 | esac 44 | done 45 | 46 | # Get project list from gcloud 47 | RESULTS=$(gcloud projects list --format="json") 48 | 49 | # Check if there are any projects 50 | if [[ $RESULTS != "[]" ]]; then 51 | # Process each project 52 | while IFS= read -r PROJECT; do 53 | # Extract project details 54 | NAME=$(jq -r '.name' <<< "$PROJECT") 55 | APPLICATION=$(jq -r '.labels.app' <<< "$PROJECT") 56 | OWNER=$(jq -r '.labels.adid' <<< "$PROJECT") 57 | 58 | # Output project details 59 | echo "Name: $NAME" 60 | [[ $APPLICATION != "null" ]] && echo "Application: $APPLICATION" 61 | [[ $OWNER != "null" ]] && echo "Owner: $OWNER" 62 | echo "" # Blank line between projects 63 | sleep "$SLEEP_SECONDS" # Introduce delay between projects 64 | done < <(jq -rc '.[]' <<< "$RESULTS") 65 | else 66 | # No projects found 67 | echo "No projects found" 68 | fi 69 | -------------------------------------------------------------------------------- /src/cis-1.18.1-secrets-exposed-in-cloud-functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | declare SEPARATOR="----------------------------------------------------------------------------------------"; 38 | 39 | if [[ $PROJECT_IDS == "" ]]; then 40 | declare PROJECT_IDS=$(get_projects); 41 | fi; 42 | 43 | for PROJECT_ID in $PROJECT_IDS; do 44 | 45 | set_project $PROJECT_ID; 46 | 47 | if ! api_enabled cloudfunctions.googleapis.com; then 48 | echo "Cloud Functions not enabled for Project $PROJECT_ID."; 49 | continue; 50 | fi 51 | 52 | declare RESULTS=$(gcloud functions list --quiet --format="json"); 53 | 54 | if [[ $RESULTS != "[]" ]]; then 55 | 56 | # Get project details 57 | get_project_details $PROJECT_ID 58 | 59 | echo $SEPARATOR; 60 | echo "Cloud functions for project $PROJECT_ID"; 61 | echo "Project Application: $PROJECT_APPLICATION"; 62 | echo "Project Owner: $PROJECT_OWNER"; 63 | echo $BLANK_LINE; 64 | 65 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r CLOUD_FUNCTION;do 66 | 67 | NAME=$(echo $CLOUD_FUNCTION | jq -rc '.name' | cut -d '/' -f 6); 68 | BUILD_ENIVRONMENT_VARIABLES=$(echo $CLOUD_FUNCTION | jq '.buildEnvironmentVariables'); 69 | ENIVRONMENT_VARIABLES=$(echo $CLOUD_FUNCTION | jq '.environmentVariables'); 70 | INGRESS_SETTINGS=$(echo $CLOUD_FUNCTION | jq '.ingressSettings'); 71 | 72 | echo "Name: $NAME"; 73 | echo "Build Environment Variables: $BUILD_ENIVRONMENT_VARIABLES"; 74 | echo "Environment Variables: $ENIVRONMENT_VARIABLES"; 75 | if [[ $INGRESS_SETTINGS =~ "ALLOW_ALL" ]]; then echo "VIOLATION: Cloud function allows all ingress"; fi; 76 | echo $BLANK_LINE; 77 | done; 78 | else 79 | echo $SEPARATOR; 80 | echo "No cloud functions found for $PROJECT_ID"; 81 | echo $BLANK_LINE; 82 | fi; 83 | sleep $SLEEP_SECONDS; 84 | done; 85 | 86 | -------------------------------------------------------------------------------- /.tools/create-feature-branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Purpose: Create a new feature branch and push it to the remote repository 3 | 4 | # Print messages with a timestamp 5 | print_message() { 6 | echo "" 7 | echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" 8 | } 9 | 10 | # Display help message 11 | show_help() { 12 | echo "Usage: $0 " 13 | echo "" 14 | echo "Options:" 15 | echo " -h, --help Display this help message." 16 | exit 0 17 | } 18 | 19 | # Handle errors 20 | handle_error() { 21 | print_message "Error: $1" 22 | exit 1 23 | } 24 | 25 | # Parse command-line arguments 26 | while [[ "$#" -gt 0 ]]; do 27 | case $1 in 28 | -h|--help) show_help ;; 29 | *) FEATURE_BRANCH_NAME=$1; shift ;; 30 | esac 31 | done 32 | 33 | # Ensure a branch name is provided 34 | if [[ -z "$FEATURE_BRANCH_NAME" ]]; then 35 | handle_error "No feature branch name provided. Usage: $0 " 36 | fi 37 | 38 | # Check if git is installed 39 | if ! command -v git &> /dev/null; then 40 | handle_error "Git is not installed. Please install Git." 41 | fi 42 | 43 | # Ensure inside a git repository 44 | if ! git rev-parse --is-inside-work-tree &> /dev/null; then 45 | handle_error "Not inside a valid git repository." 46 | fi 47 | 48 | # Check if the branch exists locally 49 | if git show-ref --verify --quiet refs/heads/"$FEATURE_BRANCH_NAME"; then 50 | handle_error "Branch '$FEATURE_BRANCH_NAME' already exists locally." 51 | fi 52 | 53 | # Clear local cache of remote branches 54 | print_message "Fetching latest remote branches to clear any cached data." 55 | git fetch origin --prune || handle_error "Failed to fetch remote branches." 56 | 57 | # Check if the branch exists remotely 58 | if git ls-remote --heads origin "$FEATURE_BRANCH_NAME" | grep "$FEATURE_BRANCH_NAME" &> /dev/null; then 59 | handle_error "Branch '$FEATURE_BRANCH_NAME' already exists on the remote." 60 | fi 61 | 62 | # Create the new feature branch 63 | print_message "Creating new feature branch: $FEATURE_BRANCH_NAME" 64 | git checkout -b "$FEATURE_BRANCH_NAME" || handle_error "Failed to create feature branch: $FEATURE_BRANCH_NAME" 65 | 66 | # Push the new branch to the remote and set upstream tracking 67 | print_message "Pushing feature branch '$FEATURE_BRANCH_NAME' to the remote repository" 68 | git push -u origin "$FEATURE_BRANCH_NAME" || handle_error "Failed to push feature branch to the remote." 69 | 70 | # Show the current branch and status 71 | print_message "Branch '$FEATURE_BRANCH_NAME' created and pushed successfully." 72 | print_message "Current branch: $(git branch --show-current)" 73 | git status || handle_error "Failed to show git status." 74 | 75 | print_message "Script completed successfully." 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gcp-audit 2 | 3 | The GCP Audit project automates many of the audits found in the Center for Internet Security (CIS) Google Cloud Platform Foundation Benchmark. There is one script per benchmark. The script is named after the corresponding benchmark. The scripts can enumerate all projects in an organization and scan each project, or the user can pass in the project as a parameter. 4 | 5 | # Usage Instructions (Cloud Shell) 6 | 7 | ## Video Tutorial 8 | 9 | [**How to Use GCP Audit (Cloud Shell)**](https://www.youtube.com/watch?v=-3GKp9kEcwY) 10 | 11 | # Usage Instructions (Local Terminal) 12 | 13 | ## Video Tutorial 14 | 15 | [**How to Use GCP Audit (Local Terminal)**](https://www.youtube.com/watch?v=cnkr_gF7Erg) 16 | 17 | ## Dependencies 18 | 19 | ### An operating system to install the needed software 20 | 21 | If you would like to use an Ubuntu virtual machine, [**install Ubuntu on VirtualBox**](https://www.youtube.com/watch?v=Cazzls2sZVk) or other hypervisor. Ubuntu runs better on VirtualBox if [**the Guest Additions are installed**](https://www.youtube.com/watch?v=8VCeFRwRmRU). If VirtualBox is not installed, [**install VirtualBox**](https://www.youtube.com/watch?v=61GhP8DsQMw). 22 | 23 | ### The Google Cloud Platform (GPC) *gcloud* client software 24 | 25 | [**This video**](https://www.youtube.com/watch?v=04GONi_U6zU) shows [**how to install the gcloud CLI on Ubuntu Linux**](https://www.youtube.com/watch?v=04GONi_U6zU). Otherwise, follow [**the instructions for your distribution**](https://cloud.google.com/sdk/docs/install#linux) 26 | 27 | ### This project 28 | 29 | `git clone https://github.com/webpwnized/gcp-audit.git` 30 | 31 | ## Optional Pre-Installation Instructions 32 | 33 | 1. If you would like to use an Ubuntu virtual machine, [**install Ubuntu on VirtualBox**](https://www.youtube.com/watch?v=Cazzls2sZVk) or other hypervisor. 34 | 35 | 2. Ubuntu runs better on VirtualBox if [**the Guest Additions are installed**](https://www.youtube.com/watch?v=AuJGvJoMrgQ). 36 | 37 | 3. If VirtualBox is not installed, [**install VirtualBox**](https://www.youtube.com/watch?v=61GhP8DsQMw). 38 | 39 | ## Contributing 40 | 41 | Contributions are welcome! If you'd like to contribute to GCP Audit, please follow these steps: 42 | 43 | 1. Fork the repository. 44 | 2. Create a new branch (\`git checkout -b feature/my-feature\`). 45 | 3. Make your changes and commit them (\`git commit -am 'Add new feature'\`). 46 | 4. Push to the branch (\`git push origin feature/my-feature\`). 47 | 5. Create a new Pull Request. 48 | 49 | Please read our [Contribution Guidelines](CONTRIBUTING.md) for more details. 50 | 51 | ## License 52 | 53 | This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/utility-list-project-buckets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc 4 | source functions.inc 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | declare PROJECT_IDS=$(get_projects); 44 | fi; 45 | 46 | if [[ $CSV == "True" ]]; then 47 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"BUCKET_NAME\""; 48 | fi; 49 | 50 | for PROJECT_ID in $PROJECT_IDS; do 51 | 52 | set_project $PROJECT_ID; 53 | 54 | if ! api_enabled storage.googleapis.com; then 55 | if [[ $CSV != "True" ]]; then 56 | echo "Storage API is not enabled on Project $PROJECT_ID"; 57 | echo $BLANK_LINE; 58 | fi; 59 | continue; 60 | fi 61 | 62 | declare BUCKET_NAMES=$(gsutil ls); 63 | 64 | if [[ $DEBUG == "True" ]]; then 65 | echo "Buckets: $BUCKET_NAMES"; 66 | echo $BLANK_LINE; 67 | fi; 68 | 69 | if [[ $BUCKET_NAMES != "" ]]; then 70 | 71 | #Get project details 72 | get_project_details $PROJECT_ID 73 | 74 | if [[ $CSV != "True" ]]; then 75 | echo $SEPARATOR; 76 | echo "Storage Buckets for Project $PROJECT_ID"; 77 | echo $SEPARATOR; 78 | fi; 79 | 80 | for BUCKET_NAME in $BUCKET_NAMES; do 81 | 82 | if [[ $CSV != "True" ]]; then 83 | echo "Project ID: $PROJECT_ID"; 84 | echo "Project Name: $PROJECT_NAME"; 85 | echo "Project Application: $PROJECT_APPLICATION"; 86 | echo "Project Owner: $PROJECT_OWNER"; 87 | echo "Bucket Name: $BUCKET_NAME"; 88 | echo $BLANK_LINE; 89 | else 90 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$BUCKET_NAME\""; 91 | fi; 92 | 93 | done; 94 | echo $BLANK_LINE; 95 | else 96 | if [[ $CSV != "True" ]]; then 97 | echo "No storage buckets found for Project $PROJECT_ID"; 98 | echo $BLANK_LINE; 99 | fi; 100 | fi; 101 | sleep $SLEEP_SECONDS; 102 | done; 103 | 104 | -------------------------------------------------------------------------------- /src/functions.inc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to print debug information about projects 4 | debug_projects() { 5 | if [[ $DEBUG == "True" ]]; then 6 | echo "DEBUG: Projects: $PROJECTS" 7 | echo "$BLANK_LINE" 8 | fi 9 | } 10 | 11 | # Function to print debug information about JSON data 12 | debug_json() { 13 | local DATA_TYPE=$1 14 | local PROJECT_ID=$2 15 | local JSON_DATA=$3 16 | 17 | if [[ $DEBUG == "True" ]]; then 18 | echo "PROJECT: $PROJECT_ID:" 19 | echo "DEBUG: $DATA_TYPE (JSON):" 20 | echo "$(jq -C '.' <<< "$JSON_DATA")" 21 | echo "$BLANK_LINE" 22 | fi 23 | } 24 | 25 | # Function to print a message when no output is returned 26 | function no_output_returned() { 27 | local MESSAGE=$1 28 | 29 | if [[ $CSV != "True" ]]; then 30 | echo "$MESSAGE" 31 | echo "$BLANK_LINE" 32 | fi 33 | } 34 | 35 | # Function to encode double quotes by doubling them 36 | encode_double_quotes() { 37 | local input="$1" 38 | echo "$input" | sed 's/"/""/g' 39 | } 40 | 41 | # Function to log error to file 42 | log_error() { 43 | local ERROR_MESSAGE="$1" 44 | local CURRENT_TIME=$(date +"%Y-%m-%d %H:%M:%S") 45 | echo "[$CURRENT_TIME] ERROR: $ERROR_MESSAGE" >> "$ERROR_LOG_FILE" 46 | } 47 | 48 | # Function to delete $ERROR_LOG_FILE if it is empty 49 | delete_empty_error_log() { 50 | local LOG_FILE="$1" 51 | 52 | # Check if the log file is empty 53 | if [[ ! -s "$LOG_FILE" ]]; then 54 | # Delete the file if it's empty 55 | rm -f "$LOG_FILE" 56 | fi 57 | } 58 | 59 | api_enabled(){ 60 | 61 | local API_NAME=$1; 62 | local NOT_ENABLED=0; 63 | local ENABLED=1; 64 | 65 | if [[ $(gcloud services list --quiet --enabled --filter="NAME=$API_NAME" 2>/dev/null | grep -c $API_NAME) -ge 1 ]]; then 66 | return $NOT_ENABLED; 67 | else 68 | return $ENABLED; 69 | fi; 70 | } 71 | 72 | function get_projects() { 73 | 74 | local PROJECT_ID=$1; 75 | 76 | if [[ $PROJECT_ID == "" ]]; then 77 | # Get all projects 78 | gcloud projects list --format="flattened(PROJECT_ID)" | grep project_id | cut -d " " -f 2; 79 | else 80 | # User wants to look at a specific project 81 | echo "$PROJECT_ID"; 82 | fi; 83 | } 84 | 85 | function set_project() { 86 | local PROJECT_ID=$1; 87 | gcloud config set project $PROJECT_ID 2>/dev/null; 88 | } 89 | 90 | 91 | # Function to get project details project_name, project_application, project_owner 92 | function get_project_details() { 93 | local PROJECT_ID=$1; 94 | 95 | PROJECT_DETAILS=$(gcloud projects describe $PROJECT_ID --format="json") 96 | PROJECT_NAME=$(echo $PROJECT_DETAILS | jq -rc '.name') 97 | PROJECT_APPLICATION=$(echo $PROJECT_DETAILS | jq -rc '.labels.app // empty') 98 | PROJECT_OWNER=$(echo $PROJECT_DETAILS | jq -rc '.labels.adid // empty') 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/cis-4.8.1-shielded-vms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | HELP=$(cat << EOL 9 | $0 [-p, --project PROJECT] [-d, --debug] [-h, --help] 10 | EOL 11 | ); 12 | 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | "--help") set -- "$@" "-h" ;; 17 | "--debug") set -- "$@" "-d" ;; 18 | "--project") set -- "$@" "-p" ;; 19 | *) set -- "$@" "$arg" 20 | esac 21 | done 22 | 23 | while getopts "hdp:" option 24 | do 25 | case "${option}" 26 | in 27 | p) 28 | PROJECT_IDS=${OPTARG};; 29 | d) 30 | DEBUG="True";; 31 | h) 32 | echo $HELP; 33 | exit 0;; 34 | esac; 35 | done; 36 | 37 | if [[ $PROJECT_IDS == "" ]]; then 38 | declare PROJECT_IDS=$(get_projects); 39 | fi; 40 | 41 | for PROJECT_ID in $PROJECT_IDS; do 42 | 43 | set_project $PROJECT_ID; 44 | 45 | if ! api_enabled compute.googleapis.com; then 46 | echo "Compute Engine API is not enabled on Project $PROJECT_ID" 47 | continue 48 | fi 49 | 50 | #Get project details 51 | get_project_details $PROJECT_ID 52 | 53 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json"); 54 | 55 | if [[ $INSTANCES != "[]" ]]; then 56 | 57 | echo "---------------------------------------------------------------------------------"; 58 | echo "Instances for Project $PROJECT_ID"; 59 | echo "Project Application: $PROJECT_APPLICATION"; 60 | echo "Project Owner: $PROJECT_OWNER"; 61 | echo "---------------------------------------------------------------------------------"; 62 | 63 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE;do 64 | 65 | NAME=$(echo $INSTANCE | jq -rc '.name'); 66 | SHIELDED_INSTANCE_CONFIG=$(echo $INSTANCE | jq -rc '.shieldedInstanceConfig'); 67 | ENABLE_INTEGRITY_MONITORING=$(echo $INSTANCE | jq -rc '.shieldedInstanceConfig.enableIntegrityMonitoring' | tr '[:upper:]' '[:lower:]'); 68 | ENABLE_SECURE_BOOT=$(echo $INSTANCE | jq -rc '.shieldedInstanceConfig.enableSecureBoot' | tr '[:upper:]' '[:lower:]'); 69 | ENABLE_VTPM=$(echo $INSTANCE | jq -rc '.shieldedInstanceConfig.enableVtpm' | tr '[:upper:]' '[:lower:]'); 70 | 71 | if [[ $SHIELDED_INSTANCE_CONFIG =~ "false" ]]; then 72 | echo "Instance Name: $NAME"; 73 | echo "Shielded instance Configuration: $SHIELDED_INSTANCE_CONFIG"; 74 | if [[ $ENABLE_INTEGRITY_MONITORING == "false" ]]; then 75 | echo "VIOLATION: Integrity monitoring is not enabled"; 76 | fi; 77 | if [[ $ENABLE_SECURE_BOOT == "false" ]]; then 78 | echo "VIOLATION: Secure boot is not enabled"; 79 | fi; 80 | if [[ $ENABLE_VTPM == "false" ]]; then 81 | echo "VIOLATION: Virtual TPM is not enabled"; 82 | fi; 83 | echo $BLANK_LINE; 84 | fi; 85 | done; 86 | echo $BLANK_LINE; 87 | else 88 | echo "No instances found for Project $PROJECT_ID"; 89 | echo $BLANK_LINE; 90 | fi; 91 | sleep $SLEEP_SECONDS; 92 | done; 93 | 94 | -------------------------------------------------------------------------------- /.tools/push-development-branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Purpose: Push feature branch, merge into development branch, push development branch 3 | # Usage: ./push-development-branch.sh 4 | # Description: This script pushes the feature branch, merges it into the development branch, and 5 | # calls another script 'git.sh' with the version and annotation to push both branches. 6 | 7 | # Function to print messages with a timestamp 8 | print_message() { 9 | echo "" 10 | echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" 11 | } 12 | 13 | # Function to display help message 14 | show_help() { 15 | echo "Usage: $0 " 16 | echo "" 17 | echo "Options:" 18 | echo " -h, --help Display this help message." 19 | echo "" 20 | echo "Description:" 21 | echo "This script pushes the feature branch, merges it into the development branch," 22 | echo "and calls another script 'git.sh' with the version and annotation." 23 | exit 0 24 | } 25 | 26 | # Function to handle errors 27 | handle_error() { 28 | print_message "Error: $1" 29 | exit 1 30 | } 31 | 32 | # Parse options 33 | while [[ "$#" -gt 0 ]]; do 34 | case $1 in 35 | -h|--help) show_help ;; 36 | *) break ;; 37 | esac 38 | shift 39 | done 40 | 41 | # Check if exactly three arguments are passed 42 | if (( $# != 3 )); then 43 | handle_error "Incorrect number of arguments. Usage: $0 " 44 | fi 45 | 46 | # Assign arguments to variables 47 | FEATURE_BRANCH=$1 48 | VERSION=$2 49 | ANNOTATION=$3 50 | 51 | # Verify 'git.sh' script exists and is executable 52 | GIT_SCRIPT="./git.sh" 53 | if [[ ! -x "$GIT_SCRIPT" ]]; then 54 | handle_error "'git.sh' script not found or not executable" 55 | fi 56 | 57 | # Push feature branch 58 | print_message "Checking out feature branch: $FEATURE_BRANCH" 59 | git checkout "$FEATURE_BRANCH" || handle_error "Failed to checkout feature branch: $FEATURE_BRANCH" 60 | 61 | print_message "Pushing feature branch: $FEATURE_BRANCH" 62 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push feature branch using git.sh" 63 | 64 | # Merge feature branch into development branch 65 | print_message "Checking out development branch" 66 | git checkout development || handle_error "Failed to checkout development branch" 67 | 68 | print_message "Merging feature branch '$FEATURE_BRANCH' into 'development'" 69 | git merge "$FEATURE_BRANCH" || handle_error "Failed to merge feature branch into development branch" 70 | 71 | # Push development branch 72 | print_message "Pushing development branch" 73 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push development branch using git.sh" 74 | 75 | print_message "Checking out feature branch: $FEATURE_BRANCH" 76 | git checkout "$FEATURE_BRANCH" || handle_error "Failed to checkout feature branch: $FEATURE_BRANCH" 77 | 78 | # Show git status 79 | print_message "Git status" 80 | git status || handle_error "Failed to show git status" 81 | 82 | print_message "Script completed successfully" -------------------------------------------------------------------------------- /src/utility-list-accounts-with-privileges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | 5 | declare ROLE="owner"; 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-r,--role] [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--role") set -- "$@" "-r" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:r:" option 27 | do 28 | case "${option}" 29 | in 30 | r) 31 | ROLE=${OPTARG};; 32 | p) 33 | PROJECT_IDS=${OPTARG};; 34 | d) 35 | DEBUG="True";; 36 | c) 37 | CSV="True";; 38 | h) 39 | echo $HELP; 40 | exit 0;; 41 | esac; 42 | done; 43 | 44 | if [[ $PROJECT_IDS == "" ]]; then 45 | declare PROJECT_IDS=$(gcloud projects list --format="json"); 46 | else 47 | declare PROJECT_IDS=$(gcloud projects list --format="json" --filter="name:$PROJECT_IDS"); 48 | fi; 49 | 50 | if [[ $PROJECT_IDS != "[]" ]]; then 51 | 52 | if [[ $CSV == "True" ]]; then 53 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"ACCOUNT\", \"ACCOUNT_TYPE\", \"ENVIRONMENT\""; 54 | fi; 55 | 56 | echo $PROJECT_IDS | jq -rc '.[]' | while IFS='' read PROJECT;do 57 | 58 | PROJECT_ID=$(echo $PROJECT | jq -r '.projectId'); 59 | PROJECT_NAME=$(echo $PROJECT | jq -r '.name'); 60 | PROJECT_OWNER=$(echo $PROJECT | jq -r '.labels.adid'); 61 | PROJECT_APPLICATION=$(echo $PROJECT | jq -r '.labels.app'); 62 | MEMBERS=$(gcloud projects get-iam-policy $PROJECT_ID --format="json" | jq -r '.bindings[] | select(.role=="roles/'$ROLE'") | .members[]'); 63 | ENVIRONMENT=""; 64 | 65 | for ENV in "sandbox" "dev" "sys" "uat" "prod"; do 66 | if [[ $(grep -ic $ENV <<< $PROJECT_ID) == 1 ]]; then 67 | ENVIRONMENT=$ENV; 68 | fi; 69 | done; 70 | 71 | if [[ $MEMBERS != "" ]]; then 72 | if [[ $CSV != "True" ]]; then 73 | echo "Project ID: $PROJECT_ID"; 74 | echo "Project Name: $PROJECT_NAME"; 75 | echo "Project Owner: $PROJECT_OWNER"; 76 | echo "Project Application: $PROJECT_APPLICATION"; 77 | echo -e "Members ($ROLE role):\n$MEMBERS"; 78 | echo "Environment: $ENVIRONMENT"; 79 | echo $BLANK_LINE; 80 | else 81 | for MEMBER in $MEMBERS;do 82 | ACCOUNT_TYPE=$(echo $MEMBER | cut -d ":" -f1); 83 | ACCOUNT=$(echo $MEMBER | cut -d ":" -f2); 84 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$ACCOUNT\", \"$ACCOUNT_TYPE\", \"$ENVIRONMENT\""; 85 | done; 86 | fi; 87 | fi; 88 | sleep $SLEEP_SECONDS; 89 | done; 90 | else 91 | if [[ $CSV != "True" ]]; then 92 | echo "No projects found"; 93 | echo $BLANK_LINE; 94 | fi; 95 | fi; 96 | -------------------------------------------------------------------------------- /.tools/push-main-branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Purpose: Push feature branch, merge into development branch, push development branch, merge into main branch, push main branch 3 | # Usage: ./push-main-branch.sh 4 | # Description: This script pushes the feature branch, merges it into the development branch, pushes the development branch, merges it into the main branch, and pushes the main branch. 5 | 6 | # Function to print messages with a timestamp 7 | print_message() { 8 | echo "" 9 | echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" 10 | } 11 | 12 | # Function to display help message 13 | show_help() { 14 | echo "Usage: $0 " 15 | echo "" 16 | echo "Options:" 17 | echo " -h, --help Display this help message." 18 | echo "" 19 | echo "Description:" 20 | echo "This script pushes the feature branch, merges it into the development branch," 21 | echo "pushes the development branch, merges it into the main branch, and pushes the main branch." 22 | exit 0 23 | } 24 | 25 | # Function to handle errors 26 | handle_error() { 27 | print_message "Error: $1" 28 | exit 1 29 | } 30 | 31 | # Parse options 32 | while [[ "$#" -gt 0 ]]; do 33 | case $1 in 34 | -h|--help) show_help ;; 35 | *) break ;; 36 | esac 37 | shift 38 | done 39 | 40 | # Check if exactly three arguments are passed 41 | if (( $# != 3 )); then 42 | handle_error "Incorrect number of arguments. Usage: $0 " 43 | fi 44 | 45 | # Assign arguments to variables 46 | FEATURE_BRANCH=$1 47 | VERSION=$2 48 | ANNOTATION=$3 49 | 50 | # Verify 'git.sh' script exists and is executable 51 | GIT_SCRIPT="./git.sh" 52 | if [[ ! -x "$GIT_SCRIPT" ]]; then 53 | handle_error "'git.sh' script not found or not executable" 54 | fi 55 | 56 | # Push feature branch 57 | print_message "Checking out feature branch: $FEATURE_BRANCH" 58 | git checkout "$FEATURE_BRANCH" || handle_error "Failed to checkout feature branch: $FEATURE_BRANCH" 59 | 60 | print_message "Pushing feature branch: $FEATURE_BRANCH" 61 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push feature branch using git.sh" 62 | 63 | # Merge feature branch into development branch 64 | print_message "Checking out development branch" 65 | git checkout development || handle_error "Failed to checkout development branch" 66 | 67 | print_message "Merging feature branch '$FEATURE_BRANCH' into 'development'" 68 | git merge "$FEATURE_BRANCH" || handle_error "Failed to merge feature branch into development branch" 69 | 70 | # Push development branch 71 | print_message "Pushing development branch" 72 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push development branch using git.sh" 73 | 74 | # Merge development branch into main branch 75 | print_message "Checking out main branch" 76 | git checkout main || handle_error "Failed to checkout main branch" 77 | 78 | print_message "Merging development branch into 'main'" 79 | git merge development || handle_error "Failed to merge development branch into main branch" 80 | 81 | # Push main branch 82 | print_message "Pushing main branch" 83 | "$GIT_SCRIPT" "$VERSION" "$ANNOTATION" || handle_error "Failed to push main branch using git.sh" 84 | 85 | print_message "Checking out feature branch: $FEATURE_BRANCH" 86 | git checkout "$FEATURE_BRANCH" || handle_error "Failed to checkout feature branch: $FEATURE_BRANCH" 87 | 88 | # Show git status 89 | print_message "Git status" 90 | git status || handle_error "Failed to show git status" 91 | 92 | print_message "Script completed successfully" -------------------------------------------------------------------------------- /src/cis-3.8.2-firewall-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | 44 | fi; 45 | 46 | declare SEPARATOR="----------------------------------------------------------------------------------------"; 47 | 48 | 49 | if [[ $CSV == "True" ]]; then 50 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"FIREWALL_RULE_NAME\", \"LOG_CONFIG\", \"LOG_CONFIG_STATUS_MESSAGE\""; 51 | fi; 52 | 53 | for PROJECT_ID in $PROJECT_IDS; do 54 | 55 | set_project $PROJECT_ID; 56 | 57 | if ! api_enabled compute.googleapis.com; then 58 | if [[ $CSV != "True" ]]; then 59 | echo "Compute Engine API is not enabled on Project $PROJECT_ID"; 60 | continue; 61 | fi; 62 | fi; 63 | 64 | 65 | #Get project details 66 | get_project_details $PROJECT_ID 67 | 68 | 69 | declare RESULTS=$(gcloud compute firewall-rules list --quiet --format="json"); 70 | 71 | if [[ $RESULTS != "[]" ]]; then 72 | 73 | if [[ $CSV != "True" ]]; then 74 | echo $SEPARATOR; 75 | echo "Firewall rules for project $PROJECT_ID"; 76 | echo $BLANK_LINE; 77 | fi; 78 | 79 | #Loop through each firewall rule in the project 80 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r FIREWALL_RULE;do 81 | 82 | # Debugging output 83 | if [[ $DEBUG == "True" ]]; then 84 | echo $FIREWALL_RULE | jq '.'; 85 | fi; 86 | 87 | FIREWALL_RULE_NAME=$(echo $FIREWALL_RULE | jq -rc '.name'); 88 | LOG_CONFIG=$(echo $FIREWALL_RULE | jq -rc '.logConfig.enable // false'); 89 | 90 | # Calculate Logging Violations 91 | if [[ $LOG_CONFIG == "false" ]]; then 92 | LOG_CONFIG_STATUS_MESSAGE="VIOLATION: Firewall logging is not enabled"; 93 | else 94 | LOG_CONFIG_STATUS_MESSAGE="Firewall logging is enabled"; 95 | fi; 96 | 97 | if [[ $CSV != "True" ]]; then 98 | # Print Project Information 99 | echo "Name: $FIREWALL_RULE_NAME"; 100 | echo "Project Name: $PROJECT_NAME"; 101 | echo "Project Application: $PROJECT_APPLICATION"; 102 | echo "Project Owner: $PROJECT_OWNER"; 103 | echo "Logging: $LOG_CONFIG"; 104 | echo "Logging Status: $LOG_CONFIG_STATUS_MESSAGE"; 105 | echo $BLANK_LINE; 106 | else 107 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$FIREWALL_RULE_NAME\", \"$LOG_CONFIG\", \"$LOG_CONFIG_STATUS_MESSAGE\""; 108 | fi; 109 | done; 110 | else 111 | if [[ $CSV != "True" ]]; then 112 | echo $SEPARATOR; 113 | echo "No firewall rules found for $PROJECT_ID"; 114 | echo $BLANK_LINE; 115 | fi; 116 | fi; 117 | sleep $SLEEP_SECONDS; 118 | done; 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/cis-6.6.1-cloud-sql-public-ips.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | fi; 44 | 45 | if [[ $CSV == "True" ]]; then 46 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"DATABASE_NAME\", \"DATABASE_VERSION\", \"EXTERNAL_IP\", \"EXTERNAL_IP_EXISTS_FLAG\""; 47 | fi; 48 | 49 | for PROJECT_ID in $PROJECT_IDS; do 50 | 51 | set_project $PROJECT_ID; 52 | 53 | if ! api_enabled sqladmin.googleapis.com; then 54 | if [[ $CSV != "True" ]]; then 55 | echo "Cloud SQL API is not enabled on Project $PROJECT_ID"; 56 | echo $BLANK_LINE; 57 | fi; 58 | continue; 59 | fi; 60 | 61 | declare INSTANCES=$(gcloud sql instances list --quiet --format="json"); 62 | 63 | if [[ $DEBUG == "True" ]]; then 64 | echo "Cloud SQL Instances (json): $INSTANCES"; 65 | echo $BLANK_LINE; 66 | fi; 67 | 68 | if [[ $INSTANCES != "[]" ]]; then 69 | 70 | # Note: tr -d "\\" was added because Google returns errant backslashes in the field containing the certificate value 71 | echo $INSTANCES | jq -rc '.[]' | tr -d "\\" 2>/dev/null | while IFS='' read INSTANCE;do 72 | 73 | if [[ $DEBUG == "True" ]]; then 74 | echo "Cloud SQL Instance (json): $INSTANCE"; 75 | echo $BLANK_LINE; 76 | fi; 77 | 78 | DATABASE_NAME=$(echo $INSTANCE | jq -rc '.name'); 79 | DATABASE_VERSION=$(echo $INSTANCE | jq -rc '.databaseVersion'); 80 | EXTERNAL_IP=$(echo $INSTANCE | jq -rc '.ipAddresses[]' | jq 'select(.type == "PRIMARY")' | jq '.ipAddress'); 81 | 82 | EXTERNAL_IP_EXISTS_FLAG="False"; 83 | if [[ $EXTERNAL_IP == "" ]]; then 84 | EXTERNAL_IP="No external IP found"; 85 | else 86 | EXTERNAL_IP_EXISTS_FLAG="True"; 87 | fi; 88 | 89 | # Get project details 90 | get_project_details $PROJECT_ID 91 | 92 | if [[ $CSV != "True" ]]; then 93 | echo "Project Name: $PROJECT_NAME"; 94 | echo "Project Application: $PROJECT_APPLICATION"; 95 | echo "Project Owner: $PROJECT_OWNER"; 96 | echo "Cloud SQL Instance $DATABASE_NAME"; 97 | echo "Version: $DATABASE_VERSION"; 98 | echo "External IP Addresses: $EXTERNAL_IP"; 99 | echo $BLANK_LINE; 100 | else 101 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$DATABASE_NAME\", \"$DATABASE_VERSION\", \"$EXTERNAL_IP\", \"$EXTERNAL_IP_EXISTS_FLAG\""; 102 | fi; 103 | 104 | done; 105 | 106 | else 107 | if [[ $CSV != "True" ]]; then 108 | echo "Project $PROJECT_ID: No Cloud SQL found. Disable the Cloud SQL API until needed."; 109 | echo $BLANK_LINE; 110 | fi; 111 | fi; 112 | 113 | sleep $SLEEP_SECONDS; 114 | done; 115 | 116 | -------------------------------------------------------------------------------- /src/utility-list-cloud-run-services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc 5 | 6 | PROJECT_IDS=""; 7 | DEBUG="False"; 8 | CSV="False"; 9 | HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-d, --debug] [-c, --csv] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--project") set -- "$@" "-p" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hcdp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) PROJECT_IDS=${OPTARG} ;; 30 | d) DEBUG="True" ;; 31 | c) CSV="True" ;; 32 | h) echo $HELP; exit 0 ;; 33 | esac; 34 | done; 35 | 36 | if [[ $PROJECT_IDS == "" ]]; then 37 | declare PROJECT_IDS=$(get_projects); 38 | fi; 39 | 40 | # Print CSV header if CSV output is enabled 41 | if [[ $CSV == "True" ]]; then 42 | echo "\"PROJECT_ID\", \"SERVIC_NAME\", \"SERVICE_INGRESS_SETTING\", \"VIOLATION\""; 43 | fi 44 | 45 | for PROJECT_ID in $PROJECT_IDS; do 46 | set_project $PROJECT_ID; 47 | 48 | # Check if Cloud Run API is enabled for the project 49 | if ! api_enabled run.googleapis.com; then 50 | if [[ $CSV != "True" ]]; then 51 | echo "Cloud Run API is not enabled for Project $PROJECT_ID."; 52 | echo "" 53 | fi 54 | continue 55 | fi 56 | 57 | # Check if Compute Engine API is enabled for the project 58 | if ! api_enabled compute.googleapis.com; then 59 | if [[ $CSV != "True" ]]; then 60 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 61 | echo "" 62 | fi 63 | continue 64 | fi 65 | 66 | declare SERVICES=$(gcloud run services list --quiet --format="json"); 67 | 68 | if [[ $SERVICES != "[]" ]]; then 69 | if [[ $CSV != "True" ]]; then 70 | echo "---------------------------------------------------------------------------------"; 71 | echo "Cloud Run Services for Project $PROJECT_ID"; 72 | echo "---------------------------------------------------------------------------------"; 73 | fi 74 | 75 | echo $SERVICES | jq -rc '.[]' | while IFS='' read -r SERVICE; do 76 | NAME=$(echo $SERVICE | jq -rc '.metadata.name'); 77 | INGRESS_SETTING=$(echo $SERVICE | jq -rc '.metadata.annotations."run.googleapis.com/ingress"'); 78 | 79 | if [[ $CSV == "True" ]]; then 80 | VIOLATION="N/A"; 81 | if [[ $INGRESS_SETTING == "all" ]]; then 82 | VIOLATION="The ingress setting is configured to ALL, which allows all requests including requests directly from the internet"; 83 | fi 84 | echo "\"$PROJECT_ID\", \"$NAME\", \"$INGRESS_SETTING\", \"$VIOLATION\""; 85 | else 86 | echo "Service Name: $NAME"; 87 | echo "Service Ingress Setting: $INGRESS_SETTING"; 88 | 89 | if [[ $INGRESS_SETTING == "all" ]]; then 90 | echo "Violation: The ingress setting is configured to ALL, which allows all requests including requests directly from the internet"; 91 | fi 92 | echo $BLANK_LINE; 93 | fi 94 | 95 | done; 96 | 97 | if [[ $CSV != "True" ]]; then 98 | echo $BLANK_LINE; 99 | fi 100 | 101 | else 102 | if [[ $CSV != "True" ]]; then 103 | echo "No Cloud Run Services found for Project $PROJECT_ID"; 104 | echo $BLANK_LINE; 105 | fi 106 | fi; 107 | 108 | sleep $SLEEP_SECONDS; 109 | done; 110 | 111 | -------------------------------------------------------------------------------- /src/cis-4.3.1-project-wide-ssh-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./common-constants.inc; 4 | source ./functions.inc; 5 | 6 | function output_header() { 7 | if [[ $CSV == "True" ]]; then 8 | output_csv_header; 9 | fi; 10 | }; 11 | 12 | function output_csv_header() { 13 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"INSTANCE_NAME\", \"INSTANCE_ALLOWS_PROJECT_WIDE_SSH_KEYS_FLAG\", \"INSTANCE_STATUS\""; 14 | }; 15 | 16 | function output_instance_csv() { 17 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$INSTANCE_NAME\", \"$INSTANCE_ALLOWS_PROJECT_WIDE_SSH_KEYS_FLAG\", \"$INSTANCE_STATUS\""; 18 | }; 19 | 20 | function output_instance() { 21 | if [[ $CSV == "True" ]]; then 22 | output_instance_csv; 23 | else 24 | output_instance_text; 25 | fi; 26 | }; 27 | 28 | function output_instance_text() { 29 | echo "Project Name: $PROJECT_NAME"; 30 | echo "Project Application: $PROJECT_APPLICATION"; 31 | echo "Project Owner: $PROJECT_OWNER"; 32 | echo "Instance Name: $INSTANCE_NAME"; 33 | echo "Instance Status: $INSTANCE_STATUS"; 34 | echo $BLANK_LINE; 35 | }; 36 | 37 | function parse_instance() { 38 | local l_INSTANCE=$1; 39 | 40 | INSTANCE_NAME=$(echo $l_INSTANCE | jq -rc '.name'); 41 | BLOCK_PROJECT_WIDE_SSH_KEYS=$(echo $INSTANCE | jq -rc '.metadata.items[] | select(.key=="block-project-ssh-keys")' | jq -rc '.value' ); 42 | 43 | INSTANCE_STATUS="INFO: Instance does not allow Project-wide SSH keys"; 44 | INSTANCE_ALLOWS_PROJECT_WIDE_SSH_KEYS_FLAG="False"; 45 | if [[ $BLOCK_PROJECT_WIDE_SSH_KEYS != "true" ]]; then 46 | INSTANCE_STATUS="VIOLATION: Instance allows Project-wide SSH keys"; 47 | INSTANCE_ALLOWS_PROJECT_WIDE_SSH_KEYS_FLAG="True"; 48 | fi; 49 | }; 50 | 51 | source ./standard-menu.inc; 52 | 53 | if [[ $PROJECT_ID == "" ]]; then 54 | declare PROJECTS=$(gcloud projects list --format="json"); 55 | else 56 | declare PROJECTS=$(gcloud projects list --format="json" --filter="name:$PROJECT_ID"); 57 | fi; 58 | 59 | if [[ $DEBUG == "True" ]]; then 60 | echo "Projects: $PROJECTS"; 61 | echo $BLANK_LINE; 62 | fi; 63 | 64 | if [[ $PROJECTS != "[]" ]]; then 65 | 66 | output_header; 67 | 68 | echo $PROJECTS | jq -rc '.[]' | while IFS='' read PROJECT;do 69 | 70 | 71 | PROJECT_ID=$(echo $PROJECT | jq -r '.projectId'); 72 | 73 | set_project $PROJECT_ID; 74 | 75 | if ! api_enabled compute.googleapis.com; then 76 | if [[ $CSV != "True" ]]; then 77 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 78 | fi; 79 | continue; 80 | fi; 81 | 82 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json(name, metadata.items)"); 83 | 84 | if [[ $DEBUG == "True" ]]; then 85 | echo "Instances (JSON): $INSTANCES"; 86 | echo $BLANK_LINE; 87 | fi; 88 | 89 | if [[ $INSTANCES != "[]" ]]; then 90 | 91 | #Get project details 92 | get_project_details $PROJECT_ID; 93 | 94 | # "read -r" ensures the backslashes in the JSON are not consumed by "read()" 95 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE;do 96 | 97 | if [[ $DEBUG == "True" ]]; then 98 | echo "Instance (JSON): $INSTANCE"; 99 | echo $BLANK_LINE; 100 | fi; 101 | 102 | parse_instance "$INSTANCE"; 103 | output_instance; 104 | done; 105 | else 106 | if [[ $CSV == "False" ]]; then 107 | echo "No instances found for project $PROJECT_ID"; 108 | echo $BLANK_LINE; 109 | fi; 110 | fi; 111 | 112 | sleep $SLEEP_SECONDS; 113 | done; 114 | else 115 | if [[ $CSV == "False" ]]; then 116 | echo "No projects found"; 117 | echo $BLANK_LINE; 118 | fi; 119 | fi; 120 | 121 | -------------------------------------------------------------------------------- /src/utility-list-projects-pam-enabled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ============================================================================= 4 | # Script Name: check-pam-api.sh 5 | # Description: 6 | # This script lists GCP projects and checks if the 7 | # privilegedaccessmanager.googleapis.com API is enabled in each respective project. 8 | # 9 | # Usage: 10 | # ./check-pam-api.sh [-c, --csv] [-d, --debug] [-h, --help] 11 | # 12 | # Arguments: 13 | # -c, --csv Output results in CSV format. 14 | # -d, --debug Enable debug mode to display API check output. 15 | # -h, --help Display help information. 16 | # 17 | # Examples: 18 | # 1. List all projects in CSV format: 19 | # ./check-pam-api.sh --csv 20 | # 21 | # 2. Enable debug mode: 22 | # ./check-pam-api.sh --debug 23 | # ============================================================================= 24 | 25 | source common-constants.inc 26 | source functions.inc 27 | 28 | declare RESULTS=$(gcloud projects list --format="json") 29 | declare DEBUG="False" 30 | declare CSV="False" 31 | declare HELP=$(cat << EOL 32 | Usage: $0 [-c, --csv] [-d, --debug] [-h, --help] 33 | -c, --csv Output results in CSV format. 34 | -d, --debug Enable debug mode to display API check output. 35 | -h, --help Display help information. 36 | 37 | Examples: 38 | 1. List all projects in CSV format: 39 | $0 --csv 40 | 41 | 2. Enable debug mode: 42 | $0 --debug 43 | EOL 44 | ) 45 | 46 | # Parse long options and convert them to short options for compatibility 47 | for arg in "$@"; do 48 | shift 49 | case "$arg" in 50 | "--help") set -- "$@" "-h" ;; 51 | "--debug") set -- "$@" "-d" ;; 52 | "--csv") set -- "$@" "-c" ;; 53 | *) set -- "$@" "$arg" ;; 54 | esac 55 | done 56 | 57 | # Parse short options 58 | while getopts "hdc" option; do 59 | case "${option}" in 60 | d) DEBUG="True" ;; 61 | c) CSV="True" ;; 62 | h) 63 | echo "$HELP" 64 | exit 0 65 | ;; 66 | esac 67 | done 68 | 69 | # Check if there are results 70 | if [[ $RESULTS != "[]" ]]; then 71 | # Print CSV headers if CSV option is enabled 72 | if [[ $CSV == "True" ]]; then 73 | echo "\"PROJECT_ID\",\"PROJECT_NAME\",\"PAM_API_ENABLED\"" 74 | fi 75 | 76 | # Iterate through each project 77 | echo "$RESULTS" | jq -rc '.[]' | while IFS='' read -r PROJECT; do 78 | PROJECT_ID=$(echo "$PROJECT" | jq -rc '.projectId') 79 | PROJECT_NAME=$(echo "$PROJECT" | jq -rc '.name') 80 | 81 | # Check if the privileged access manager API is enabled 82 | if gcloud services list --project="$PROJECT_ID" --format="value(config.name)" 2>/dev/null | \ 83 | grep -q "^privilegedaccessmanager.googleapis.com$"; then 84 | PAM_API_ENABLED="true" 85 | else 86 | PAM_API_ENABLED="false" 87 | fi 88 | 89 | # Debug output if enabled 90 | if [[ $DEBUG == "True" ]]; then 91 | echo "[DEBUG] Project: $PROJECT_NAME ($PROJECT_ID), PAM API Enabled: $PAM_API_ENABLED" 92 | fi 93 | 94 | # Output results 95 | if [[ $CSV == "True" ]]; then 96 | echo "\"$PROJECT_ID\",\"$PROJECT_NAME\",\"$PAM_API_ENABLED\"" 97 | else 98 | echo "Project ID: $PROJECT_ID" 99 | echo "Project Name: $PROJECT_NAME" 100 | echo "PAM API Enabled: $PAM_API_ENABLED" 101 | echo "$BLANK_LINE" 102 | fi 103 | sleep "$SLEEP_SECONDS" 104 | done 105 | else 106 | echo "No projects found" 107 | echo "$BLANK_LINE" 108 | fi 109 | -------------------------------------------------------------------------------- /src/cis-5.1.1-publicly-accessible-cloud-storage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | fi; 44 | 45 | if [[ $CSV == "True" ]]; then 46 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"BUCKET_NAME\", \"MEMBERS\", \"ROLE\", \"ALL_USERS_MESSAGE\", \"ALL_AUTHENTICATED_USERS_MESSAGE\""; 47 | fi; 48 | 49 | for PROJECT_ID in $PROJECT_IDS; do 50 | 51 | set_project $PROJECT_ID; 52 | 53 | if ! api_enabled storage.googleapis.com; then 54 | if [[ $CSV != "True" ]]; then 55 | echo "Storage API is not enabled on Project $PROJECT_ID"; 56 | echo $BLANK_LINE; 57 | fi; 58 | continue; 59 | fi; 60 | 61 | declare BUCKET_NAMES=$(gsutil ls); 62 | 63 | if [[ $DEBUG == "True" ]]; then 64 | echo "Buckets: $BUCKET_NAMES"; 65 | echo $BLANK_LINE; 66 | fi; 67 | 68 | if [[ $BUCKET_NAMES != "" ]]; then 69 | 70 | #Get project details 71 | get_project_details $PROJECT_ID 72 | 73 | for BUCKET_NAME in $BUCKET_NAMES; do 74 | 75 | declare PERMISSIONS=$(gsutil iam get $BUCKET_NAME); 76 | 77 | if [[ $DEBUG == "True" ]]; then 78 | echo "Permissions (JSON): $PERMISSIONS"; 79 | fi; 80 | 81 | echo $PERMISSIONS | jq -r -c '.bindings[]' | while IFS='' read -r PERMISSION;do 82 | 83 | MEMBERS=$(echo $PERMISSION | jq -rc '.members[]'); 84 | ROLE=$(echo $PERMISSION | jq '.role'); 85 | 86 | if [[ $ROLE =~ "allUsers" ]]; then 87 | ALL_USERS_MESSAGE="VIOLATION: Bucket publicly exposed to allUsers"; 88 | else 89 | ALL_USERS_MESSAGE="OK: The allUsers group does not have permission to the bucket"; 90 | fi; 91 | 92 | if [[ $ROLE =~ "allAuthenticatedUsers" ]]; then 93 | ALL_AUTHENTICATED_USERS_MESSAGE="VIOLATION: Bucket publicly exposed to allAuthenticatedUsers"; 94 | else 95 | ALL_AUTHENTICATED_USERS_MESSAGE="OK: The allAuthenticatedUsers group does not have permission to the bucket"; 96 | fi; 97 | 98 | if [[ $CSV != "True" ]]; then 99 | echo "Project ID: $PROJECT_ID"; 100 | echo "Project Name: $PROJECT_NAME"; 101 | echo "Project Application: $PROJECT_APPLICATION"; 102 | echo "Project Owner: $PROJECT_OWNER"; 103 | echo "Bucket Name: $BUCKET_NAME"; 104 | echo "Members: $MEMBERS"; 105 | echo "Role: $ROLE"; 106 | echo "$ALL_USERS_MESSAGE"; 107 | echo "$ALL_AUTHENTICATED_USERS_MESSAGE"; 108 | echo $BLANK_LINE; 109 | else 110 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$BUCKET_NAME\", \"$MEMBERS\", \"$ROLE\", \"$ALL_USERS_MESSAGE\", \"$ALL_AUTHENTICATED_USERS_MESSAGE\""; 111 | fi; 112 | done; 113 | done; 114 | echo $BLANK_LINE; 115 | else 116 | if [[ $CSV != "True" ]]; then 117 | echo "No storage buckets found for Project $PROJECT_ID"; 118 | echo $BLANK_LINE; 119 | fi; 120 | fi; 121 | sleep $SLEEP_SECONDS; 122 | done; 123 | 124 | -------------------------------------------------------------------------------- /src/cis-3.8.3-cloud-nat-service-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcip:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | fi; 44 | 45 | declare SEPARATOR="----------------------------------------------------------------------------------------"; 46 | 47 | if [[ $CSV == "True" ]]; then 48 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"CLOUD_NAT_NAME\", \"CLOUD_NAT_ROUTER\", \"LOG_CONFIG\", \"LOG_CONFIG_STATUS_MESSAGE\""; 49 | fi; 50 | 51 | for PROJECT_ID in $PROJECT_IDS; do 52 | set_project $PROJECT_ID; 53 | 54 | if ! api_enabled compute.googleapis.com; then 55 | echo "Compute Engine API is not enabled on Project $PROJECT_ID" 56 | continue 57 | fi 58 | 59 | 60 | #Get project details 61 | get_project_details $PROJECT_ID 62 | 63 | declare RESULTS=$(gcloud compute routers list --project $PROJECT_ID --format="json"); 64 | 65 | if [[ $RESULTS != "[]" ]]; then 66 | if [[ $CSV != "True" ]]; then 67 | echo $SEPARATOR; 68 | echo "Cloud NAT services for project $PROJECT_ID"; 69 | echo $BLANK_LINE; 70 | fi; 71 | 72 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r ROUTER;do 73 | ROUTER_NAME=$(echo $ROUTER | jq -rc '.name'); 74 | ROUTER_REGION=$(echo $ROUTER | jq -rc '.region'); 75 | 76 | # Debugging output for router 77 | if [[ $DEBUG == "True" ]]; then 78 | echo "Router details:" 79 | echo $ROUTER | jq '.'; 80 | fi; 81 | 82 | NATS=$(gcloud compute routers nats list --router=$ROUTER_NAME --router-region=$ROUTER_REGION --project=$PROJECT_ID --format="json"); 83 | echo $NATS | jq -r -c '.[]' | while IFS='' read -r NAT; do 84 | NAT_NAME=$(echo $NAT | jq -rc '.name'); 85 | LOG_CONFIG=$(echo $NAT | jq -rc '.logConfig.enable // false'); 86 | 87 | # Debugging output for Cloud NAT 88 | if [[ $DEBUG == "True" ]]; then 89 | echo "Cloud NAT details:" 90 | echo $NAT | jq '.'; 91 | fi; 92 | 93 | 94 | if [[ $LOG_CONFIG == "false" ]]; then 95 | LOG_CONFIG_STATUS_MESSAGE="VIOLATION:Cloud NAT logging is not enabled"; 96 | else 97 | LOG_CONFIG_STATUS_MESSAGE="Cloud NAT logging is enabled"; 98 | fi; 99 | 100 | if [[ $CSV != "True" ]]; then 101 | echo "Cloud NAT Name: $NAT_NAME"; 102 | echo "Cloud NAT Router: $ROUTER_NAME"; 103 | echo "Project Name: $PROJECT_NAME"; 104 | echo "Project Application: $PROJECT_APPLICATION"; 105 | echo "Project Owner: $PROJECT_OWNER"; 106 | echo "Logging Enabled: $LOG_CONFIG"; 107 | echo "Logging Status: $LOG_CONFIG_STATUS_MESSAGE"; 108 | echo $BLANK_LINE; 109 | else 110 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\",\"$PROJECT_OWNER\", \"$NAT_NAME\", \"$ROUTER_NAME\", \"$LOG_CONFIG\", \"$LOG_CONFIG_STATUS_MESSAGE\""; 111 | fi; 112 | done; 113 | done; 114 | else 115 | if [[ $CSV != "True" ]]; then 116 | echo $SEPARATOR; 117 | echo "No Cloud NAT services for project $PROJECT_ID"; 118 | echo $BLANK_LINE; 119 | fi; 120 | fi; 121 | sleep $SLEEP_SECONDS; 122 | done; 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/utility-list-gke-clusters.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | 43 | if [[ $PROJECT_IDS == "" ]]; then 44 | declare PROJECT_IDS=$(get_projects); 45 | fi; 46 | 47 | if [[ $DEBUG == "True" ]]; then 48 | echo "Projects: $PROJECT_IDS"; 49 | fi; 50 | 51 | if [[ $CSV == "True" ]]; then 52 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"CLUSTER_NAME\", \"CLUSTER_NODE_VERSION\", \"BINARY_AUTHORIZATION_MODE\", \"DATABASE_ENCRYPTION_MODE\", \"PRIVATE_CLUSTER_MODE\""; 53 | fi; 54 | 55 | for PROJECT_ID in $PROJECT_IDS; do 56 | 57 | set_project $PROJECT_ID; 58 | 59 | if ! api_enabled container.googleapis.com; then 60 | if [[ $CSV != "True" ]]; then 61 | echo "GKE Cluster API is not enabled on Project $PROJECT_ID"; 62 | continue; 63 | fi; 64 | fi; 65 | 66 | declare CLUSTERS=$(gcloud container clusters list --quiet --format="json"); 67 | 68 | if [[ $DEBUG == "True" ]]; then 69 | echo "GKE Clusters (JSON): $CLUSTERS"; 70 | fi; 71 | 72 | if [[ $CSV != "True" ]]; then 73 | echo $SEPARATOR; 74 | echo "GKE Clusters for Project $PROJECT_ID"; 75 | echo $SEPARATOR; 76 | echo $BLANK_LINE; 77 | fi; 78 | 79 | if [[ $CLUSTERS != "[]" ]]; then 80 | 81 | #Get project details 82 | get_project_details $PROJECT_ID 83 | 84 | echo $CLUSTERS | jq -rc '.[]' | while IFS='' read -r CLUSTER;do 85 | 86 | CLUSTER_NAME=$(echo $CLUSTER | jq -rc '.name'); 87 | CLUSTER_CONTROLLER_VERSION=$(echo $CLUSTER | jq -rc '.currentMasterVersion'); 88 | CLUSTER_NODE_VERSION=$(echo $CLUSTER | jq -rc '.currentNodeVersion'); 89 | NODE_COUNT=$(echo $CLUSTER | jq -rc '.currentNodeCount'); 90 | BINARY_AUTHORIZATION_MODE=$(echo $CLUSTER | jq -rc '.binaryAuthorization.evaluationMode // empty'); 91 | DATABASE_ENCRYPTION_MODE=$(echo $CLUSTER | jq -rc '.databaseEncryption.state // empty'); 92 | PRIVATE_CLUSTER_MODE=$(echo $CLUSTER | jq -rc '.privateClusterConfig.enablePrivateNodes // empty'); 93 | 94 | # Print the results gathered above 95 | if [[ $CSV != "True" ]]; then 96 | echo "Project ID: $PROJECT_ID"; 97 | echo "Project Name: $PROJECT_NAME"; 98 | echo "Project Application: $PROJECT_APPLICATION"; 99 | echo "Project Owner: $PROJECT_OWNER"; 100 | echo "GKE Cluster Name: $CLUSTER_NAME"; 101 | echo "GKE Cluster Node Version: $CLUSTER_NODE_VERSION"; 102 | echo "GKE Cluster Binary Authorization Status: $BINARY_AUTHORIZATION_MODE"; 103 | echo "GKE Cluster Database Encryption Mode: $DATABASE_ENCRYPTION_MODE"; 104 | echo "GKE Cluster Private Cluster Mode: $PRIVATE_CLUSTER_MODE"; 105 | echo $BLANK_LINE; 106 | else 107 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$CLUSTER_NAME\", \"$CLUSTER_NODE_VERSION\", \"$BINARY_AUTHORIZATION_MODE\", \"$DATABASE_ENCRYPTION_MODE\", \"$PRIVATE_CLUSTER_MODE\""; 108 | fi; 109 | 110 | done; 111 | else 112 | if [[ $CSV != "True" ]]; then 113 | echo "No GKE Clusters found for project $PROJECT_ID"; 114 | echo $BLANK_LINE; 115 | fi; 116 | fi; 117 | sleep $SLEEP_SECONDS; 118 | done; 119 | 120 | -------------------------------------------------------------------------------- /src/cis-4.6.1-ip-forwarding-enabled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------" 7 | declare PROJECT_IDS="" 8 | declare DEBUG="False" 9 | declare CSV="False" 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ) 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcip:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo "$HELP" 38 | exit 0;; 39 | esac 40 | done 41 | 42 | 43 | if [[ $PROJECT_IDS == "" ]]; then 44 | declare PROJECT_IDS=$(get_projects) 45 | fi 46 | 47 | if [[ $DEBUG == "True" ]]; then 48 | echo "Projects: $PROJECT_IDS" 49 | echo "" 50 | fi 51 | 52 | if [[ $CSV == "True" ]]; then 53 | echo "\"PROJECT_ID\",\"PROJECT_NAME\",\"PROJECT_OWNER\",\"PROJECT_APPLICATION\",\"INSTANCE_NAME\",\"IP_FORWARDING_ENABLED\",\"IP_FORWARDING_STATUS_MESSAGE\"" 54 | fi 55 | 56 | for PROJECT_ID in $PROJECT_IDS; do 57 | 58 | set_project $PROJECT_ID 59 | 60 | if ! api_enabled compute.googleapis.com; then 61 | if [[ $CSV != "True" ]]; then 62 | echo "Compute Engine API is not enabled on Project $PROJECT_ID" 63 | echo "" 64 | fi 65 | continue 66 | fi 67 | 68 | #Get project details 69 | get_project_details $PROJECT_ID 70 | 71 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json") 72 | 73 | if [[ $DEBUG == "True" ]]; then 74 | echo "Instances (JSON): $INSTANCES" 75 | echo "" 76 | fi 77 | 78 | if [[ $INSTANCES != "[]" ]]; then 79 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE; do 80 | if [[ $DEBUG == "True" ]]; then 81 | echo "Instance (JSON): $INSTANCE" 82 | echo "" 83 | fi 84 | 85 | INSTANCE_NAME=$(echo $INSTANCE | jq -rc '.name') 86 | IP_FORWARDING_ENABLED=$(echo $INSTANCE | jq -rc '.canIpForward' | tr '[:upper:]' '[:lower:]') 87 | 88 | if [[ $IP_FORWARDING_ENABLED == "true" ]]; then 89 | IP_FORWARDING_STATUS_MESSAGE="VIOLATION: IP forwarding enabled" 90 | 91 | elif [[ -z $IP_FORWARDING_ENABLED || $IP_FORWARDING_ENABLED == "null" ]]; then 92 | IP_FORWARDING_ENABLED="IP Forwarding is NOT explicitly configured" 93 | IP_FORWARDING_STATUS_MESSAGE="N/A" 94 | else 95 | IP_FORWARDING_STATUS_MESSAGE="IP forwarding disabled" 96 | fi 97 | 98 | # Print the results gathered above 99 | if [[ $CSV != "True" ]]; then 100 | echo "Project ID: $PROJECT_ID" 101 | echo "Project Name: $PROJECT_NAME" 102 | echo "Project Application: $PROJECT_APPLICATION" 103 | echo "Project Owner: $PROJECT_OWNER" 104 | echo "Instance Name: $INSTANCE_NAME" 105 | echo "IP Forwarding Enabled: $IP_FORWARDING_ENABLED" 106 | echo "IP Forwarding Status: $IP_FORWARDING_STATUS_MESSAGE" 107 | echo "" 108 | else 109 | echo "\"$PROJECT_ID\",\"$PROJECT_NAME\",\"$PROJECT_OWNER\",\"$PROJECT_APPLICATION\",\"$INSTANCE_NAME\",\"$IP_FORWARDING_ENABLED\",\"$IP_FORWARDING_STATUS_MESSAGE\"" 110 | fi 111 | done 112 | echo "" 113 | else 114 | if [[ $CSV != "True" ]]; then 115 | echo "No instances found for Project $PROJECT_ID" 116 | echo "" 117 | fi 118 | fi 119 | sleep $SLEEP_SECONDS; 120 | done 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/cis-4.9.3-gke-cluster-public-ips.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | # Reference: 7 | # https://cloud.google.com/sdk/gcloud/reference/container/clusters/list 8 | 9 | function output_header() { 10 | if [[ $CSV == "True" ]]; then 11 | output_csv_header; 12 | fi; 13 | }; 14 | 15 | function output_csv_header() { 16 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"CLUSTER_NAME\", \"CLUSTER_STATUS\", \"CLUSTER_NETWORK\", \"CLUSTER_SUBNETWORK\", \"CLUSTER_AUTHORIZED_NETWORKS\", \"CLUSTER_PUBLIC_ENDPOINT\", \"CLUSTER_PUBLIC_ENDPOINT_FLAG\""; 17 | }; 18 | 19 | function output_gke_cluster_csv() { 20 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$CLUSTER_NAME\", \"$CLUSTER_STATUS\", \"$CLUSTER_NETWORK\", \"$CLUSTER_SUBNETWORK\", \"$CLUSTER_AUTHORIZED_NETWORKS\", \"$CLUSTER_PUBLIC_ENDPOINT\", \"$CLUSTER_PUBLIC_ENDPOINT_FLAG\""; 21 | }; 22 | 23 | function output_gke_cluster() { 24 | if [[ $CSV == "True" ]]; then 25 | output_gke_cluster_csv; 26 | else 27 | output_gke_cluster_text; 28 | fi; 29 | }; 30 | 31 | function output_gke_cluster_text() { 32 | echo "Project Name: $PROJECT_NAME"; 33 | echo "Project Application: $PROJECT_APPLICATION"; 34 | echo "Project Owner: $PROJECT_OWNER"; 35 | echo "Cluster Name: $CLUSTER_NAME"; 36 | echo "Cluster Status: $CLUSTER_STATUS"; 37 | echo "Cluster Network: $CLUSTER_NETWORK"; 38 | echo "Cluster Subnetwork: $CLUSTER_SUBNETWORK"; 39 | echo "Cluster Autorized Networks: $CLUSTER_AUTHORIZED_NETWORKS"; 40 | echo "Cluster Control Plane External IP Address: $CLUSTER_PUBLIC_ENDPOINT"; 41 | echo $BLANK_LINE; 42 | }; 43 | 44 | function parse_gke_cluster() { 45 | local l_CLUSTER=$1; 46 | 47 | CLUSTER_NAME=$(echo $l_CLUSTER | jq -rc '.name'); 48 | CLUSTER_STATUS=$(echo $l_CLUSTER | jq -rc '.status'); 49 | CLUSTER_SUBNETWORK=$(echo $l_CLUSTER | jq -rc '.subnetwork'); 50 | CLUSTER_NETWORK=$(echo $l_CLUSTER | jq -rc '.network'); 51 | CLUSTER_PUBLIC_ENDPOINT=$(echo $l_CLUSTER | jq -rc '.privateClusterConfig.publicEndpoint // empty'); 52 | CLUSTER_AUTHORIZED_NETWORKS=$(echo $l_CLUSTER | jq -rc 'if (.masterAuthorizedNetworksConfig.cidrBlocks | length) > 0 then .masterAuthorizedNetworksConfig.cidrBlocks | map(.cidrBlock) | join(" ") else empty end'); 53 | 54 | CLUSTER_PUBLIC_ENDPOINT_FLAG="False"; 55 | if [[ $CLUSTER_PUBLIC_ENDPOINT != "" ]]; then 56 | CLUSTER_PUBLIC_ENDPOINT_FLAG="True"; 57 | fi; 58 | }; 59 | 60 | source ./standard-menu.inc; 61 | 62 | if [[ $PROJECT_ID == "" ]]; then 63 | declare PROJECTS=$(gcloud projects list --format="json"); 64 | else 65 | declare PROJECTS=$(gcloud projects list --format="json" --filter="name:$PROJECT_ID"); 66 | fi; 67 | 68 | if [[ $DEBUG == "True" ]]; then 69 | echo "Projects: $PROJECTS"; 70 | echo $BLANK_LINE; 71 | fi; 72 | 73 | if [[ $PROJECTS != "[]" ]]; then 74 | 75 | output_header; 76 | 77 | echo $PROJECTS | jq -rc '.[]' | while IFS='' read PROJECT;do 78 | 79 | PROJECT_ID=$(echo $PROJECT | jq -r '.projectId'); 80 | 81 | set_project $PROJECT_ID; 82 | 83 | if ! api_enabled container.googleapis.com; then 84 | if [[ $CSV != "True" ]]; then 85 | echo "Container API is not enabled for Project $PROJECT_ID."; 86 | fi; 87 | continue; 88 | fi; 89 | 90 | declare CLUSTERS=$(gcloud container clusters list --quiet --format="json" 2>/dev/null); 91 | 92 | if [[ $DEBUG == "True" ]]; then 93 | echo "Clusters (JSON): $CLUSTERS"; 94 | echo $BLANK_LINE; 95 | fi; 96 | 97 | if [[ $CLUSTERS != "[]" ]]; then 98 | 99 | #Get project details 100 | get_project_details $PROJECT_ID; 101 | 102 | echo $CLUSTERS | jq -rc '.[]' | while IFS='' read CLUSTER;do 103 | 104 | if [[ $DEBUG == "True" ]]; then 105 | echo "Cluster (JSON): $CLUSTER"; 106 | echo $BLANK_LINE; 107 | fi; 108 | 109 | parse_gke_cluster "$CLUSTER"; 110 | output_gke_cluster; 111 | done; 112 | else 113 | if [[ $CSV == "False" ]]; then 114 | echo "No clusters found"; 115 | echo $BLANK_LINE; 116 | fi; 117 | fi; 118 | 119 | sleep $SLEEP_SECONDS; 120 | done; 121 | else 122 | if [[ $CSV == "False" ]]; then 123 | echo "No projects found"; 124 | echo $BLANK_LINE; 125 | fi; 126 | fi; 127 | 128 | -------------------------------------------------------------------------------- /src/cis-2.3.1-project-cloud-logging-buckets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | declare PROJECT_IDS=$(get_projects); 44 | fi; 45 | 46 | if [[ $DEBUG == "True" ]]; then 47 | echo "Projects: $PROJECT_IDS"; 48 | fi; 49 | 50 | if [[ $CSV == "True" ]]; then 51 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"BUCKET_NAME_SHORT\", \"BUCKET_NAME_LOCATION\", \"BUCKET_LIFECYCLE_STATE\", \"BUCKET_RETENTION_DAYS\", \"BUCKET_DESCRIPTION\", \"BUCKET_RETENTION_WARNING\""; 52 | fi; 53 | 54 | for PROJECT_ID in $PROJECT_IDS; do 55 | 56 | set_project $PROJECT_ID; 57 | 58 | if ! api_enabled logging.googleapis.com; then 59 | if [[ $CSV != "True" ]]; then 60 | echo "WARNING: Logging API is not enabled"; 61 | fi; 62 | continue; 63 | fi; 64 | 65 | declare BUCKETS=$(gcloud logging buckets list --format json --project="$PROJECT_ID"); 66 | 67 | if [[ $DEBUG == "True" ]]; then 68 | echo "Buckets (JSON): $BUCKETS"; 69 | fi; 70 | 71 | if [[ $CSV != "True" ]]; then 72 | echo $SEPARATOR; 73 | echo "Log buckets for Project $PROJECT_ID"; 74 | echo $SEPARATOR; 75 | fi; 76 | 77 | if [[ $BUCKETS != "[]" ]]; then 78 | 79 | #Get project details 80 | get_project_details $PROJECT_ID 81 | 82 | echo $BUCKETS | jq -rc '.[]' | while IFS='' read -r BUCKET;do 83 | 84 | if [[ $DEBUG == "True" ]]; then 85 | echo "Project Name: $PROJECT_NAME"; 86 | echo "Project Application: $PROJECT_APPLICATION"; 87 | echo "Project Owner: $PROJECT_OWNER"; 88 | echo "Log Bucket (JSON): $BUCKET"; 89 | echo $BLANK_LINE; 90 | fi; 91 | 92 | BUCKET_NAME=$(echo $BUCKET | jq -rc '.name'); 93 | BUCKET_NAME_SHORT="$(echo $BUCKET_NAME | cut -d/ -f6)"; 94 | BUCKET_LOCATION="$(echo $BUCKET_NAME | cut -d/ -f4)"; 95 | BUCKET_DESCRIPTION=$(echo $BUCKET | jq -rc '.description'); 96 | BUCKET_LIFECYCLE_STATE=$(echo $BUCKET | jq -rc '.lifecycleState'); 97 | BUCKET_RETENTION_DAYS=$(echo $BUCKET | jq -rc '.retentionDays'); 98 | BUCKET_RETENTION_WARNING=""; 99 | 100 | if [[ $BUCKET_NAME_SHORT == "_Default" && $BUCKET_RETENTION_DAYS > 30 ]]; then 101 | BUCKET_RETENTION_WARNING="WARNING: _Default bucket retention policy exceeds 30 days. Costs may increase."; 102 | fi; 103 | 104 | # Print the results gathered above 105 | if [[ $CSV != "True" ]]; then 106 | echo "Project ID: $PROJECT_ID"; 107 | echo "Project Name: $PROJECT_NAME"; 108 | echo "Project Application: $PROJECT_APPLICATION"; 109 | echo "Project Owner: $PROJECT_OWNER"; 110 | echo "Log Bucket Name: $BUCKET_NAME_SHORT"; 111 | echo "Log Bucket Location: $BUCKET_LOCATION"; 112 | echo "Log Bucket Lifecycle State: $BUCKET_LIFECYCLE_STATE"; 113 | echo "Log Bucket Retention Days: $BUCKET_RETENTION_DAYS"; 114 | echo "Log Bucket Description: $BUCKET_DESCRIPTION"; 115 | echo "Log Bucket Retention Information: $BUCKET_RETENTION_WARNING"; 116 | echo $BLANK_LINE; 117 | else 118 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$BUCKET_NAME_SHORT\", \"$BUCKET_NAME_LOCATION\", \"$BUCKET_LIFECYCLE_STATE\", \"$BUCKET_RETENTION_DAYS\", \"$BUCKET_DESCRIPTION\", \"$BUCKET_RETENTION_WARNING\""; 119 | fi; 120 | 121 | done; 122 | 123 | else 124 | if [[ $CSV != "True" ]]; then 125 | echo "No log buckets found for project $PROJECT_ID"; 126 | echo $BLANK_LINE; 127 | fi; 128 | fi; 129 | sleep $SLEEP_SECONDS; 130 | done; 131 | 132 | -------------------------------------------------------------------------------- /src/cis-4.1.2-app-engine-default-service-account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [--csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | declare PROJECT_IDS=$(get_projects); 44 | fi; 45 | 46 | if [[ $DEBUG == "True" ]]; then 47 | echo "Projects: $PROJECT_IDS"; 48 | echo $BLANK_LINE; 49 | fi; 50 | 51 | for PROJECT_ID in $PROJECT_IDS; do 52 | 53 | set_project $PROJECT_ID; 54 | 55 | if ! api_enabled appengine.googleapis.com; then 56 | if [[ $CSV != "True" ]]; then 57 | echo "App Engine API is not enabled on Project $PROJECT_ID"; 58 | fi; 59 | continue; 60 | fi; 61 | 62 | declare APPLICATION=$(gcloud app describe --quiet --format="json" 2>/dev/null); 63 | 64 | if [[ $DEBUG == "True" ]]; then 65 | echo "Application (JSON): $APPLICATION"; 66 | echo $BLANK_LINE; 67 | fi; 68 | 69 | if [[ $APPLICATION != "" ]]; then 70 | 71 | #Get project details 72 | get_project_details $PROJECT_ID 73 | 74 | if [[ $DEBUG == "True" ]]; then 75 | echo "Project Name: $PROJECT_NAME"; 76 | echo "Project Application: $PROJECT_APPLICATION"; 77 | echo "Project Owner: $PROJECT_OWNER"; 78 | echo "Application (JSON): $APPLICATION"; 79 | fi; 80 | 81 | APPLICATION_NAME=$(echo $APPLICATION | jq -rc '.name'); 82 | SERVICE_ACCOUNT=$(echo $APPLICATION | jq -rc '.serviceAccount'); 83 | CODE_BUCKET=$(echo $APPLICATION | jq -rc '.codeBucket'); 84 | DEFAULT_BUCKET=$(echo $APPLICATION | jq -rc '.defaultBucket'); 85 | DEFAULT_HOSTNAME=$(echo $APPLICATION | jq -rc '.defaultHostname'); 86 | SERVING_STATUS=$(echo $APPLICATION | jq -rc '.servingStatus'); 87 | 88 | ROLES=$(gcloud projects get-iam-policy $PROJECT_ID --flatten="bindings[].members" --filter="bindings.members:$SERVICE_ACCOUNT" --format="json" | jq '.[]' | jq '.bindings.role'); 89 | 90 | # Determine if the default service account is in use 91 | if [[ $SERVICE_ACCOUNT == "$PROJECT_ID@appspot.gserviceaccount.com" ]]; then 92 | IS_SA_VIOLATION="True"; 93 | SERVICE_ACCOUNT_STATUS_MESSAGE="VIOLATION: Default Service Account detected"; 94 | else 95 | IS_SA_VIOLATION="False"; 96 | SERVICE_ACCOUNT_STATUS_MESSAGE="NOTICE: Custom Service Account Detected"; 97 | fi; 98 | 99 | if [[ $ROLES =~ editor ]]; then 100 | IS_ROLE_VIOLATION="True"; 101 | ROLES_STATUS_MESSAGE="VIOLATION: Service account has Editor permission"; 102 | else 103 | IS_ROLE_VIOLATION="False"; 104 | ROLES_STATUS_MESSAGE="NOTICE: Service account has custom permissions"; 105 | fi; 106 | 107 | if [[ $CSV != "True" ]]; then 108 | echo "Project ID: $PROJECT_ID"; 109 | echo "Project Name: $PROJECT_NAME"; 110 | echo "Project Application: $PROJECT_APPLICATION"; 111 | echo "Project Owner: $PROJECT_OWNER"; 112 | echo "Application Name: $APPLICATION_NAME"; 113 | echo "Hostname: $DEFAULT_HOSTNAME"; 114 | echo "Code Bucket: $CODE_BUCKET"; 115 | echo "Default Bucket: $DEFAULT_BUCKET"; 116 | echo "Serving Status: $SERVING_STATUS"; 117 | echo "Service Accounts: $SERVICE_ACCOUNT"; 118 | echo "Service Account Status: $SERVICE_ACCOUNT_STATUS_MESSAGE"; 119 | echo "Roles: $ROLES"; 120 | echo "$ROLES_STATUS_MESSAGE"; 121 | echo $BLANK_LINE; 122 | else 123 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$APPLICATION_NAME\", \"$DEFAULT_HOSTNAME\", \"$CODE_BUCKET\", \"$DEFAULT_BUCKET\", \"$SERVING_STATUS\", \"$SERVICE_ACCOUNT\", \"$SERVICE_ACCOUNT_STATUS_MESSAGE\", $ROLES, \"$ROLES_STATUS_MESSAGE\""; 124 | fi; 125 | else 126 | if [[ $CSV != "True" ]]; then 127 | echo "No application found for project $PROJECT_ID"; 128 | echo $BLANK_LINE; 129 | fi; 130 | fi; 131 | sleep $SLEEP_SECONDS; 132 | done; 133 | 134 | -------------------------------------------------------------------------------- /src/cis-4.1.1-compute-instance-default-service-account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | declare PROJECT_IDS=$(get_projects); 44 | fi; 45 | 46 | if [[ $DEBUG == "True" ]]; then 47 | echo "Projects: $PROJECT_IDS"; 48 | echo $BLANK_LINE; 49 | fi; 50 | 51 | if [[ $CSV == "True" ]]; then 52 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"INSTANCE_NAME\", \"SERVICE_ACCOUNTS\", \"IS_SA_VIOLATION\", \"IS_ROLE_VIOLATION\", \"ROLES\", \"ROLES_STATUS_MESSAGE\", \"SERVICE_ACCOUNT_STATUS_MESSAGE\", \"SCOPES\""; 53 | fi; 54 | 55 | for PROJECT_ID in $PROJECT_IDS; do 56 | 57 | set_project $PROJECT_ID; 58 | 59 | if ! api_enabled compute.googleapis.com; then 60 | if [[ $CSV != "True" ]]; then 61 | echo "Compute Engine API is not enabled on Project $PROJECT_ID"; 62 | fi; 63 | continue; 64 | fi; 65 | 66 | declare INSTANCES=$( gcloud compute instances list --quiet --format="json"); 67 | 68 | if [[ $DEBUG == "True" ]]; then 69 | echo "Instances (JSON): $INSTANCES"; 70 | echo $BLANK_LINE; 71 | fi; 72 | 73 | if [[ $INSTANCES != "[]" ]]; then 74 | 75 | #Get project details 76 | get_project_details $PROJECT_ID 77 | 78 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE;do 79 | 80 | if [[ $DEBUG == "True" ]]; then 81 | echo "Project Name: $PROJECT_NAME"; 82 | echo "Project Application: $PROJECT_APPLICATION"; 83 | echo "Project Owner: $PROJECT_OWNER"; 84 | echo "Instance (JSON): $INSTANCE"; 85 | fi; 86 | 87 | INSTANCE_NAME=$(echo $INSTANCE | jq -rc '.name'); 88 | SERVICE_ACCOUNTS=$(echo $INSTANCE | jq -rc '.serviceAccounts[].email'); 89 | IS_GKE_NODE=$(echo $INSTANCE | jq '.labels' | jq 'has("goog-gke-node")'); 90 | SCOPES=$(echo $INSTANCE | jq -rc '.serviceAccounts[].scopes[]'); 91 | ROLES=$(gcloud projects get-iam-policy $PROJECT_ID --flatten="bindings[].members" --filter="bindings.members:$SERVICE_ACCOUNTS" --format="json" | jq '.[]' | jq '.bindings.role'); 92 | 93 | # Determine if the default service account is in use 94 | if [[ $SERVICE_ACCOUNTS =~ [-]compute[@]developer[.]gserviceaccount[.]com && $IS_GKE_NODE == "false" ]]; then 95 | IS_SA_VIOLATION="True"; 96 | SERVICE_ACCOUNT_STATUS_MESSAGE="VIOLATION: Default Service Account detected"; 97 | else 98 | IS_SA_VIOLATION="False"; 99 | SERVICE_ACCOUNT_STATUS_MESSAGE="NOTICE: Custom Service Account Detected"; 100 | fi; 101 | 102 | if [[ $ROLES =~ editor ]]; then 103 | IS_ROLE_VIOLATION="True"; 104 | ROLES_STATUS_MESSAGE="VIOLATION: Service account has Editor permission"; 105 | else 106 | IS_ROLE_VIOLATION="False"; 107 | ROLES_STATUS_MESSAGE="NOTICE: Service account has custom permissions"; 108 | fi; 109 | 110 | if [[ $CSV != "True" ]]; then 111 | echo "Project ID: $PROJECT_ID"; 112 | echo "Project Name: $PROJECT_NAME"; 113 | echo "Project Application: $PROJECT_APPLICATION"; 114 | echo "Project Owner: $PROJECT_OWNER"; 115 | echo "Instance Name: $INSTANCE_NAME"; 116 | echo "Service Accounts: $SERVICE_ACCOUNTS"; 117 | echo "Status: $SERVICE_ACCOUNT_STATUS_MESSAGE"; 118 | echo "Google OAuth Scopes:"; 119 | echo $SCOPES; 120 | echo "Roles: $ROLES"; 121 | echo "$ROLES_STATUS_MESSAGE"; 122 | echo $BLANK_LINE; 123 | else 124 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$INSTANCE_NAME\", \"$SERVICE_ACCOUNTS\", \"$IS_SA_VIOLATION\", \"$IS_ROLE_VIOLATION\", \"$ROLES\", \"$ROLES_STATUS_MESSAGE\", \"$SERVICE_ACCOUNT_STATUS_MESSAGE\", \"$SCOPES\""; 125 | fi; 126 | done; 127 | else 128 | if [[ $CSV != "True" ]]; then 129 | echo "No instances found for project $PROJECT_ID"; 130 | echo $BLANK_LINE; 131 | fi; 132 | fi; 133 | sleep $SLEEP_SECONDS; 134 | done; 135 | 136 | -------------------------------------------------------------------------------- /src/cis-4.9.1-compute-instance-public-ips.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_ID=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_ID == "" ]]; then 42 | declare PROJECTS=$(gcloud projects list --format="json"); 43 | else 44 | declare PROJECTS=$(gcloud projects list --format="json" --filter="name:$PROJECT_ID"); 45 | fi; 46 | 47 | if [[ $DEBUG == "True" ]]; then 48 | echo "Project ID: $PROJECT_ID"; 49 | echo $BLANK_LINE; 50 | echo "Projects"; 51 | echo $PROJECTS; 52 | fi; 53 | 54 | if [[ $PROJECTS != "[]" ]]; then 55 | 56 | if [[ $CSV == "True" ]]; then 57 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"INSTANCE_NAME\", \"NETWORK\", \"SUBNETWORK\", \"INTERFACE_NAME\", \"IP_ADDRESS\", \"IS_GKE_NODE\", \"EXTERNAL_IP_STATUS_MESSAGE\""; 58 | fi; 59 | 60 | echo $PROJECTS | jq -rc '.[]' | while IFS='' read PROJECT;do 61 | 62 | PROJECT_ID=$(echo $PROJECT | jq -r '.projectId'); 63 | 64 | set_project $PROJECT_ID; 65 | 66 | if ! api_enabled compute.googleapis.com; then 67 | if [[ $CSV != "True" ]]; then 68 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 69 | fi; 70 | continue; 71 | fi; 72 | 73 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json" 2>/dev/null); 74 | 75 | if [[ $INSTANCES != "[]" ]]; then 76 | 77 | #Get project details 78 | get_project_details $PROJECT_ID 79 | 80 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE; do 81 | 82 | INSTANCE_NAME=$(echo $INSTANCE | jq -rc '.name'); 83 | EXTERNAL_NETWORK_INTERFACES=$(echo $INSTANCE | jq -rc '.networkInterfaces' | jq 'select("accessConfigs")'); 84 | IS_GKE_NODE=$(echo $INSTANCE | jq '.labels' | jq 'has("goog-gke-node")'); 85 | 86 | echo $EXTERNAL_NETWORK_INTERFACES | jq -rc '.[]' | while IFS='' read -r INTERFACE;do 87 | 88 | HAS_EXTERNAL_IP_ADDRESS=$(echo $INTERFACE | jq -rc '.accessConfigs // empty'); 89 | 90 | if [[ $HAS_EXTERNAL_IP_ADDRESS != "" ]]; then 91 | 92 | INTERFACE_NAME=$(echo $INTERFACE | jq -rc '.name'); 93 | IP_ADDRESS=$(echo $INTERFACE | jq -rc '.accessConfigs[].natIP'); 94 | NETWORK=$(echo $INTERFACE | jq -rc '.network' | awk -F/ '{print $(NF)}'); 95 | SUBNETWORK=$(echo $INTERFACE | jq -rc '.subnetwork' | awk -F/ '{print $(NF)}'); 96 | if [[ $IS_GKE_NODE == "false" ]]; then 97 | EXTERNAL_IP_STATUS_MESSAGE="VIOLATION: Exterally routable IP address detected"; 98 | else 99 | EXTERNAL_IP_STATUS_MESSAGE="VIOLATION: GKE cluster is not a Private Kubernetes Cluster"; 100 | fi; 101 | 102 | if [[ $CSV != "True" ]]; then 103 | echo "Project Name: $PROJECT_NAME"; 104 | echo "Project Application: $PROJECT_APPLICATION"; 105 | echo "Project Owner: $PROJECT_OWNER"; 106 | echo "Instance Name: $INSTANCE_NAME"; 107 | echo "Interface Name: $INTERFACE_NAME"; 108 | echo "Network: $NETWORK"; 109 | echo "Subnetwork: $SUBNETWORK"; 110 | echo "IP Address: $IP_ADDRESS"; 111 | echo "Status: $EXTERNAL_IP_STATUS_MESSAGE"; 112 | echo $BLANK_LINE; 113 | else 114 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$INSTANCE_NAME\", \"$NETWORK\", \"$SUBNETWORK\", \"$INTERFACE_NAME\", \"$IP_ADDRESS\", \"$IS_GKE_NODE\", \"$EXTERNAL_IP_STATUS_MESSAGE\""; 115 | fi; 116 | else 117 | if [[ $CSV != "True" ]]; then 118 | echo "Non-issue: The instance does not have an external network interface"; 119 | echo $BLANK_LINE; 120 | fi; 121 | fi; 122 | done; 123 | done; 124 | else # if no instances 125 | if [[ $CSV != "True" ]]; then 126 | echo "No compute instances found for Project $PROJECT_ID"; 127 | echo $BLANK_LINE; 128 | fi; 129 | fi; # if instances 130 | sleep $SLEEP_SECONDS; 131 | done; 132 | else # if no projects 133 | if [[ $CSV != "True" ]]; then 134 | echo "No projects found"; 135 | echo $BLANK_LINE; 136 | fi; 137 | fi; # if projects 138 | 139 | -------------------------------------------------------------------------------- /src/cis-4.5.1-enabled-serial-ports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | 43 | if [[ $PROJECT_IDS == "" ]]; then 44 | declare PROJECT_IDS=$(get_projects); 45 | fi; 46 | 47 | if [[ $DEBUG == "True" ]]; then 48 | echo "Projects: $PROJECT_IDS"; 49 | echo $BLANK_LINE; 50 | fi; 51 | 52 | if [[ $CSV == "True" ]]; then 53 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"INSTANCE_NAME\", \"ENABLED_SERIAL_PORTS\", \"ENABLED_SERIAL_PORT_LOGGING\", \"ENABLED_SERIAL_PORTS_STATUS_MESSAGE\", \"ENABLED_SERIAL_PORT_LOGGING_STATUS_MESSAGE\""; 54 | fi; 55 | 56 | for PROJECT_ID in $PROJECT_IDS; do 57 | 58 | set_project $PROJECT_ID; 59 | 60 | if ! api_enabled compute.googleapis.com; then 61 | if [[ $CSV != "True" ]]; then 62 | echo "Compute Engine API is not enabled on Project $PROJECT_ID"; 63 | continue; 64 | fi; 65 | fi; 66 | 67 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json"); 68 | 69 | if [[ $DEBUG == "True" ]]; then 70 | echo "Instances (JSON): $INSTANCES"; 71 | echo $BLANK_LINE; 72 | fi; 73 | 74 | if [[ $INSTANCES != "[]" ]]; then 75 | 76 | #Get project details 77 | get_project_details $PROJECT_ID 78 | 79 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE;do 80 | 81 | if [[ $DEBUG == "True" ]]; then 82 | echo "Instance (JSON): $INSTANCES"; 83 | echo $BLANK_LINE; 84 | fi; 85 | 86 | INSTANCE_NAME=$(echo $INSTANCE | jq -rc '.name'); 87 | ENABLED_SERIAL_PORTS=$(echo $INSTANCE | jq -rc '.metadata.items[] | select(.key=="serial-port-enable")' | jq -rc '.value' | tr '[:upper:]' '[:lower:]' ); 88 | ENABLED_SERIAL_PORT_LOGGING=$(echo $INSTANCE | jq -rc '.metadata.items[] | select(.key=="serial-port-logging-enable")' | jq -rc '.value' | tr '[:upper:]' '[:lower:]' ); 89 | ENABLED_SERIAL_PORTS_STATUS_MESSAGE="Serial port disabled"; 90 | ENABLED_SERIAL_PORT_LOGGING_STATUS_MESSAGE="Serial port logging enabled"; 91 | 92 | if [[ $DEBUG == "True" ]]; then 93 | echo "Instance Metadata (JSON): $(echo $INSTANCE | jq -rc '.metadata.items[]')"; 94 | echo $BLANK_LINE; 95 | fi; 96 | 97 | if [[ $ENABLED_SERIAL_PORTS == "" ]]; then 98 | ENABLED_SERIAL_PORTS="false"; 99 | fi; 100 | 101 | if [[ $ENABLED_SERIAL_PORT_LOGGING == "" ]]; then 102 | ENABLED_SERIAL_PORT_LOGGING="false"; 103 | fi; 104 | 105 | if [[ $ENABLED_SERIAL_PORTS == "true" ]]; then 106 | ENABLED_SERIAL_PORTS_STATUS_MESSAGE="VIOLATION: Serial port enabled"; 107 | fi; 108 | 109 | if [[ $ENABLED_SERIAL_PORT_LOGGING != "true" ]]; then 110 | ENABLED_SERIAL_PORT_LOGGING_STATUS_MESSAGE="VIOLATION: Serial port logging disabled"; 111 | fi; 112 | 113 | # Print the results gathered above 114 | if [[ $CSV != "True" ]]; then 115 | echo "Project ID: $PROJECT_ID"; 116 | echo "Project Name: $PROJECT_NAME"; 117 | echo "Project Application: $PROJECT_APPLICATION"; 118 | echo "Project Owner: $PROJECT_OWNER"; 119 | echo "Instance Name: $INSTANCE_NAME"; 120 | echo "Serial Port Setting: $ENABLED_SERIAL_PORTS"; 121 | echo "Serial Port Status: $ENABLED_SERIAL_PORTS_STATUS_MESSAGE"; 122 | echo "Serial Port Logging Setting: $ENABLED_SERIAL_PORT_LOGGING"; 123 | echo "Serial Port Logging Status: $ENABLED_SERIAL_PORT_LOGGING_STATUS_MESSAGE"; 124 | echo $BLANK_LINE; 125 | else 126 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$INSTANCE_NAME\", \"$ENABLED_SERIAL_PORTS\", \"$ENABLED_SERIAL_PORT_LOGGING\", \"$ENABLED_SERIAL_PORTS_STATUS_MESSAGE\", \"$ENABLED_SERIAL_PORT_LOGGING_STATUS_MESSAGE\""; 127 | fi; 128 | 129 | done; 130 | echo $BLANK_LINE; 131 | else 132 | if [[ $CSV != "True" ]]; then 133 | echo "No instances found for project $PROJECT_ID"; 134 | echo $BLANK_LINE; 135 | fi; 136 | fi; 137 | sleep $SLEEP_SECONDS; 138 | done; 139 | 140 | -------------------------------------------------------------------------------- /src/cis-3.8.1-vpc-flow-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:r:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | fi; 44 | 45 | if [[ $CSV == "True" ]]; then 46 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"SUBNET_NAME\", \"IP_RANGE\", \"FLOW_LOGS_ENABLED\", \"FLOW_LOG_AGGREGATION_INTERVAL\", \"FLOW_LOG_SAMPLE_RATE\", \"FLOW_LOG_METADATA_CONFIGURATION\", \"FLOW_LOGS_ENABLED\", \"FLOW_LOG_STATUS_MESSAGE\", \"FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE\""; 47 | fi; 48 | 49 | for PROJECT_ID in $PROJECT_IDS; do 50 | 51 | set_project $PROJECT_ID; 52 | 53 | if ! api_enabled compute.googleapis.com ; then 54 | echo "Compute Engine API is not enabled on Project $PROJECT_ID"; 55 | continue; 56 | fi; 57 | 58 | declare SUBNETS=$(gcloud compute networks subnets list --format json); 59 | 60 | if [[ $CSV != "True" ]]; then 61 | echo "---------------------------------------------------------------------------------"; 62 | echo "Subnets for Project $PROJECT_ID"; 63 | echo "---------------------------------------------------------------------------------"; 64 | fi; 65 | 66 | if [[ $SUBNETS != "[]" ]]; then 67 | 68 | #Get project details 69 | get_project_details $PROJECT_ID 70 | 71 | echo $SUBNETS | jq -rc '.[]' | while IFS='' read -r SUBNET;do 72 | 73 | SUBNET_NAME=$(echo $SUBNET | jq -rc '.name'); 74 | IP_RANGE=$(echo $SUBNET | jq -rc '.ipCidrRange'); 75 | FLOW_LOGS_CONFIGURED=$(echo $SUBNET | jq -rc '.logConfig // ""'); 76 | 77 | if [[ $FLOW_LOGS_CONFIGURED != "" ]]; then 78 | 79 | FLOW_LOGS_ENABLED=$(echo $SUBNET | jq -rc '.enableFlowLogs'); 80 | FLOW_LOG_AGGREGATION_INTERVAL=$(echo $SUBNET | jq -rc '.logConfig.aggregationInterval'); 81 | FLOW_LOG_SAMPLE_RATE=$(echo $SUBNET | jq -rc '.logConfig.flowSampling'); 82 | FLOW_LOG_METADATA_CONFIGURATION=$(echo $SUBNET | jq -rc '.logConfig.metadata'); 83 | 84 | # Returns 0 for FALSE and 1 for TRUE 85 | FLOW_LOG_SAMPLE_RATE_TOO_LOW=$(echo "$FLOW_LOG_SAMPLE_RATE < 0.10" | bc); 86 | 87 | if [[ $FLOW_LOGS_ENABLED == "false" ]]; then 88 | FLOW_LOG_STATUS_MESSAGE="VIOLATION: Flow logging is configured but not enabled"; 89 | else 90 | FLOW_LOG_STATUS_MESSAGE="PASS: Flow log enabled"; 91 | fi; 92 | 93 | if [[ $FLOW_LOG_SAMPLE_RATE_TOO_LOW == 1 ]]; then 94 | FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE="VIOLATION: Flow log sample rate is too low"; 95 | else 96 | FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE="PASS: Flow log sample rate is at least 10%"; 97 | fi; 98 | 99 | else # $FLOW_LOGS_CONFIGURED is NULL 100 | FLOW_LOGS_ENABLED="false"; 101 | FLOW_LOG_AGGREGATION_INTERVAL="0"; 102 | FLOW_LOG_SAMPLE_RATE="0"; 103 | FLOW_LOG_METADATA_CONFIGURATION="false"; 104 | FLOW_LOG_STATUS_MESSAGE="VIOLATION: Flow logging is configured but not enabled"; 105 | FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE="VIOLATION: Flow log sample rate is too low"; 106 | fi; 107 | 108 | # Print the results gathered above 109 | if [[ $CSV != "True" ]]; then 110 | echo "Project Name: $PROJECT_NAME"; 111 | echo "Project Application: $PROJECT_APPLICATION"; 112 | echo "Project Owner: $PROJECT_OWNER"; 113 | echo "Subnet: $SUBNET_NAME"; 114 | echo "IP Range: $IP_RANGE"; 115 | echo "Flow Log Enabled: $FLOW_LOGS_ENABLED"; 116 | echo "Flow Log Aggregation Interval: $FLOW_LOG_AGGREGATION_INTERVAL"; 117 | echo "Flow Log Sample Rate: $FLOW_LOG_SAMPLE_RATE"; 118 | echo "Flow Log Metadata Configuration: $FLOW_LOG_METADATA_CONFIGURATION"; 119 | echo $FLOW_LOG_STATUS_MESSAGE; 120 | echo $FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE; 121 | echo $BLANK_LINE; 122 | else 123 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$SUBNET_NAME\", \"$IP_RANGE\", \"$FLOW_LOGS_ENABLED\", \"$FLOW_LOG_AGGREGATION_INTERVAL\", \"$FLOW_LOG_SAMPLE_RATE\", \"$FLOW_LOG_METADATA_CONFIGURATION\", \"$FLOW_LOGS_ENABLED\", \"$FLOW_LOG_STATUS_MESSAGE\", \"$FLOW_LOG_SAMPLE_RATE_STATUS_MESSAGE\""; 124 | fi; 125 | 126 | done; 127 | 128 | else 129 | if [[ $CSV != "True" ]]; then 130 | echo "No subnets found for project $PROJECT_ID"; 131 | echo $BLANK_LINE; 132 | fi; 133 | fi; 134 | sleep $SLEEP_SECONDS; 135 | done; 136 | 137 | -------------------------------------------------------------------------------- /src/cis-2.2.3-organization-cloud-logging-sinks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare ORGANIZATION_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-o, --organization ORGANIZATION] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--orgnanization") set -- "$@" "-o" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdco:" option 26 | do 27 | case "${option}" 28 | in 29 | o) 30 | ORGNAIZATION_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if ! api_enabled logging.googleapis.com; then 42 | echo "WARNING: Logging API is not enabled"; 43 | exit 1000; 44 | fi; 45 | 46 | declare DEFAULT_DEFAULT_LOG_SINK_FILTER="NOT LOG_ID(\"cloudaudit.googleapis.com/activity\") AND NOT LOG_ID(\"externalaudit.googleapis.com/activity\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"externalaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") AND NOT LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 47 | 48 | declare DEFAULT_REQUIRED_LOG_SINK_FILTER="LOG_ID(\"cloudaudit.googleapis.com/activity\") OR LOG_ID(\"externalaudit.googleapis.com/activity\") OR LOG_ID(\"cloudaudit.googleapis.com/system_event\") OR LOG_ID(\"externalaudit.googleapis.com/system_event\") OR LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") OR LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 49 | 50 | declare SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE="NOTICE: Google _Default log sink filter is in use"; 51 | 52 | declare SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE="NOTICE: Google _Required log sink filter is in use"; 53 | 54 | declare SINK_FILTER_IS_NOT_DEFAULT_MESSAGE="NOTICE: Custom log sink filter is in use"; 55 | 56 | if [[ $ORGANIZATIONAL_IDS == "" ]]; then 57 | declare ORGANIZATIONS=$(gcloud organizations list --format="json"); 58 | fi; 59 | 60 | if [[ $DEBUG == "True" ]]; then 61 | echo "Organizations (JSON): $ORGANIZATIONS"; 62 | fi; 63 | 64 | if [[ $CSV == "True" ]]; then 65 | echo "\"ORGANIZATION_DISPLAY_NAME\", \"SINK_NAME\", \"SINK_DESTINATION\", \"SINK_FILTER_IS_DEFAULT_DEFAULT\", \"SINK_FILTER_IS_REQUIRED_DEFAULT\", \"SINK_FILTER_MESSAGE\", \"SINK_FILTER\""; 66 | fi; 67 | 68 | echo $ORGANIZATIONS | jq -rc '.[]' | while IFS='' read -r ORGANIZATION; do 69 | 70 | ORGANIZATION_NAME=$(echo $ORGANIZATION | jq -rc '.name' | cut -d"/" -f2); 71 | ORGANIZATION_DISPLAY_NAME=$(echo $ORGANIZATION | jq -rc '.displayName'); 72 | 73 | declare SINKS=$(gcloud logging sinks list --format="json" --organization="$ORGANIZATION_NAME"); 74 | 75 | if [[ $DEBUG == "True" ]]; then 76 | echo "Sinks (JSON): $SINKS"; 77 | fi; 78 | 79 | if [[ $CSV != "True" ]]; then 80 | echo "---------------------------------------------------------------------------------"; 81 | echo "Log Sinks for Organization $ORGANIZATION_DISPLAY_NAME"; 82 | echo "---------------------------------------------------------------------------------"; 83 | echo $BLANK_LINE; 84 | fi; 85 | 86 | if [[ $SINKS != "[]" ]]; then 87 | 88 | echo $SINKS | jq -rc '.[]' | while IFS='' read -r SINK;do 89 | 90 | if [[ $DEBUG == "True" ]]; then 91 | echo "Log Sink (JSON): $SINK"; 92 | fi; 93 | 94 | SINK_NAME=$(echo $SINK | jq -rc '.name'); 95 | SINK_DESTINATION=$(echo $SINK | jq -rc '.destination'); 96 | SINK_FILTER=$(echo $SINK | jq -rc '.filter'); 97 | SINK_FILTER_IS_DEFAULT_DEFAULT="False"; 98 | SINK_FILTER_IS_REQUIRED_DEFAULT="False"; 99 | 100 | if [[ $SINK_FILTER == $DEFAULT_DEFAULT_LOG_SINK_FILTER ]]; then 101 | SINK_FILTER_IS_DEFAULT_DEFAULT="True"; 102 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE; 103 | elif [[ $SINK_FILTER == $DEFAULT_REQUIRED_LOG_SINK_FILTER ]]; then 104 | SINK_FILTER_IS_REQUIRED_DEFAULT="True"; 105 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE; 106 | else 107 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_NOT_DEFAULT_MESSAGE; 108 | fi; 109 | 110 | # Print the results gathered above 111 | if [[ $CSV != "True" ]]; then 112 | echo "Organization: $ORGANIZATION_DISPLAY_NAME"; 113 | echo "Log Sink Name: $SINK_NAME"; 114 | echo "Log Sink Destination: $SINK_DESTINATION"; 115 | echo "Log Sink Filter Message: $SINK_FILTER_MESSAGE"; 116 | echo "Log Sink Filter: $SINK_FILTER"; 117 | echo $BLANK_LINE; 118 | else 119 | echo "\"$ORGANIZATION_DISPLAY_NAME\", \"$SINK_NAME\", \"$SINK_DESTINATION\", \"$SINK_FILTER_IS_DEFAULT_DEFAULT\", \"$SINK_FILTER_IS_REQUIRED_DEFAULT\", \"$SINK_FILTER_MESSAGE\", \"$SINK_FILTER\""; 120 | fi; # if csv 121 | 122 | done; #sinks 123 | else 124 | if [[ $CSV != "True" ]]; then 125 | echo "No log sinks found for organization $ORGANIZATION_DISPLAY_NAME"; 126 | echo $BLANK_LINE; 127 | fi; 128 | fi; 129 | sleep $SLEEP_SECONDS; 130 | done; #organizations 131 | 132 | -------------------------------------------------------------------------------- /src/cis-4.9.2-project-public-ips.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | declare HELP=$(cat << EOL 10 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 11 | EOL 12 | ); 13 | 14 | for arg in "$@"; do 15 | shift 16 | case "$arg" in 17 | "--help") set -- "$@" "-h" ;; 18 | "--debug") set -- "$@" "-d" ;; 19 | "--csv") set -- "$@" "-c" ;; 20 | "--project") set -- "$@" "-p" ;; 21 | *) set -- "$@" "$arg" 22 | esac 23 | done 24 | 25 | while getopts "hdcp:" option 26 | do 27 | case "${option}" 28 | in 29 | p) 30 | PROJECT_IDS=${OPTARG};; 31 | d) 32 | DEBUG="True";; 33 | c) 34 | CSV="True";; 35 | h) 36 | echo $HELP; 37 | exit 0;; 38 | esac; 39 | done; 40 | 41 | if [[ $PROJECT_IDS == "" ]]; then 42 | declare PROJECT_IDS=$(get_projects); 43 | fi; 44 | 45 | if [[ $PROJECTS != "[]" ]]; then 46 | 47 | if [[ $CSV == "True" ]]; then 48 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"IP_ADDRESS\", \"ADDRESS_TYPE\", \"KIND\", \"ADDRESS_NAME\", \"PURPOSE\", \"DESCRIPTION\", \"STATUS\", \"VERSION\", \"DIRTY\", \"EXTERNAL_IP_STATUS_MESSAGE\""; 49 | fi; 50 | 51 | for PROJECT_ID in $PROJECT_IDS; do 52 | set_project $PROJECT_ID; 53 | 54 | 55 | if ! api_enabled compute.googleapis.com; then 56 | if [[ $CSV != "True" ]]; then 57 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 58 | fi; 59 | continue; 60 | fi; 61 | 62 | declare ADDRESSES=$(gcloud compute addresses list --quiet --format="json"); 63 | 64 | if [[ $ADDRESSES != "[]" ]]; then 65 | 66 | #Get project details 67 | get_project_details $PROJECT_ID 68 | 69 | if [[ $CSV != "True" ]]; then 70 | echo "---------------------------------------------------------------------------------"; 71 | echo "External IP Addresses for Project $PROJECT_ID"; 72 | echo "---------------------------------------------------------------------------------"; 73 | fi; 74 | 75 | echo $ADDRESSES | jq -rc '.[]' | while IFS='' read -r ADDRESS;do 76 | 77 | if [[ $DEBUG == "True" ]]; then 78 | echo $ADDRESS | jq -rc '.'; 79 | fi; 80 | 81 | ADDRESS_NAME=$(echo $ADDRESS | jq -rc '.name'); 82 | IP_ADDRESS=$(echo $ADDRESS | jq -rc '.address'); 83 | ADDRESS_TYPE=$(echo $ADDRESS | jq -rc '.addressType'); 84 | KIND=$(echo $ADDRESS | jq -rc '.kind'); 85 | STATUS=$(echo $ADDRESS | jq -rc '.status'); 86 | DESCRIPTION=$(echo $ADDRESS | jq -rc '.description //empty'); 87 | VERSION=$(echo $ADDRESS | jq -rc '.ipVersion // empty'); 88 | PURPOSE=$(echo $ADDRESS | jq -rc '.purpose // empty'); 89 | USERS=$(echo $ADDRESS | jq -rc '.users[]?'); 90 | 91 | # Set the Purpose field to a better value 92 | if [[ $PURPOSE == "" ]]; then 93 | if [[ $USERS == *"forwardingRules"* ]]; then 94 | PURPOSE="Forwarding Rule"; 95 | elif [[ $USERS == *"routers"* ]]; then 96 | PURPOSE="Cloud NAT Router"; 97 | elif [[ $STATUS == "RESERVED" ]]; then 98 | PURPOSE="Reserved"; 99 | fi; 100 | elif [[ $PURPOSE == "NAT_AUTO" ]]; then 101 | PURPOSE="Cloud NAT Router"; 102 | fi; 103 | 104 | # Decide if the IP address is dirty 105 | if [[ $PURPOSE == "Cloud NAT Router" ]]; then 106 | DIRTY="False"; 107 | EXTERNAL_IP_STATUS_MESSAGE="Non-issue: The IP address belongs to a Cloud NAT Router"; 108 | elif [[ $PURPOSE == "Forwarding Rule" ]]; then 109 | DIRTY="False"; 110 | EXTERNAL_IP_STATUS_MESSAGE="Non-issue: The IP address belongs to a Load Balancer Forwarding Rule"; 111 | elif [[ $PURPOSE == "Reserved" ]]; then 112 | DIRTY="True"; 113 | EXTERNAL_IP_STATUS_MESSAGE="WARNING: The IP address is external but not in use at this time"; 114 | elif [[ $ADDRESS_TYPE == "EXTERNAL" ]]; then 115 | DIRTY="True"; 116 | EXTERNAL_IP_STATUS_MESSAGE="VIOLATION: Exterally routable IP address detected"; 117 | else 118 | DIRTY="False"; 119 | EXTERNAL_IP_STATUS_MESSAGE="Non-issue: The IP address cannot be routed externally"; 120 | fi; 121 | 122 | # Right now, we only print dirty addresses, but we could add a flag to print all 123 | if [[ $CSV != "True" && $DIRTY == "True" ]]; then 124 | echo "Project Name: $PROJECT_NAME"; 125 | echo "Project Application: $PROJECT_APPLICATION"; 126 | echo "Project Owner: $PROJECT_OWNER"; 127 | echo "IP Address: $IP_ADDRESS ($ADDRESS_TYPE $KIND)"; 128 | echo "Name: $ADDRESS_NAME"; 129 | echo "Status: $STATUS"; 130 | echo "Purpose: $PURPOSE"; 131 | echo "Description: $DESCRIPTION"; 132 | echo "Version: $VERSION"; 133 | echo "$EXTERNAL_IP_STATUS_MESSAGE"; 134 | echo $BLANK_LINE; 135 | elif [[ $CSV == "True" && $DIRTY == "True" ]]; then 136 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$IP_ADDRESS\", \"$ADDRESS_TYPE\", \"$KIND\", \"$ADDRESS_NAME\", \"$PURPOSE\", \"$DESCRIPTION\", \"$STATUS\", \"$VERSION\", \"$DIRTY\", \"$EXTERNAL_IP_STATUS_MESSAGE\""; 137 | fi; 138 | done; 139 | fi; 140 | sleep $SLEEP_SECONDS; 141 | done; 142 | else 143 | if [[ $CSV != "True" ]]; then 144 | echo "No projects found"; 145 | echo $BLANK_LINE; 146 | fi; 147 | fi; 148 | 149 | -------------------------------------------------------------------------------- /src/cis-2.2.1-project-cloud-logging-sinks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | declare DEFAULT_DEFAULT_LOG_SINK_FILTER="NOT LOG_ID(\"cloudaudit.googleapis.com/activity\") AND NOT LOG_ID(\"externalaudit.googleapis.com/activity\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"externalaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") AND NOT LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 43 | 44 | declare DEFAULT_REQUIRED_LOG_SINK_FILTER="LOG_ID(\"cloudaudit.googleapis.com/activity\") OR LOG_ID(\"externalaudit.googleapis.com/activity\") OR LOG_ID(\"cloudaudit.googleapis.com/system_event\") OR LOG_ID(\"externalaudit.googleapis.com/system_event\") OR LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") OR LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 45 | 46 | declare SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE="NOTICE: Google _Default log sink filter is in use"; 47 | 48 | declare SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE="NOTICE: Google _Required log sink filter is in use"; 49 | 50 | declare SINK_FILTER_IS_NOT_DEFAULT_MESSAGE="NOTICE: Custom log sink filter is in use"; 51 | 52 | if [[ $PROJECT_IDS == "" ]]; then 53 | declare PROJECT_IDS=$(get_projects); 54 | fi; 55 | 56 | if [[ $DEBUG == "True" ]]; then 57 | echo "Projects: $PROJECT_IDS"; 58 | echo $BLANK_LINE; 59 | fi; 60 | 61 | if [[ $CSV == "True" ]]; then 62 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"SINK_NAME\", \"SINK_DESTINATION\", \"SINK_FILTER_IS_DEFAULT_DEFAULT\", \"SINK_FILTER_IS_REQUIRED_DEFAULT\", \"SINK_FILTER_MESSAGE\", \"SINK_FILTER\""; 63 | fi; 64 | 65 | for PROJECT_ID in $PROJECT_IDS; do 66 | 67 | set_project $PROJECT_ID; 68 | 69 | if ! api_enabled logging.googleapis.com; then 70 | if [[ $CSV != "True" ]]; then 71 | echo "WARNING: Logging API is not enabled"; 72 | fi; 73 | continue; 74 | fi; 75 | 76 | declare SINKS=$(gcloud logging sinks list --format json --project="$PROJECT_ID"); 77 | 78 | if [[ $DEBUG == "True" ]]; then 79 | echo "Sinks (JSON): $SINKS"; 80 | echo $BLANK_LINE; 81 | fi; 82 | 83 | if [[ $CSV != "True" ]]; then 84 | echo $SEPARATOR; 85 | echo "Log Sinks for Project $PROJECT_ID"; 86 | echo $SEPARATOR; 87 | fi; 88 | 89 | if [[ $SINKS != "[]" ]]; then 90 | 91 | #Get project details 92 | get_project_details $PROJECT_ID 93 | 94 | echo $SINKS | jq -rc '.[]' | while IFS='' read -r SINK;do 95 | 96 | if [[ $DEBUG == "True" ]]; then 97 | echo "Project Name: $PROJECT_NAME"; 98 | echo "Project Application: $PROJECT_APPLICATION"; 99 | echo "Project Owner: $PROJECT_OWNER"; 100 | echo "Log Sink (JSON): $SINK"; 101 | fi; 102 | 103 | SINK_NAME=$(echo $SINK | jq -rc '.name'); 104 | SINK_DESTINATION=$(echo $SINK | jq -rc '.destination'); 105 | SINK_FILTER=$(echo $SINK | jq -rc '.filter'); 106 | SINK_FILTER_IS_DEFAULT_DEFAULT="False"; 107 | SINK_FILTER_IS_REQUIRED_DEFAULT="False"; 108 | 109 | if [[ $SINK_FILTER == $DEFAULT_DEFAULT_LOG_SINK_FILTER ]]; then 110 | SINK_FILTER_IS_DEFAULT_DEFAULT="True"; 111 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE; 112 | elif [[ $SINK_FILTER == $DEFAULT_REQUIRED_LOG_SINK_FILTER ]]; then 113 | SINK_FILTER_IS_REQUIRED_DEFAULT="True"; 114 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE; 115 | else 116 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_NOT_DEFAULT_MESSAGE; 117 | fi; 118 | 119 | # Print the results gathered above 120 | if [[ $CSV != "True" ]]; then 121 | echo "Project ID: $PROJECT_ID"; 122 | echo "Project Name: $PROJECT_NAME"; 123 | echo "Project Application: $PROJECT_APPLICATION"; 124 | echo "Project Owner: $PROJECT_OWNER"; 125 | echo "Log Sink Name: $SINK_NAME"; 126 | echo "Log Sink Destination: $SINK_DESTINATION"; 127 | echo "Log Sink Filter Message: $SINK_FILTER_MESSAGE"; 128 | echo "Log Sink Filter: $SINK_FILTER"; 129 | echo $BLANK_LINE; 130 | else 131 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$SINK_NAME\", \"$SINK_DESTINATION\", \"$SINK_FILTER_IS_DEFAULT_DEFAULT\", \"$SINK_FILTER_IS_REQUIRED_DEFAULT\", \"$SINK_FILTER_MESSAGE\", \"$SINK_FILTER\""; 132 | fi; 133 | 134 | done; 135 | 136 | else 137 | if [[ $CSV != "True" ]]; then 138 | echo "No log sinks found for project $PROJECT_ID"; 139 | echo $BLANK_LINE; 140 | fi; 141 | fi; 142 | sleep $SLEEP_SECONDS; 143 | done; 144 | 145 | -------------------------------------------------------------------------------- /src/cis-3.8.4-vpc-dns-policy-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare PROJECT_IDS=""; 7 | declare DEBUG="False"; 8 | declare CSV="False"; 9 | 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ) 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:r:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG} ;; 32 | d) 33 | DEBUG="True" ;; 34 | c) 35 | CSV="True" ;; 36 | h) 37 | echo "$HELP" 38 | exit 0 ;; 39 | esac 40 | done 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | PROJECT_IDS=$(get_projects) 44 | fi 45 | 46 | if [[ $CSV == "True" ]]; then 47 | echo "\"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"VPC_NAME\", \"DNS_POLICY_NAME\", \"DNS_POLICY_ENABLED\", \"DNS_POLICY_LOGGING_ENABLED\", \"STATUS_MESSAGE\"" 48 | fi 49 | 50 | 51 | for PROJECT_ID in $PROJECT_IDS; do 52 | set_project $PROJECT_ID 53 | 54 | if ! api_enabled compute.googleapis.com; then 55 | echo "Compute Engine API is not enabled on Project $PROJECT_ID" 56 | continue 57 | fi 58 | 59 | 60 | VPCS=$(gcloud compute networks list --format json) 61 | 62 | if [[ $CSV != "True" ]]; then 63 | echo "---------------------------------------------------------------------------------" 64 | echo "VPCs for Project $PROJECT_ID" 65 | echo $BLANK_LINE; 66 | fi 67 | 68 | if [[ $VPCS != "[]" ]]; then 69 | 70 | #Get project details 71 | get_project_details $PROJECT_ID 72 | 73 | 74 | echo $VPCS | jq -rc '.[]' | while IFS='' read -r VPC; do 75 | VPC_NAME=$(echo $VPC | jq -rc '.name') 76 | 77 | DNS_POLICIES=$(gcloud dns policies list --format json --project $PROJECT_ID) 78 | if [[ $DNS_POLICIES != "[]" ]]; then 79 | echo "${DNS_POLICIES}" | jq -rc '.[]' | while IFS='' read -r POLICY; do 80 | POLICY_NETWORK=$(echo "$POLICY" | jq -r '.networks[] | select(.networkUrl | contains("'$VPC_NAME'"))') 81 | if [[ $POLICY_NETWORK ]]; then 82 | DNS_POLICY_NAME=$(echo "$POLICY" | jq -r '.name') 83 | DNS_POLICY_ENABLED="true" 84 | DNS_POLICY_LOGGING_ENABLED=$(echo "$POLICY" | jq -r '.enableLogging') 85 | if [[ $DNS_POLICY_LOGGING_ENABLED == "false" ]]; then 86 | STATUS_MESSAGE="VIOLATION: DNS policy logging is not enabled" 87 | else 88 | STATUS_MESSAGE="DNS policy logging is enabled" 89 | fi 90 | 91 | if [[ $DEBUG == "True" ]]; then 92 | echo "DEBUG: VPC Name: $VPC_NAME" 93 | echo "DEBUG: DNS Policy: $POLICY" 94 | fi 95 | 96 | # Print the results gathered above 97 | if [[ $CSV != "True" ]]; then 98 | echo "Project Name: $PROJECT_NAME" 99 | echo "Project Application: $PROJECT_APPLICATION" 100 | echo "Project Owner: $PROJECT_OWNER" 101 | echo "VPC Name: $VPC_NAME" 102 | echo "DNS Policy Name: $DNS_POLICY_NAME" 103 | echo "DNS Policy Enabled: $DNS_POLICY_ENABLED" 104 | echo "DNS Policy Logging Enabled: $DNS_POLICY_LOGGING_ENABLED" 105 | echo "Status: $STATUS_MESSAGE" 106 | echo $BLANK_LINE; 107 | else 108 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$VPC_NAME\", \"$DNS_POLICY_NAME\", \"$DNS_POLICY_ENABLED\", \"$DNS_POLICY_LOGGING_ENABLED\", \"$STATUS_MESSAGE\"" 109 | fi 110 | fi 111 | done 112 | else 113 | DNS_POLICY_NAME="No DNS Policy" 114 | DNS_POLICY_ENABLED="false" 115 | DNS_POLICY_LOGGING_ENABLED="Not applicable" 116 | STATUS_MESSAGE="Not applicable" 117 | 118 | if [[ $DEBUG == "True" ]]; then 119 | echo "DEBUG: VPC Name: $VPC_NAME" 120 | fi 121 | 122 | # Print the results for VPC with no DNS policy 123 | if [[ $CSV != "True" ]]; then 124 | echo "Project Name: $PROJECT_NAME" 125 | echo "Project Application: $PROJECT_APPLICATION" 126 | echo "Project Owner: $PROJECT_OWNER" 127 | echo "VPC Name: $VPC_NAME" 128 | echo "DNS Policy Name: $DNS_POLICY_NAME" 129 | echo "DNS Policy Enabled: $DNS_POLICY_ENABLED" 130 | echo "DNS Policy Logging Enabled: $DNS_POLICY_LOGGING_ENABLED" 131 | echo "Status: $STATUS_MESSAGE" 132 | echo $BLANK_LINE; 133 | else 134 | echo "\"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$VPC_NAME\", \"$DNS_POLICY_NAME\", \"$DNS_POLICY_ENABLED\", \"$DNS_POLICY_LOGGING_ENABLED\", \"$STATUS_MESSAGE\"" 135 | fi 136 | fi 137 | done 138 | else 139 | if [[ $CSV != "True" ]]; then 140 | echo "No VPCs found for project $PROJECT_ID" 141 | echo $BLANK_LINE; 142 | fi 143 | fi 144 | sleep $SLEEP_SECONDS; 145 | done 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/utility-list-project-owners.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ============================================================================= 4 | # Script Name: list-projects.sh 5 | # Description: 6 | # This script lists GCP projects with optional filters and output formats. 7 | # Supports CSV output, debug mode, owner-only output, and filtering by owner. 8 | # 9 | # Usage: 10 | # ./list-projects.sh [-c, --csv] [-d, --debug] [-h, --help] 11 | # [-o, --owner-only] [-fo, --filter-owners ] 12 | # 13 | # Arguments: 14 | # -c, --csv Output results in CSV format. 15 | # -d, --debug Enable debug mode to display project labels. 16 | # -h, --help Display help information. 17 | # -o, --owner-only Output only the project owner information. 18 | # -fo, --filter-owners Filter projects by a comma-separated list of owners (adid). 19 | # 20 | # Examples: 21 | # 1. List all projects in CSV format: 22 | # ./list-projects.sh --csv 23 | # 24 | # 2. Display only project owners: 25 | # ./list-projects.sh --owner-only 26 | # 27 | # 3. Enable debug mode: 28 | # ./list-projects.sh --debug 29 | # 30 | # 4. Filter projects by specific owners: 31 | # ./list-projects.sh --filter-owners owner1,owner2 32 | # 33 | # 5. Combine CSV output and owner filter: 34 | # ./list-projects.sh --csv --filter-owners owner1,owner2 35 | # 36 | # ============================================================================= 37 | 38 | source common-constants.inc 39 | source functions.inc 40 | 41 | declare RESULTS=$(gcloud projects list --format="json"); 42 | declare DEBUG="False"; 43 | declare CSV="False"; 44 | declare OWNER_ONLY="False"; 45 | declare FILTER_OWNERS=""; 46 | declare HELP=$(cat << EOL 47 | Usage: $0 [-c, --csv] [-d, --debug] [-h, --help] [-o, --owner-only] [-fo, --filter-owners ] 48 | -c, --csv Output results in CSV format. 49 | -d, --debug Enable debug mode to display project labels. 50 | -h, --help Display help information. 51 | -o, --owner-only Output only the project owner information. 52 | -fo, --filter-owners Filter projects by a comma-separated list of owners (adid). 53 | 54 | Examples: 55 | 1. List all projects in CSV format: 56 | $0 --csv 57 | 58 | 2. Display only project owners: 59 | $0 --owner-only 60 | 61 | 3. Enable debug mode: 62 | $0 --debug 63 | 64 | 4. Filter projects by specific owners: 65 | $0 --filter-owners owner1,owner2 66 | 67 | 5. Combine CSV output and owner filter: 68 | $0 --csv --filter-owners owner1,owner2 69 | EOL 70 | ); 71 | 72 | # Parse long options and convert them to short options for compatibility 73 | for arg in "$@"; do 74 | shift 75 | case "$arg" in 76 | "--help") set -- "$@" "-h" ;; 77 | "--debug") set -- "$@" "-d" ;; 78 | "--csv") set -- "$@" "-c" ;; 79 | "--owner-only") set -- "$@" "-o" ;; 80 | "--filter-owners") set -- "$@" "-f" ;; 81 | *) set -- "$@" "$arg" ;; 82 | esac 83 | done 84 | 85 | # Parse short options 86 | while getopts "hdcof:" option; do 87 | case "${option}" in 88 | d) DEBUG="True" ;; 89 | c) CSV="True" ;; 90 | o) OWNER_ONLY="True" ;; 91 | f) FILTER_OWNERS="${OPTARG}" ;; 92 | h) 93 | echo "$HELP" 94 | exit 0 95 | ;; 96 | esac 97 | done 98 | 99 | # Function to check if a value is in a comma-separated list 100 | is_in_list() { 101 | local value=$1 102 | local list=$2 103 | [[ ",$list," =~ ",$value," ]] 104 | } 105 | 106 | # Check if there are results 107 | if [[ $RESULTS != "[]" ]]; then 108 | # Print CSV headers if CSV option is enabled 109 | if [[ $CSV == "True" ]]; then 110 | if [[ $OWNER_ONLY == "True" ]]; then 111 | echo "\"PROJECT_OWNER\"" 112 | else 113 | echo "\"PROJECT_NAME\",\"PROJECT_APPLICATION_ENTRY_CODE\",\"PROJECT_OWNER\",\"PROJECT_ACC\",\"PROJECT_DEPARTMENT_CODE\",\"PROJECT_PAR\"" 114 | fi 115 | fi 116 | 117 | # Iterate through each project 118 | echo "$RESULTS" | jq -rc '.[]' | while IFS='' read -r PROJECT; do 119 | PROJECT_NAME=$(echo "$PROJECT" | jq -rc '.name') 120 | PROJECT_APPLICATION_ENTRY_CODE=$(echo "$PROJECT" | jq -rc '.labels.app') 121 | PROJECT_OWNER=$(echo "$PROJECT" | jq -rc '.labels.adid') 122 | PROJECT_ACC=$(echo "$PROJECT" | jq -rc '.labels.acc') 123 | PROJECT_DEPARTMENT_CODE=$(echo "$PROJECT" | jq -rc '.labels.dept') 124 | PROJECT_PAR=$(echo "$PROJECT" | jq -rc '.labels.par') 125 | 126 | # Debug output if enabled 127 | if [[ $DEBUG == "True" ]]; then 128 | echo -n "Labels (JSON): " 129 | echo "$PROJECT" | jq -rc '.labels' 130 | fi 131 | 132 | # Filter by owners if FILTER_OWNERS is set 133 | if [[ -n $FILTER_OWNERS ]] && ! is_in_list "$PROJECT_OWNER" "$FILTER_OWNERS"; then 134 | continue # Skip projects not in the filter list 135 | fi 136 | 137 | # CSV Output 138 | if [[ $CSV == "True" ]]; then 139 | if [[ $OWNER_ONLY == "True" ]]; then 140 | echo "\"$PROJECT_OWNER\"" 141 | else 142 | echo "\"$PROJECT_NAME\",\"$PROJECT_APPLICATION_ENTRY_CODE\",\"$PROJECT_OWNER\",\"$PROJECT_ACC\",\"$PROJECT_DEPARTMENT_CODE\",\"$PROJECT_PAR\"" 143 | fi 144 | else 145 | # Standard Output 146 | if [[ $OWNER_ONLY == "True" ]]; then 147 | echo "$PROJECT_OWNER" 148 | else 149 | echo "Project Name: $PROJECT_NAME" 150 | echo "Project Owner: $PROJECT_OWNER" 151 | echo "Project Application Entry Code: $PROJECT_APPLICATION_ENTRY_CODE" 152 | echo "Project ACC: $PROJECT_ACC" 153 | echo "Project Department Code: $PROJECT_DEPARTMENT_CODE" 154 | echo "Project PAR: $PROJECT_PAR" 155 | echo "$BLANK_LINE" 156 | fi 157 | fi 158 | sleep "$SLEEP_SECONDS" 159 | done 160 | else 161 | echo "No projects found" 162 | echo "$BLANK_LINE" 163 | fi 164 | -------------------------------------------------------------------------------- /src/cis-2.2.2-folder-cloud-logging-sinks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare ORGANIZATION_IDS=""; 7 | declare FOLDER_ID=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-o, --organization ORGANIZATION] [-f, --folder FOLDER] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--orgnanization") set -- "$@" "-o" ;; 22 | "--folder") set -- "$@" "-f" ;; 23 | *) set -- "$@" "$arg" 24 | esac 25 | done 26 | 27 | while getopts "hdco:f:" option 28 | do 29 | case "${option}" 30 | in 31 | o) 32 | ORGNAIZATION_IDS=${OPTARG};; 33 | f) 34 | FOLDER_IDS=${OPTARG};; 35 | d) 36 | DEBUG="True";; 37 | c) 38 | CSV="True";; 39 | h) 40 | echo $HELP; 41 | exit 0;; 42 | esac; 43 | done; 44 | 45 | if ! api_enabled logging.googleapis.com; then 46 | echo "WARNING: Logging API is not enabled"; 47 | exit 1000; 48 | fi; 49 | 50 | declare DEFAULT_DEFAULT_LOG_SINK_FILTER="NOT LOG_ID(\"cloudaudit.googleapis.com/activity\") AND NOT LOG_ID(\"externalaudit.googleapis.com/activity\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"externalaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") AND NOT LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 51 | 52 | declare DEFAULT_REQUIRED_LOG_SINK_FILTER="LOG_ID(\"cloudaudit.googleapis.com/activity\") OR LOG_ID(\"externalaudit.googleapis.com/activity\") OR LOG_ID(\"cloudaudit.googleapis.com/system_event\") OR LOG_ID(\"externalaudit.googleapis.com/system_event\") OR LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") OR LOG_ID(\"externalaudit.googleapis.com/access_transparency\")"; 53 | 54 | declare SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE="NOTICE: Google _Default log sink filter is in use"; 55 | 56 | declare SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE="NOTICE: Google _Required log sink filter is in use"; 57 | 58 | declare SINK_FILTER_IS_NOT_DEFAULT_MESSAGE="NOTICE: Custom log sink filter is in use"; 59 | 60 | if [[ $ORGANIZATIONAL_IDS == "" ]]; then 61 | declare ORGANIZATIONS=$(gcloud organizations list --format="json"); 62 | fi; 63 | 64 | if [[ $DEBUG == "True" ]]; then 65 | echo "Organizations (JSON): $ORGANIZATIONS"; 66 | fi; 67 | 68 | if [[ $CSV == "True" ]]; then 69 | echo "\"ORGANIZATION_DISPLAY_NAME\", \"FOLDER_DISPLAY_NAME\", \"SINK_NAME\", \"SINK_DESTINATION\", \"SINK_FILTER_IS_DEFAULT_DEFAULT\", \"SINK_FILTER_IS_REQUIRED_DEFAULT\", \"SINK_FILTER_MESSAGE\", \"SINK_FILTER\""; 70 | fi; 71 | 72 | echo $ORGANIZATIONS | jq -rc '.[]' | while IFS='' read -r ORGANIZATION; do 73 | 74 | ORGANIZATION_NAME=$(echo $ORGANIZATION | jq -rc '.name' | cut -d"/" -f2); 75 | ORGANIZATION_DISPLAY_NAME=$(echo $ORGANIZATION | jq -rc '.displayName'); 76 | 77 | if [[ $CSV != "True" ]]; then 78 | echo "---------------------------------------------------------------------------------"; 79 | echo "Organization $ORGANIZATION_DISPLAY_NAME"; 80 | echo "---------------------------------------------------------------------------------"; 81 | echo $BLANK_LINE; 82 | fi; 83 | 84 | if [[ $FOLDER_IDS == "" ]]; then 85 | declare FOLDERS=$(gcloud resource-manager folders list --format="json" --organization="$ORGANIZATION_NAME"); 86 | fi; 87 | 88 | if [[ $DEBUG == "True" ]]; then 89 | echo "Folders (JSON): $FOLDERS"; 90 | fi; 91 | 92 | echo $FOLDERS | jq -rc '.[]' | while IFS='' read -r FOLDER; do 93 | 94 | FOLDER_NAME=$(echo $FOLDER | jq -rc '.name' | cut -d"/" -f2); 95 | FOLDER_DISPLAY_NAME=$(echo $FOLDER | jq -rc '.displayName'); 96 | 97 | declare SINKS=$(gcloud logging sinks list --format="json" --folder="$FOLDER_NAME"); 98 | 99 | if [[ $DEBUG == "True" ]]; then 100 | echo "Sinks (JSON): $SINKS"; 101 | fi; 102 | 103 | if [[ $CSV != "True" ]]; then 104 | echo "---------------------------------------------------------------------------------"; 105 | echo "Log Sinks for Folder $FOLDER_DISPLAY_NAME"; 106 | echo "---------------------------------------------------------------------------------"; 107 | fi; 108 | 109 | if [[ $SINKS != "[]" ]]; then 110 | 111 | echo $SINKS | jq -rc '.[]' | while IFS='' read -r SINK;do 112 | 113 | if [[ $DEBUG == "True" ]]; then 114 | echo "Log Sink (JSON): $SINK"; 115 | fi; 116 | 117 | SINK_NAME=$(echo $SINK | jq -rc '.name'); 118 | SINK_DESTINATION=$(echo $SINK | jq -rc '.destination'); 119 | SINK_FILTER=$(echo $SINK | jq -rc '.filter'); 120 | SINK_FILTER_IS_DEFAULT_DEFAULT="False"; 121 | SINK_FILTER_IS_REQUIRED_DEFAULT="False"; 122 | 123 | if [[ $SINK_FILTER == $DEFAULT_DEFAULT_LOG_SINK_FILTER ]]; then 124 | SINK_FILTER_IS_DEFAULT_DEFAULT="True"; 125 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_DEFAULT_DEFAULT_MESSAGE; 126 | elif [[ $SINK_FILTER == $DEFAULT_REQUIRED_LOG_SINK_FILTER ]]; then 127 | SINK_FILTER_IS_REQUIRED_DEFAULT="True"; 128 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_REQUIRED_DEFAULT_MESSAGE; 129 | else 130 | SINK_FILTER_MESSAGE=$SINK_FILTER_IS_NOT_DEFAULT_MESSAGE; 131 | fi; 132 | 133 | # Print the results gathered above 134 | if [[ $CSV != "True" ]]; then 135 | echo "Organization: $ORGANIZATION_DISPLAY_NAME"; 136 | echo "Folder: $FOLDER_DISPLAY_NAME"; 137 | echo "Log Sink Name: $SINK_NAME"; 138 | echo "Log Sink Destination: $SINK_DESTINATION"; 139 | echo "Log Sink Filter Message: $SINK_FILTER_MESSAGE"; 140 | echo "Log Sink Filter: $SINK_FILTER"; 141 | echo $BLANK_LINE; 142 | else 143 | echo "\"$ORGANIZATION_DISPLAY_NAME\", \"$FOLDER_DISPLAY_NAME\", \"$SINK_NAME\", \"$SINK_DESTINATION\", \"$SINK_FILTER_IS_DEFAULT_DEFAULT\", \"$SINK_FILTER_IS_REQUIRED_DEFAULT\", \"$SINK_FILTER_MESSAGE\", \"$SINK_FILTER\""; 144 | fi; 145 | 146 | done; #sinks 147 | else 148 | if [[ $CSV != "True" ]]; then 149 | echo "No log sinks found for folder $FOLDER_DISPLAY_NAME"; 150 | echo $BLANK_LINE; 151 | fi; 152 | fi; 153 | sleep $SLEEP_SECONDS; 154 | done; #folders 155 | done; #organizations 156 | 157 | -------------------------------------------------------------------------------- /src/cis-4.4.1-project-level-oslogin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | declare SEPARATOR="---------------------------------------------------------------------------------"; 7 | declare PROJECT_IDS=""; 8 | declare DEBUG="False"; 9 | declare CSV="False"; 10 | declare HELP=$(cat << EOL 11 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 12 | EOL 13 | ); 14 | 15 | for arg in "$@"; do 16 | shift 17 | case "$arg" in 18 | "--help") set -- "$@" "-h" ;; 19 | "--debug") set -- "$@" "-d" ;; 20 | "--csv") set -- "$@" "-c" ;; 21 | "--project") set -- "$@" "-p" ;; 22 | *) set -- "$@" "$arg" 23 | esac 24 | done 25 | 26 | while getopts "hdcp:" option 27 | do 28 | case "${option}" 29 | in 30 | p) 31 | PROJECT_IDS=${OPTARG};; 32 | d) 33 | DEBUG="True";; 34 | c) 35 | CSV="True";; 36 | h) 37 | echo $HELP; 38 | exit 0;; 39 | esac; 40 | done; 41 | 42 | if [[ $PROJECT_IDS == "" ]]; then 43 | declare PROJECT_IDS=$(get_projects); 44 | fi; 45 | 46 | if [[ $DEBUG == "True" ]]; then 47 | echo "Projects: $PROJECT_IDS"; 48 | echo $BLANK_LINE; 49 | fi; 50 | 51 | if [[ $CSV == "True" ]]; then 52 | echo "\"PROJECT_ID\", \"PROJECT_NAME\", \"PROJECT_OWNER\", \"PROJECT_APPLICATION\", \"INSTANCE\", \"INSTANCE_NAME\", \"VIOLATION_FLAG\", \"OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE\""; 53 | fi; 54 | 55 | for PROJECT_ID in $PROJECT_IDS; do 56 | set_project $PROJECT_ID; 57 | 58 | if ! api_enabled compute.googleapis.com; then 59 | if [[ $CSV != "True" ]]; then 60 | echo "COMMENT: Compute Engine API is not enabled for Project $PROJECT_ID."; 61 | fi; 62 | continue 63 | fi 64 | 65 | declare PROJECT_INFO=$(gcloud compute project-info describe --format="json"); 66 | 67 | if [[ $DEBUG == "True" ]]; then 68 | echo "Project Info (JSON): $PROJECT_INFO"; 69 | echo $BLANK_LINE; 70 | fi; 71 | 72 | if [[ $PROJECT_INFO != "" ]]; then 73 | 74 | #Get project details 75 | get_project_details $PROJECT_ID 76 | 77 | # Checking the project level confirguration 78 | OSLOGIN_ENABLED_PROJECT=$(echo $PROJECT_INFO | jq -rc '.commonInstanceMetadata.items[] | with_entries( .value |= ascii_downcase ) | select(.key=="enable-oslogin") | select(.value=="true") // empty' ); 79 | VIOLATION_FLAG="False"; 80 | 81 | if [[ $OSLOGIN_ENABLED_PROJECT == "" ]]; then 82 | OSLOGIN_ENABLED_PROJECT_STATUS_MESSAGE="VIOLATION: OS Login is NOT enabled at the Project level"; 83 | VIOLATION_FLAG="True"; 84 | else 85 | OSLOGIN_ENABLED_PROJECT_STATUS_MESSAGE="COMMENT: OS Login is enabled at the project level, but we need to check if OS Login is enabled at the instance level"; 86 | VIOLATION_FLAG="False"; 87 | fi; 88 | else 89 | if [[ $CSV != "True" ]]; then 90 | echo "No project information found for Project $PROJECT_ID"; 91 | echo $BLANK_LINE; 92 | fi; 93 | fi; 94 | 95 | #Output results for Project level 96 | if [[ $CSV != "True" ]]; then 97 | echo "Project ID: $PROJECT_ID"; 98 | echo "Project Name: $PROJECT_NAME"; 99 | echo "Project Application: $PROJECT_APPLICATION"; 100 | echo "Project Owner: $PROJECT_OWNER"; 101 | echo "Level: Project"; 102 | echo "Project Name: $PROJECT_NAME"; 103 | echo "Status: $OSLOGIN_ENABLED_PROJECT_STATUS_MESSAGE"; 104 | echo $BLANK_LINE; 105 | else 106 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$PROJECT\", \"$PROJECT_NAME\", \"$VIOLATION_FLAG\", \"$OSLOGIN_ENABLED_PROJECT_STATUS_MESSAGE\""; 107 | fi; 108 | 109 | # Checking the instance level configuration 110 | declare INSTANCES=$(gcloud compute instances list --quiet --format="json"); 111 | 112 | if [[ $INSTANCES != "[]" ]]; then 113 | 114 | if [[ $CSV != "True" ]]; then 115 | echo $SEPARATOR; 116 | echo "Instances for Project $PROJECT_ID"; 117 | echo $SEPARATOR; 118 | fi; 119 | 120 | echo $INSTANCES | jq -rc '.[]' | while IFS='' read -r INSTANCE;do 121 | 122 | if [[ $DEBUG == "True" ]]; then 123 | echo "Instance (JSON): $INSTANCE"; 124 | fi; 125 | 126 | INSTANCE_NAME=$(echo $INSTANCE | jq -rc '.name'); 127 | OSLOGIN_ENABLED_INSTANCE=$(echo $INSTANCE | jq -rc '.metadata.items[] | with_entries( .value |= ascii_downcase ) | select(.key=="enable-oslogin") // empty' ); 128 | VIOLATION_FLAG="False"; 129 | 130 | if [[ $OSLOGIN_ENABLED_PROJECT != "" && $OSLOGIN_ENABLED_INSTANCE == "" ]]; then 131 | OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE="PASSED: OS Login is enabled at the project level but not the instance level"; 132 | VIOLATION_FLAG="False"; 133 | elif [[ $OSLOGIN_ENABLED_INSTANCE == "" ]]; then 134 | OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE="COMMENT: Ignoring instance $NAME. OS Login is not enable on this instance."; 135 | VIOLATION_FLAG="False"; 136 | elif [[ $OSLOGIN_ENABLED_INSTANCE != "" ]]; then 137 | if [[ $OSLOGIN_ENABLED_PROJECT != "" ]]; then 138 | OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE="VIOLATION: OS Login is enabled at the project level AND at the instance level. OS Login must be enabled but ONLY at the project level"; 139 | VIOLATION_FLAG="True"; 140 | else 141 | OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE="VIOLATION: OS Login is NOT enabled at the project level but IS enabled at the instance level. OS Login must be enabled but ONLY at the project level"; 142 | VIOLATION_FLAG="True"; 143 | fi; 144 | fi; 145 | 146 | #Output results for Instance level 147 | if [[ $CSV != "True" ]]; then 148 | echo "Project ID: $PROJECT_ID"; 149 | echo "Project Name: $PROJECT_NAME"; 150 | echo "Project Application: $PROJECT_APPLICATION"; 151 | echo "Project Owner: $PROJECT_OWNER"; 152 | echo "Level: Instance"; 153 | echo "Instance Name: $INSTANCE_NAME"; 154 | echo "Status: $OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE"; 155 | echo $BLANK_LINE; 156 | else 157 | echo "\"$PROJECT_ID\", \"$PROJECT_NAME\", \"$PROJECT_OWNER\", \"$PROJECT_APPLICATION\", \"$INSTANCE\", \"$INSTANCE_NAME\", \"$VIOLATION_FLAG\", \"$OSLOGIN_ENABLED_INSTANCE_STATUS_MESSAGE\""; 158 | fi; 159 | 160 | done; 161 | else 162 | if [[ $CSV != "True" ]]; then 163 | echo "COMMENT: No instances found for Project $PROJECT_ID"; 164 | echo $BLANK_LINE; 165 | fi; 166 | fi; 167 | 168 | sleep $SLEEP_SECONDS; 169 | done; 170 | 171 | -------------------------------------------------------------------------------- /src/cis-3.6.1-ssh-firewall-rules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc 4 | source functions.inc 5 | source ./standard-menu.inc 6 | 7 | function csv_escape() { 8 | local raw="$1" 9 | printf '"%s"' "${raw//\"/\"\"}" 10 | } 11 | 12 | function output_header() { 13 | if [[ $CSV == "True" ]]; then 14 | echo "\"Project ID\",\"Project Name\",\"Application\",\"Owner\",\"Rule Name\",\"Direction\",\"Protocols\",\"Ports\",\"Source Ranges\",\"Destination Ranges\",\"Logging Enabled\",\"Disabled\",\"Has Violation\",\"Violation\"" 15 | fi 16 | } 17 | 18 | function emit_csv() { 19 | local violation="$1" 20 | local has_violation="True" 21 | 22 | printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" \ 23 | "$(csv_escape "${PROJECT_ID:-}")" \ 24 | "$(csv_escape "${PROJECT_NAME:-}")" \ 25 | "$(csv_escape "${PROJECT_APPLICATION:-}")" \ 26 | "$(csv_escape "${PROJECT_OWNER:-}")" \ 27 | "$(csv_escape "${NAME:-}")" \ 28 | "$(csv_escape "${DIRECTION:-}")" \ 29 | "$(csv_escape "${PROTOCOL:-}")" \ 30 | "$(csv_escape "${PORTS:-}")" \ 31 | "$(csv_escape "${SOURCE_RANGES:-}")" \ 32 | "$(csv_escape "${DEST_RANGES:-}")" \ 33 | "$(csv_escape "${LOG_CONFIG:-}")" \ 34 | "$(csv_escape "${DISABLED:-}")" \ 35 | "$(csv_escape "$has_violation")" \ 36 | "$(csv_escape "$violation")" 37 | } 38 | 39 | function is_non_rfc1918() { 40 | local cidr="$1" 41 | [[ "$cidr" =~ ^10\. ]] && return 1 42 | [[ "$cidr" =~ ^192\.168\. ]] && return 1 43 | [[ "$cidr" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]] && return 1 44 | return 0 45 | } 46 | 47 | if [[ -z "$PROJECT_ID" ]]; then 48 | PROJECTS=$(gcloud projects list --format="json") 49 | else 50 | PROJECTS=$(gcloud projects list --format="json" --filter="name:$PROJECT_ID") 51 | fi 52 | 53 | if [[ $PROJECTS != "[]" ]]; then 54 | output_header 55 | 56 | echo "$PROJECTS" | jq -rc '.[]' | while IFS='' read -r PROJECT; do 57 | PROJECT_ID=$(echo "$PROJECT" | jq -r '.projectId') 58 | set_project "$PROJECT_ID" 59 | 60 | if ! api_enabled compute.googleapis.com; then 61 | [[ $CSV != "True" ]] && echo "Compute Engine API is not enabled on Project $PROJECT_ID" 62 | continue 63 | fi 64 | 65 | RESULTS=$(gcloud compute firewall-rules list --quiet --format="json" 2>/dev/null || echo "[]") 66 | 67 | if [[ $RESULTS != "[]" ]]; then 68 | get_project_details "$PROJECT_ID" 69 | 70 | echo "$RESULTS" | jq -rc '.[]' | while IFS='' read -r FIREWALL_RULE; do 71 | NAME=$(echo "$FIREWALL_RULE" | jq -rc '.name // empty') 72 | ALLOWED_PROTOCOLS=$(echo "$FIREWALL_RULE" | jq -rc '.allowed // empty') 73 | DENIED_PROTOCOLS=$(echo "$FIREWALL_RULE" | jq -rc '.denied // empty') 74 | DIRECTION=$(echo "$FIREWALL_RULE" | jq -rc '.direction // empty') 75 | LOG_CONFIG=$(echo "$FIREWALL_RULE" | jq -rc '.logConfig.enable // empty') 76 | SOURCE_RANGES=$(echo "$FIREWALL_RULE" | jq -rc '.sourceRanges // "null"') 77 | DEST_RANGES=$(echo "$FIREWALL_RULE" | jq -rc '.destinationRanges // "null"') 78 | DISABLED=$(echo "$FIREWALL_RULE" | jq -rc '.disabled // empty') 79 | 80 | HAS_INTERNET_SOURCE="" 81 | NON_RFC1918_SOURCES=() 82 | 83 | if [[ "$SOURCE_RANGES" != "null" ]]; then 84 | echo "$SOURCE_RANGES" | jq -r '.[]' | while read -r src; do 85 | if [[ "$src" == "0.0.0.0/0" ]]; then 86 | HAS_INTERNET_SOURCE="True" 87 | fi 88 | if is_non_rfc1918 "$src"; then 89 | NON_RFC1918_SOURCES+=("$src") 90 | fi 91 | done 92 | fi 93 | 94 | VIOLATIONS=() 95 | [[ "$LOG_CONFIG" == "false" ]] && VIOLATIONS+=("Firewall logging is not enabled") 96 | 97 | if [[ "$DIRECTION" == "INGRESS" ]]; then 98 | [[ "$NAME" == "default-allow-icmp" ]] && VIOLATIONS+=("Default ICMP rule implemented") 99 | [[ "$NAME" == "default-allow-ssh" ]] && VIOLATIONS+=("Default SSH rule implemented") 100 | [[ "$NAME" == "default-allow-rdp" ]] && VIOLATIONS+=("Default RDP rule implemented") 101 | [[ "$NAME" == "default-allow-internal" ]] && VIOLATIONS+=("Default Internal rule implemented") 102 | 103 | [[ "$DEST_RANGES" == "null" ]] && VIOLATIONS+=("Ingress rule lacks destination or target") 104 | [[ "$ALLOWED_PROTOCOLS" != "" && "$HAS_INTERNET_SOURCE" == "True" ]] && VIOLATIONS+=("Allows access from entire Internet") 105 | 106 | if [[ "$ALLOWED_PROTOCOLS" != "" ]]; then 107 | echo "$ALLOWED_PROTOCOLS" | jq -rc '.[]?' | while IFS='' read -r ALLOWED_PROTOCOL; do 108 | PROTOCOL=$(echo "$ALLOWED_PROTOCOL" | jq -rc '.IPProtocol // "unknown"') 109 | PORTS=$(echo "$ALLOWED_PROTOCOL" | jq -rc '.ports // empty') 110 | PORT_LIST=() 111 | [[ "$PORTS" != "" ]] && PORT_LIST=($(echo "$PORTS" | jq -r '.[]')) 112 | 113 | for port in "${PORT_LIST[@]}"; do 114 | case "$port" in 115 | 1-65535) VIOLATIONS+=("Allows all ports for protocol $PROTOCOL") ;; 116 | 21) VIOLATIONS+=("Rule includes port 21/FTP") ;; 117 | 22) [[ "$HAS_INTERNET_SOURCE" == "True" && "${#NON_RFC1918_SOURCES[@]}" -gt 0 ]] && VIOLATIONS+=("Port 22/SSH from non-RFC1918 source") ;; 118 | 23) VIOLATIONS+=("Rule includes port 23/Telnet") ;; 119 | 25) VIOLATIONS+=("Rule includes port 25/SMTP") ;; 120 | 80) VIOLATIONS+=("Rule includes port 80/HTTP") ;; 121 | 110) VIOLATIONS+=("Rule includes port 110/POP3") ;; 122 | 143) VIOLATIONS+=("Rule includes port 143/IMAP") ;; 123 | 443) VIOLATIONS+=("Rule includes port 443/HTTPS") ;; 124 | 3389) [[ "$HAS_INTERNET_SOURCE" == "True" && "${#NON_RFC1918_SOURCES[@]}" -gt 0 ]] && VIOLATIONS+=("Port 3389/RDP from non-RFC1918 source") ;; 125 | 1433) VIOLATIONS+=("Rule includes port 1433/SQL Server") ;; 126 | 3306) VIOLATIONS+=("Rule includes port 3306/MySQL") ;; 127 | 1521) VIOLATIONS+=("Rule includes port 1521/Oracle") ;; 128 | 5432) VIOLATIONS+=("Rule includes port 5432/PostgreSQL") ;; 129 | esac 130 | done 131 | 132 | if [[ $CSV == "True" ]]; then 133 | for v in "${VIOLATIONS[@]}"; do 134 | emit_csv "$v" 135 | done 136 | else 137 | echo "Project Name: $PROJECT_NAME" 138 | echo "Project Application: $PROJECT_APPLICATION" 139 | echo "Project Owner: $PROJECT_OWNER" 140 | echo "Name: $NAME ($DIRECTION)" 141 | echo "Allowed: $ALLOWED_PROTOCOLS" 142 | echo "Denied: $DENIED_PROTOCOLS" 143 | echo "Source Ranges: $SOURCE_RANGES" 144 | echo "Destination Ranges: $DEST_RANGES" 145 | echo "Logging: $LOG_CONFIG" 146 | echo "Disabled: $DISABLED" 147 | for v in "${VIOLATIONS[@]}"; do echo "VIOLATION: $v"; done 148 | echo 149 | fi 150 | done 151 | fi 152 | fi 153 | done 154 | else 155 | [[ $CSV != "True" ]] && echo "No firewall rules found for $PROJECT_ID" && echo 156 | fi 157 | sleep "${SLEEP_SECONDS:-2}" 158 | done 159 | else 160 | [[ $CSV != "True" ]] && echo "No projects found" && echo 161 | fi 162 | -------------------------------------------------------------------------------- /src/cis-3.9.4-cloud-run-load-balancers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source functions.inc 4 | 5 | # Function to describe backend services 6 | describe_backend_services() { 7 | local BACKEND_SERVICE_NAME=$1 8 | local REGION 9 | 10 | # Attempt to get as a global backend service 11 | BACKEND_SERVICE=$(gcloud compute backend-services describe $BACKEND_SERVICE_NAME --global --format="json" 2>/dev/null) 12 | 13 | # If global backend service not found, try to get regional 14 | if [[ $? -ne 0 ]]; then 15 | # Get all regions 16 | REGIONS=$(gcloud compute regions list --format="value(name)") 17 | 18 | # Loop over regions to find backend service 19 | for REGION in $REGIONS; do 20 | BACKEND_SERVICE=$(gcloud compute backend-services describe $BACKEND_SERVICE_NAME --region="$REGION" --format="json" 2>/dev/null) 21 | if [[ $? -eq 0 ]]; then 22 | echo $BACKEND_SERVICE 23 | break 24 | fi 25 | done 26 | else 27 | echo $BACKEND_SERVICE 28 | fi 29 | }; 30 | 31 | 32 | 33 | # Function to describe url maps (load balancers) 34 | describe_url_maps() { 35 | local LOAD_BALANCER_NAME=$1 36 | local REGION 37 | 38 | # Attempt to get as a global url map 39 | URL_MAP_DETAILS=$(gcloud compute url-maps describe $LOAD_BALANCER_NAME --global --format="json" 2>/dev/null) 40 | 41 | # If global url map not found, try to get regional 42 | if [[ $? -ne 0 ]]; then 43 | # Get all regions 44 | REGIONS=$(gcloud compute regions list --format="value(name)") 45 | 46 | # Loop over regions to find url map 47 | for REGION in $REGIONS; do 48 | URL_MAP_DETAILS=$(gcloud compute url-maps describe $LOAD_BALANCER_NAME --region="$REGION" --format="json" 2>/dev/null) 49 | if [[ $? -eq 0 ]]; then 50 | echo $URL_MAP_DETAILS 51 | break 52 | fi 53 | done 54 | else 55 | echo $URL_MAP_DETAILS 56 | fi 57 | }; 58 | 59 | 60 | # Function to process backend services for a load balancer 61 | processBackendServices() { 62 | local BACKEND_SERVICES="$1" 63 | declare -A PROCESSED_BACKEND_SERVICES 64 | 65 | # Get all regions 66 | REGIONS=$(gcloud compute regions list --format="value(name)") 67 | 68 | echo "Load Balancer Name: $LOAD_BALANCER_NAME" 69 | 70 | # Get Frontend Hosts for HTTP and HTTPS 71 | FRONTEND_HOSTS=$(echo $URL_MAP_DETAILS | jq -r '.hostRules[].hosts[]') 72 | 73 | # Print Frontend URLs 74 | for HOST in $FRONTEND_HOSTS; do 75 | echo "Frontend Load Balancer URL: $HOST" 76 | done 77 | echo $BLANK_LINE; 78 | 79 | 80 | # Check if backend services exist 81 | if [[ -z $BACKEND_SERVICES ]]; then 82 | echo "This load balancer has no backends configured" 83 | else 84 | for BACKEND_SERVICE_NAME in $BACKEND_SERVICES; do 85 | # Skip backend service if it was already processed 86 | if [[ ${PROCESSED_BACKEND_SERVICES[$BACKEND_SERVICE_NAME]} ]]; then 87 | continue 88 | fi 89 | PROCESSED_BACKEND_SERVICES[$BACKEND_SERVICE_NAME]=1 90 | 91 | echo "Backend Service Name: $BACKEND_SERVICE_NAME" 92 | 93 | 94 | # List Network Endpoint Groups here 95 | BACKEND_SERVICE_DETAILS=$(describe_backend_services "$BACKEND_SERVICE_NAME") 96 | 97 | NETWORK_ENDPOINT_GROUPS=$(echo $BACKEND_SERVICE_DETAILS | jq -r '.backends[].group' | awk -F '/' '{print $NF}') 98 | 99 | for NETWORK_ENDPOINT_GROUP in $NETWORK_ENDPOINT_GROUPS; do 100 | echo "Network Endpoint Name: $NETWORK_ENDPOINT_GROUP" 101 | 102 | for REGION in $REGIONS; do 103 | # Describe the NEG, checking each region 104 | NEG_DETAILS=$(gcloud compute network-endpoint-groups describe $NETWORK_ENDPOINT_GROUP --region=$REGION --format=json 2>/dev/null) 105 | if [[ $? -eq 0 ]]; then 106 | 107 | # Here, check if it's a Cloud Run service, print the service name. 108 | IS_CLOUD_RUN=$(echo $NEG_DETAILS | jq -r '.cloudRun') 109 | 110 | if [[ $IS_CLOUD_RUN != "null" ]]; then 111 | SERVICE_NAME=$(echo $NEG_DETAILS | jq -r '.cloudRun.service') 112 | echo "Endpoint Type: Cloud Run" 113 | echo " Service Name: $SERVICE_NAME" 114 | echo " " 115 | else 116 | echo "Not Associated with Cloud Run Service" 117 | echo " " 118 | fi 119 | break 120 | fi 121 | done 122 | done 123 | done 124 | fi 125 | } 126 | 127 | 128 | # Function to check if any load balancer is found in the project for the given load balancer type 129 | check_load_balancer_found() { 130 | local load_balancer_type=$1 131 | local lb_list_var=$2 132 | 133 | if [ "$(echo "$lb_list_var" | jq -rc 'length')" -eq "0" ]; then 134 | if [[ $CSV != "True" ]]; then 135 | echo "No $load_balancer_type load balancer found for Project $PROJECT_ID"; 136 | echo $BLANK_LINE; 137 | fi 138 | return 1 139 | fi 140 | 141 | return 0 142 | } 143 | 144 | declare PROJECT_IDS=""; 145 | declare DEBUG="False"; 146 | declare CSV="False"; 147 | declare HELP=$(cat << EOL 148 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 149 | EOL 150 | ); 151 | 152 | # Parse the script arguments 153 | for arg in "$@"; do 154 | shift 155 | case "$arg" in 156 | "--help") set -- "$@" "-h" ;; 157 | "--debug") set -- "$@" "-d" ;; 158 | "--csv") set -- "$@" "-c" ;; 159 | "--project") set -- "$@" "-p" ;; 160 | *) set -- "$@" "$arg" 161 | esac 162 | done 163 | 164 | # Process the parsed arguments 165 | while getopts "hdcp:" option 166 | do 167 | case "${option}" 168 | in 169 | p) PROJECT_IDS=${OPTARG};; 170 | d) DEBUG="True";; 171 | c) CSV="True";; 172 | h) echo "$HELP"; 173 | exit 0;; 174 | esac; 175 | done 176 | 177 | # If no projects are specified, get all projects 178 | if [[ $PROJECT_IDS == "" ]]; then 179 | declare PROJECT_IDS=$(get_projects); 180 | fi 181 | 182 | # ... 183 | 184 | if [[ $PROJECTS != "[]" ]]; then 185 | # Iterate over each project 186 | for PROJECT_ID in $PROJECT_IDS; do 187 | set_project $PROJECT_ID 188 | 189 | if ! api_enabled run.googleapis.com; then 190 | if [[ $CSV != "True" ]]; then 191 | echo "Cloud Run API is not enabled for Project $PROJECT_ID."; 192 | echo "" 193 | fi 194 | continue 195 | fi 196 | 197 | # Check if Compute Engine API is enabled for the project 198 | if ! api_enabled compute.googleapis.com; then 199 | if [[ $CSV != "True" ]]; then 200 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 201 | echo "" 202 | fi 203 | continue 204 | fi 205 | 206 | 207 | # Get project details 208 | get_project_details $PROJECT_ID 209 | 210 | declare RUN_SERVICES=$(gcloud run services list --format="json"); 211 | if [[ $RUN_SERVICES != "[]" ]]; then 212 | echo "Cloud Run Services for Project $PROJECT_ID"; 213 | 214 | 215 | # Print project information once per project 216 | echo "Project Name: $PROJECT_NAME" 217 | echo "Project Application: $PROJECT_APPLICATION" 218 | echo "Project Owner: $PROJECT_OWNER" 219 | echo "" 220 | 221 | # Get list of HTTP and HTTPS load balancers for the project 222 | HTTP_LOAD_BALANCER_LIST=$(gcloud compute url-maps list --format=json) 223 | 224 | # Check if any HTTP(S) load balancer is found in the project 225 | check_load_balancer_found "HTTP(S)" "$HTTP_LOAD_BALANCER_LIST" 226 | 227 | # Iterate over each HTTP(S) load balancer in the list 228 | if [ $? -eq 0 ]; then 229 | echo "$HTTP_LOAD_BALANCER_LIST" | jq -rc '.[]' | while IFS='' read LOAD_BALANCER; do 230 | LOAD_BALANCER_NAME=$(echo "$LOAD_BALANCER" | jq -rc '.name') 231 | URL_MAP_DETAILS=$(describe_url_maps "$LOAD_BALANCER_NAME") 232 | 233 | # Get all backend services for the load balancer 234 | BACKEND_SERVICES=$(echo $URL_MAP_DETAILS | jq -rc '[.defaultService, .pathMatchers[]?.defaultService?, .pathMatchers[]?.pathRules[]?.service?] | map(select(.!=null))[]' | awk -F '/' '{print $NF}') 235 | 236 | processBackendServices "$BACKEND_SERVICES" 237 | done 238 | fi; 239 | 240 | # Loop over the Cloud Run services and print their names 241 | echo "Cloud Run Service Names:" 242 | echo "$RUN_SERVICES" | jq -rc '.[].metadata.name' 243 | 244 | else 245 | echo "NO Cloud Run Services found for Project $PROJECT_ID" 246 | echo "" 247 | fi; 248 | sleep $SLEEP_SECONDS; 249 | done 250 | else # if no projects 251 | if [[ $CSV != "True" ]]; then 252 | echo "No projects found" 253 | echo $BLANK_LINE; 254 | fi 255 | fi 256 | 257 | -------------------------------------------------------------------------------- /src/utility-who-created-firewall-rule.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script: 4 | # 1. Validates the given GCP project and firewall rule. 5 | # 2. Retrieves and prints project metadata and firewall details from direct gcloud commands. 6 | # 3. Queries Cloud Logging for all matching firewall creation log entries and prints them all, 7 | # using user-defined freshness and method names if provided. 8 | # 9 | # Usage: 10 | # ./utility-who-created-firewall-rule.sh -p -f [options] 11 | # 12 | # Options: 13 | # -p, --project The GCP project ID in which the firewall rule resides. 14 | # -f, --firewall-rule The name of the firewall rule to investigate. 15 | # -c, --csv Output results in CSV format. 16 | # -d, --debug Enable debug output. 17 | # -F, --freshness How far back in time to search logs (e.g. 400d, 48h). Default: 400d. 18 | # -m, --method-names Comma-separated list of method names indicating firewall creation 19 | # (e.g. "compute.firewalls.insert,v1.compute.firewalls.insert"). 20 | # Default: "compute.firewalls.insert,v1.compute.firewalls.insert" 21 | # -h, --help Show this help message. 22 | # 23 | # Dependencies: 24 | # - gcloud CLI configured 25 | # - jq for JSON parsing 26 | # 27 | # Security: 28 | # - This script relies on gcloud for authentication. 29 | # 30 | # Exit codes: 31 | # - 0 on success 32 | # - 1 if required parameters are missing or project/firewall doesn't exist 33 | # - 2 if no creation logs found 34 | 35 | source common-constants.inc 36 | source functions.inc 37 | 38 | PROJECT_NAME="" 39 | FIREWALL_RULE_NAME="" 40 | DEBUG="False" 41 | CSV="False" 42 | FRESHNESS="100d" 43 | METHOD_NAMES="compute.firewalls.insert,v1.compute.firewalls.insert,beta.compute.firewalls.insert" 44 | 45 | HELP=$(cat < -f [options] 47 | 48 | Options: 49 | -p, --project The GCP project ID in which the firewall rule resides. 50 | -f, --firewall-rule The name of the firewall rule to investigate. 51 | -c, --csv Output results in CSV format. 52 | -d, --debug Enable debug output. 53 | -F, --freshness How far back in time to search logs (e.g. 400d, 48h). Default: $FRESHNESS. 54 | -m, --method-names Comma-separated list of method names indicating firewall creation. 55 | Default: $METHOD_NAMES 56 | -h, --help Show this help message. 57 | EOL 58 | ) 59 | 60 | # Parse long arguments first 61 | for arg in "$@"; do 62 | shift 63 | case "$arg" in 64 | "--help") set -- "$@" "-h" ;; 65 | "--debug") set -- "$@" "-d" ;; 66 | "--csv") set -- "$@" "-c" ;; 67 | "--project") set -- "$@" "-p" ;; 68 | "--firewall-rule") set -- "$@" "-f" ;; 69 | "--freshness") set -- "$@" "-F" ;; 70 | "--method-names") set -- "$@" "-m" ;; 71 | *) set -- "$@" "$arg" ;; 72 | esac 73 | done 74 | 75 | while getopts "hdcp:f:F:m:" option; do 76 | case "${option}" in 77 | p) 78 | PROJECT_NAME="${OPTARG}";; 79 | f) 80 | FIREWALL_RULE_NAME="${OPTARG}";; 81 | d) 82 | DEBUG="True";; 83 | c) 84 | CSV="True";; 85 | F) 86 | FRESHNESS="${OPTARG}";; 87 | m) 88 | METHOD_NAMES="${OPTARG}";; 89 | h) 90 | echo "$HELP" 91 | exit 0;; 92 | *) 93 | echo "$HELP" 94 | exit 1;; 95 | esac 96 | done 97 | 98 | if [[ -z "$PROJECT_NAME" || -z "$FIREWALL_RULE_NAME" ]]; then 99 | echo "Error: Both project and firewall rule name must be provided." 100 | echo "$HELP" 101 | exit 1 102 | fi 103 | 104 | [ "$DEBUG" == "True" ] && echo "Debug: PROJECT_NAME=$PROJECT_NAME, FIREWALL_RULE_NAME=$FIREWALL_RULE_NAME, FRESHNESS=$FRESHNESS, METHOD_NAMES=$METHOD_NAMES" 105 | 106 | # Validate the project 107 | PROJECT_DATA=$(gcloud projects describe "$PROJECT_NAME" --format=json 2>/dev/null) 108 | if [[ -z "$PROJECT_DATA" || "$PROJECT_DATA" == "null" ]]; then 109 | echo "Error: The project '$PROJECT_NAME' does not exist or you lack access." 110 | exit 1 111 | fi 112 | 113 | PROJECT_APPLICATION_ENTRY_CODE=$(echo "$PROJECT_DATA" | jq -rc '.labels.app // empty') 114 | PROJECT_OWNER=$(echo "$PROJECT_DATA" | jq -rc '.labels.adid // empty') 115 | PROJECT_ACC=$(echo "$PROJECT_DATA" | jq -rc '.labels.acc // empty') 116 | PROJECT_DEPARTMENT_CODE=$(echo "$PROJECT_DATA" | jq -rc '.labels.dept // empty') 117 | PROJECT_PAR=$(echo "$PROJECT_DATA" | jq -rc '.labels.par // empty') 118 | 119 | # Validate the firewall rule 120 | FIREWALL_DATA=$(gcloud compute firewall-rules describe "$FIREWALL_RULE_NAME" --project="$PROJECT_NAME" --format=json 2>/dev/null) 121 | if [[ -z "$FIREWALL_DATA" || "$FIREWALL_DATA" == "null" ]]; then 122 | echo "Error: The firewall rule '$FIREWALL_RULE_NAME' does not exist in project '$PROJECT_NAME'." 123 | exit 1 124 | fi 125 | 126 | FW_NAME=$(echo "$FIREWALL_DATA" | jq -rc '.name') 127 | FW_NETWORK=$(echo "$FIREWALL_DATA" | jq -rc '.network') 128 | FW_DIRECTION=$(echo "$FIREWALL_DATA" | jq -rc '.direction') 129 | FW_ALLOWED=$(echo "$FIREWALL_DATA" | jq -rc '.allowed') 130 | FW_DENIED=$(echo "$FIREWALL_DATA" | jq -rc '.denied') 131 | FW_SOURCERANGES=$(echo "$FIREWALL_DATA" | jq -rc '.sourceRanges') 132 | FW_SOURCETAGS=$(echo "$FIREWALL_DATA" | jq -rc '.sourceTags') 133 | FW_TARGETTAGS=$(echo "$FIREWALL_DATA" | jq -rc '.targetTags') 134 | FW_CREATION_TIMESTAMP=$(echo "$FIREWALL_DATA" | jq -rc '.creationTimestamp') 135 | 136 | # Extract just the network name from the full URL 137 | FW_NETWORK_NAME=$(echo "$FW_NETWORK" | sed 's#.*/##') 138 | 139 | # Construct the method name filter dynamically 140 | METHOD_FILTER_PART="" 141 | IFS=',' read -r -a METHODS <<< "$METHOD_NAMES" 142 | for m in "${METHODS[@]}"; do 143 | if [[ -z "$METHOD_FILTER_PART" ]]; then 144 | METHOD_FILTER_PART="protoPayload.methodName=\"$m\"" 145 | else 146 | METHOD_FILTER_PART="$METHOD_FILTER_PART OR protoPayload.methodName=\"$m\"" 147 | fi 148 | done 149 | METHOD_FILTER_PART="($METHOD_FILTER_PART)" 150 | 151 | FILTER="resource.type=\"gce_firewall_rule\" AND logName=\"projects/$PROJECT_NAME/logs/cloudaudit.googleapis.com%2Factivity\" AND $METHOD_FILTER_PART AND protoPayload.resourceName=\"projects/$PROJECT_NAME/global/firewalls/$FIREWALL_RULE_NAME\"" 152 | [ "$DEBUG" == "True" ] && echo "Debug: Running gcloud logging read with filter: $FILTER, freshness=$FRESHNESS" 153 | 154 | LOG_RESULTS=$(gcloud logging read "$FILTER" --project="$PROJECT_NAME" --format=json --freshness="$FRESHNESS" 2>/dev/null) 155 | 156 | if [[ -z "$LOG_RESULTS" || "$LOG_RESULTS" == "[]" ]]; then 157 | # No logs found 158 | if [[ $CSV == "True" ]]; then 159 | # Print header anyway if needed 160 | echo "\"PROJECT_NAME\",\"PROJECT_APPLICATION_ENTRY_CODE\",\"PROJECT_OWNER\",\"PROJECT_ACC\",\"PROJECT_DEPARTMENT_CODE\",\"PROJECT_PAR\",\"FIREWALL_RULE_NAME\",\"CREATOR\",\"LOCAL_TIMESTAMP\",\"CALLER_IP\",\"NETWORK\",\"DIRECTION\",\"ALLOWED\",\"DENIED\",\"SOURCE_RANGES\",\"SOURCE_TAGS\",\"TARGET_TAGS\",\"CREATION_TIMESTAMP\"" 161 | fi 162 | exit 2 163 | else 164 | # Process all entries 165 | SORTED_ENTRIES=$(echo "$LOG_RESULTS" | jq -rc 'sort_by(.timestamp) | .[]') 166 | 167 | # Print CSV header if CSV mode 168 | if [[ $CSV == "True" ]]; then 169 | echo "\"PROJECT_NAME\",\"PROJECT_APPLICATION_ENTRY_CODE\",\"PROJECT_OWNER\",\"PROJECT_ACC\",\"PROJECT_DEPARTMENT_CODE\",\"PROJECT_PAR\",\"FIREWALL_RULE_NAME\",\"CREATOR\",\"LOCAL_TIMESTAMP\",\"CALLER_IP\",\"NETWORK\",\"DIRECTION\",\"ALLOWED\",\"DENIED\",\"SOURCE_RANGES\",\"SOURCE_TAGS\",\"TARGET_TAGS\",\"CREATION_TIMESTAMP\"" 170 | fi 171 | 172 | while IFS= read -r ENTRY; do 173 | [ "$DEBUG" == "True" ] && echo "Debug: ENTRY=$ENTRY" 174 | 175 | CREATOR=$(echo "$ENTRY" | jq -rc '.protoPayload.authenticationInfo.principalEmail // "UNKNOWN"') 176 | CALLER_IP=$(echo "$ENTRY" | jq -rc '.protoPayload.requestMetadata.callerIp // "UNKNOWN"') 177 | TIMESTAMP=$(echo "$ENTRY" | jq -rc '.timestamp') 178 | LOCAL_TIMESTAMP=$(date -d "$TIMESTAMP" '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null) 179 | [ -z "$LOCAL_TIMESTAMP" ] && LOCAL_TIMESTAMP="$TIMESTAMP" 180 | 181 | if [[ $CSV == "True" ]]; then 182 | echo "\"$PROJECT_NAME\",\"$PROJECT_APPLICATION_ENTRY_CODE\",\"$PROJECT_OWNER\",\"$PROJECT_ACC\",\"$PROJECT_DEPARTMENT_CODE\",\"$PROJECT_PAR\",\"$FW_NAME\",\"$CREATOR\",\"$LOCAL_TIMESTAMP\",\"$CALLER_IP\",\"$FW_NETWORK_NAME\",\"$FW_DIRECTION\",\"$FW_ALLOWED\",\"$FW_DENIED\",\"$FW_SOURCERANGES\",\"$FW_SOURCETAGS\",\"$FW_TARGETTAGS\",\"$FW_CREATION_TIMESTAMP\"" 183 | else 184 | echo "Project Name: $PROJECT_NAME" 185 | echo "Project Owner: $PROJECT_OWNER" 186 | echo "Project Application Entry Code: $PROJECT_APPLICATION_ENTRY_CODE" 187 | echo "Project ACC: $PROJECT_ACC" 188 | echo "Project Department Code: $PROJECT_DEPARTMENT_CODE" 189 | echo "Project PAR: $PROJECT_PAR" 190 | echo 191 | echo "Firewall Rule Name: $FW_NAME" 192 | echo "Creator: $CREATOR" 193 | echo "Timestamp (Local): $LOCAL_TIMESTAMP" 194 | echo "Caller IP: $CALLER_IP" 195 | echo "Firewall Creation Timestamp: $FW_CREATION_TIMESTAMP" 196 | echo 197 | echo "Firewall Rule Details:" 198 | echo "Network: $FW_NETWORK_NAME" 199 | echo "Direction: $FW_DIRECTION" 200 | echo "Allowed: $FW_ALLOWED" 201 | echo "Denied: $FW_DENIED" 202 | echo "Source Ranges: $FW_SOURCERANGES" 203 | echo "Source Tags: $FWSOURCETAGS" 204 | echo "Target Tags: $FW_TARGETTAGS" 205 | echo 206 | fi 207 | 208 | done <<< "$SORTED_ENTRIES" 209 | fi 210 | 211 | exit 0 212 | -------------------------------------------------------------------------------- /src/cis-3.9.1-load-balancer-tls-policy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | function initializeVariables() { 7 | # Variables are global scope if they are not preceeded by the local keyword 8 | SSL_POLICY_DETAILS=""; 9 | SSL_POLICY_PROFILE=""; 10 | SSL_POLICY_MIN_VERSION=""; 11 | SSL_POLICY_CIPHER_SUITES=""; 12 | IS_IMPLEMENTING_ENCRYPTION="False"; 13 | IS_IMPLEMENTING_ENCRYPTION_MESSAGE="The proxy does not implement transport layer encryption"; 14 | IS_USING_DEFAULT_POLICY="Not Applicable"; 15 | IS_USING_DEFAULT_POLICY_MESSAGE="The proxy does not implement the default TLS policy"; 16 | SSL_POLICY_PROFILE="None"; 17 | IS_USING_SECURE_SSL_POLICY_PROFILE="False"; 18 | IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE="The SSL Policy Profile is insecure"; 19 | IS_SSL_POLICY_MIN_VERSION_SECURE="False"; 20 | SSL_POLICY_MIN_VERSION="None"; 21 | SSL_POLICY_MIN_VERSION_MESSAGE="The TLS Minimum Version is insecure"; 22 | SSL_POLICY_CIPHER_SUITES="None"; 23 | }; 24 | 25 | function debugProjects() { 26 | # Variables are global scope if they are not preceeded by the local keyword 27 | echo "Project ID: $PROJECT_ID"; 28 | echo $BLANK_LINE; 29 | echo "Projects"; 30 | echo $PROJECTS; 31 | echo $BLANK_LINE; 32 | }; 33 | 34 | function debugProxy() { 35 | echo "$PROXY_TYPE (JSON): $PROXY"; 36 | echo $BLANK_LINE; 37 | }; 38 | 39 | function debugSSLPolicy() { 40 | echo "SSL Policy (JSON): $SSL_POLICY"; 41 | echo $BLANK_LINE; 42 | }; 43 | 44 | function debugSSLPolicyDetails() { 45 | echo "SSL Policy Details (JSON): $SSL_POLICY_DETAILS"; 46 | echo $BLANK_LINE; 47 | }; 48 | 49 | function printOutput() { 50 | # Variables are global scope if they are not preceeded by the local keyword 51 | if [[ $CSV != "True" ]]; then 52 | echo "Project Name: $PROJECT_NAME"; 53 | echo "Project Application: $PROJECT_APPLICATION"; 54 | echo "Project Owner: $PROJECT_OWNER"; 55 | echo "Proxy Name: $PROXY_NAME"; 56 | echo "Proxy Type: $PROXY_TYPE"; 57 | echo "Encryption Status: $IS_IMPLEMENTING_ENCRYPTION_MESSAGE"; 58 | echo "Default Policy Status: $IS_USING_DEFAULT_POLICY_MESSAGE"; 59 | echo "TLS Policy Name: $SSL_POLICY_NAME"; 60 | echo "TLS Policy Profile: $SSL_POLICY_PROFILE"; 61 | echo "TLS Policy Status: $IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE"; 62 | echo "TLS Policy Minimum Version: $SSL_POLICY_MIN_VERSION"; 63 | echo "TLS Policy Minimum Version Status: $SSL_POLICY_MIN_VERSION_MESSAGE"; 64 | echo "TLS Ciphers: $SSL_POLICY_CIPHER_SUITES"; 65 | echo $BLANK_LINE; 66 | else 67 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$PROXY_NAME\", \"$PROXY_TYPE\", \"$SSL_POLICY_NAME\", \"$SSL_POLICY_MIN_VERSION\", \"$IS_IMPLEMENTING_ENCRYPTION\", \"$IS_USING_DEFAULT_POLICY\", \"$IS_USING_SECURE_SSL_POLICY_PROFILE\", \"$IS_SSL_POLICY_MIN_VERSION_SECURE\", \"$IS_IMPLEMENTING_ENCRYPTION_MESSAGE\", \"$IS_USING_DEFAULT_POLICY_MESSAGE\", \"$SSL_POLICY_PROFILE\", \"$IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE\", \"$SSL_POLICY_MIN_VERSION_MESSAGE\", \"$SSL_POLICY_CIPHER_SUITES\""; 68 | fi; # end if $CSV != "True" 69 | }; 70 | 71 | function printCSVHeaderRow() { 72 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"PROXY_NAME\", \"PROXY_TYPE\", \"SSL_POLICY_NAME\", \"SSL_POLICY_MIN_VERSION\", \"IS_IMPLEMENTING_ENCRYPTION\", \"IS_USING_DEFAULT_POLICY\", \"IS_USING_SECURE_SSL_POLICY_PROFILE\", \"IS_SSL_POLICY_MIN_VERSION_SECURE\", \"IS_IMPLEMENTING_ENCRYPTION_MESSAGE\", \"IS_USING_DEFAULT_POLICY_MESSAGE\", \"SSL_POLICY_PROFILE\", \"IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE\", \"SSL_POLICY_MIN_VERSION_MESSAGE\", \"SSL_POLICY_CIPHER_SUITES\""; 73 | }; 74 | 75 | function processSSLPolicy() { 76 | # Variables are global scope if they are not preceeded by the local keyword 77 | IS_IMPLEMENTING_ENCRYPTION="True"; 78 | IS_IMPLEMENTING_ENCRYPTION_MESSAGE="The proxy implements transport layer encryption"; 79 | IS_USING_DEFAULT_POLICY="False"; 80 | IS_USING_DEFAULT_POLICY_MESSAGE="The proxy does not implement the default TLS"; 81 | 82 | SSL_POLICY_DETAILS=$(gcloud compute ssl-policies describe --quiet --format="json" $SSL_POLICY); 83 | 84 | if [[ $SSL_POLICY_DETAILS == "" ]]; then 85 | IS_USING_DEFAULT_POLICY="True"; 86 | IS_USING_DEFAULT_POLICY_MESSAGE="The proxy implements the default TLS"; 87 | fi; # end if $DEBUG == "True" 88 | 89 | if [[ $DEBUG == "True" ]]; then 90 | debugSSLPolicyDetails; 91 | fi; # end if $DEBUG == "True" 92 | 93 | SSL_POLICY_NAME=$(echo $SSL_POLICY_DETAILS | jq -rc '.name // empty'); 94 | SSL_POLICY_PROFILE=$(echo $SSL_POLICY_DETAILS | jq -rc '.profile // empty'); 95 | SSL_POLICY_MIN_VERSION=$(echo $SSL_POLICY_DETAILS | jq -rc '.minTlsVersion // empty'); 96 | SSL_POLICY_CIPHER_SUITES=$(echo $SSL_POLICY_DETAILS | jq -rc '.enabledFeatures // empty'); 97 | 98 | if [[ $SSL_POLICY_PROFILE == "COMPATIBLE" ]]; then 99 | IS_USING_SECURE_SSL_POLICY_PROFILE="False"; 100 | IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE="The SSL Policy Profile is insecure"; 101 | fi; 102 | 103 | if [[ $SSL_POLICY_PROFILE == "MODERN" || $SSL_POLICY_PROFILE == "RESTRICTED" ]]; then 104 | IS_USING_SECURE_SSL_POLICY_PROFILE="True"; 105 | IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE="The SSL Policy Profile is secure"; 106 | fi; 107 | 108 | if [[ $SSL_POLICY_PROFILE == "CUSTOM" ]]; then 109 | IS_USING_SECURE_SSL_POLICY_PROFILE="Unknown"; 110 | IS_USING_SECURE_SSL_POLICY_PROFILE_MESSAGE="The SSL Policy Profile may be secure"; 111 | fi; 112 | 113 | if [[ $SSL_POLICY_MIN_VERSION == "TLS_1_2" ]]; then 114 | IS_SSL_POLICY_MIN_VERSION_SECURE="True"; 115 | SSL_POLICY_MIN_VERSION_MESSAGE="The TLS Minimum Version is secure"; 116 | else 117 | IS_SSL_POLICY_MIN_VERSION_SECURE="False"; 118 | SSL_POLICY_MIN_VERSION_MESSAGE="The TLS Minimum Version is insecure"; 119 | fi; # end if $SSL_POLICY_MIN_VERSION == "TLS_1_2" 120 | }; 121 | 122 | declare PROJECT_IDS=""; 123 | declare DEBUG="False"; 124 | declare CSV="False"; 125 | declare HELP=$(cat << EOL 126 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 127 | EOL 128 | ); 129 | 130 | for arg in "$@"; do 131 | shift 132 | case "$arg" in 133 | "--help") set -- "$@" "-h" ;; 134 | "--debug") set -- "$@" "-d" ;; 135 | "--csv") set -- "$@" "-c" ;; 136 | "--project") set -- "$@" "-p" ;; 137 | *) set -- "$@" "$arg" 138 | esac 139 | done 140 | 141 | while getopts "hdcp:" option 142 | do 143 | case "${option}" 144 | in 145 | p) 146 | PROJECT_ID=${OPTARG};; 147 | d) 148 | DEBUG="True";; 149 | c) 150 | CSV="True";; 151 | h) 152 | echo $HELP; 153 | exit 0;; 154 | esac; 155 | done; 156 | 157 | if [[ $PROJECT_ID == "" ]]; then 158 | declare PROJECTS=$(gcloud projects list --format="json"); 159 | else 160 | declare PROJECTS=$(gcloud projects list --format="json" --filter="name:$PROJECT_ID"); 161 | fi; 162 | 163 | if [[ $DEBUG == "True" ]]; then 164 | debugProjects; 165 | fi; 166 | 167 | if [[ $PROJECTS != "[]" ]]; then 168 | 169 | if [[ $CSV == "True" ]]; then 170 | printCSVHeaderRow; 171 | fi; 172 | 173 | echo $PROJECTS | jq -rc '.[]' | while IFS='' read PROJECT;do 174 | 175 | PROJECT_ID=$(echo $PROJECT | jq -r '.projectId'); 176 | 177 | set_project $PROJECT_ID; 178 | 179 | if ! api_enabled compute.googleapis.com; then 180 | if [[ $CSV != "True" ]]; then 181 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 182 | fi; 183 | continue; 184 | fi; 185 | 186 | # Get project details 187 | get_project_details $PROJECT_ID 188 | 189 | PROXY_TYPE="HTTP Load Balancers"; 190 | initializeVariables; 191 | 192 | declare RESULTS=$(gcloud compute target-http-proxies list --quiet --format="json"); 193 | 194 | if [[ $RESULTS != "[]" ]]; then 195 | 196 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r PROXY;do 197 | 198 | if [[ $DEBUG == "True" ]]; then 199 | debugProxy; 200 | fi; # end if $DEBUG == "True" 201 | 202 | PROXY_NAME=$(echo $PROXY | jq -rc '.name'); 203 | printOutput; 204 | 205 | done; # looping through PROXY 206 | 207 | else # there are no results 208 | if [[ $CSV != "True" ]]; then 209 | echo "No $PROXY_TYPE found for $PROJECT_ID"; 210 | echo $BLANK_LINE; 211 | fi; 212 | fi; # end if $RESULTS != "[]" 213 | 214 | 215 | PROXY_TYPE="TCP Load Balancers"; 216 | initializeVariables; 217 | 218 | declare RESULTS=$(gcloud compute target-tcp-proxies list --quiet --format="json"); 219 | 220 | if [[ $RESULTS != "[]" ]]; then 221 | 222 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r PROXY;do 223 | 224 | if [[ $DEBUG == "True" ]]; then 225 | debugProxy; 226 | fi; # end if $DEBUG == "True" 227 | 228 | PROXY_NAME=$(echo $PROXY | jq -rc '.name'); 229 | printOutput; 230 | 231 | done; # looping through PROXY 232 | 233 | else # there are no results 234 | if [[ $CSV != "True" ]]; then 235 | echo "No $PROXY_TYPE found for $PROJECT_ID"; 236 | echo $BLANK_LINE; 237 | fi; 238 | fi; # end if $RESULTS != "[]" 239 | 240 | 241 | PROXY_TYPE="TLS (SSL) Load Balancers"; 242 | initializeVariables; 243 | 244 | declare RESULTS=$(gcloud compute target-ssl-proxies list --quiet --format="json"); 245 | 246 | if [[ $RESULTS != "[]" ]]; then 247 | 248 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r PROXY;do 249 | 250 | if [[ $DEBUG == "True" ]]; then 251 | debugProxy; 252 | fi; # end if $DEBUG == "True" 253 | 254 | PROXY_NAME=$(echo $PROXY | jq -rc '.name'); 255 | SSL_POLICY=$(echo $PROXY | jq -rc '.sslPolicy // empty'); 256 | 257 | if [[ $DEBUG == "True" ]]; then 258 | debugSSLPolicy; 259 | fi; # end if $DEBUG == "True" 260 | 261 | if [[ $SSL_POLICY != "" ]]; then 262 | processSSLPolicy; 263 | fi; # end if $SSL_POLICY == "" 264 | 265 | printOutput; 266 | 267 | done; # looping through PROXY 268 | 269 | else # there are no results 270 | if [[ $CSV != "True" ]]; then 271 | echo "No $PROXY_TYPE found for $PROJECT_ID"; 272 | echo $BLANK_LINE; 273 | fi; 274 | fi; # end if $RESULTS != "[]" 275 | 276 | 277 | PROXY_TYPE="HTTPS Load Balancers"; 278 | initializeVariables; 279 | 280 | declare RESULTS=$(gcloud compute target-https-proxies list --quiet --format="json"); 281 | 282 | if [[ $RESULTS != "[]" ]]; then 283 | 284 | echo $RESULTS | jq -r -c '.[]' | while IFS='' read -r PROXY;do 285 | 286 | if [[ $DEBUG == "True" ]]; then 287 | debugProxy; 288 | fi; # end if $DEBUG == "True" 289 | 290 | PROXY_NAME=$(echo $PROXY | jq -rc '.name'); 291 | SSL_POLICY=$(echo $PROXY | jq -rc '.sslPolicy // empty'); 292 | 293 | if [[ $DEBUG == "True" ]]; then 294 | debugSSLPolicy; 295 | fi; # end if $DEBUG == "True" 296 | 297 | if [[ $SSL_POLICY != "" ]]; then 298 | processSSLPolicy; 299 | fi; # end if $SSL_POLICY == "" 300 | 301 | printOutput; 302 | 303 | done; # looping through PROXY 304 | 305 | else # there are no results 306 | if [[ $CSV != "True" ]]; then 307 | echo "No $PROXY_TYPE found for $PROJECT_ID"; 308 | echo $BLANK_LINE; 309 | fi; 310 | fi; # end if $RESULTS != "[]" 311 | 312 | sleep $SLEEP_SECONDS; 313 | 314 | done; # looping through projects 315 | 316 | else # if no projects 317 | if [[ $CSV != "True" ]]; then 318 | echo "No projects found"; 319 | echo $BLANK_LINE; 320 | fi; 321 | fi; # if projects 322 | 323 | -------------------------------------------------------------------------------- /src/cis-3.9.3-load-balancer-logging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source common-constants.inc; 4 | source functions.inc; 5 | 6 | # Print output for each load balancer and its backend service 7 | function printOutput() { 8 | 9 | if [[ $CSV != "True" ]]; then 10 | echo "Project Name: $PROJECT_NAME"; 11 | echo "Project Application: $PROJECT_APPLICATION"; 12 | echo "Project Owner: $PROJECT_OWNER"; 13 | echo "Load Balancer Name: $LOAD_BALANCER_NAME"; 14 | echo "Backend Service Name: $BACKEND_SERVICE_NAME"; 15 | echo "Logging Status: $IS_LOGGING_ENABLED"; 16 | echo "Logging Status Message: $IS_LOGGING_ENABLED_MESSAGE"; 17 | echo $BLANK_LINE; 18 | else 19 | echo "\"$PROJECT_NAME\", \"$PROJECT_APPLICATION\", \"$PROJECT_OWNER\", \"$LOAD_BALANCER_NAME\", \"$BACKEND_SERVICE_NAME\", \"$IS_LOGGING_ENABLED\", \"$IS_LOGGING_ENABLED_MESSAGE\""; 20 | fi; 21 | }; 22 | 23 | 24 | describe_tcp_proxies() { 25 | local PROXY_NAME=$1 26 | local REGION 27 | 28 | # Attempt to get as a global target TCP proxy 29 | PROXY_DETAILS=$(gcloud compute target-tcp-proxies describe $PROXY_NAME --global --format="json" 2>/dev/null) 30 | 31 | # If global target TCP proxy not found, try to get regional 32 | if [[ $? -ne 0 ]]; then 33 | # Get all regions 34 | REGIONS=$(gcloud compute regions list --format="value(name)") 35 | 36 | # Loop over regions to find target TCP proxy 37 | for REGION in $REGIONS; do 38 | PROXY_DETAILS=$(gcloud compute target-tcp-proxies describe $PROXY_NAME --region="$REGION" --format="json" 2>/dev/null) 39 | if [[ $? -eq 0 ]]; then 40 | echo $PROXY_DETAILS 41 | break 42 | fi 43 | done 44 | else 45 | echo $PROXY_DETAILS 46 | fi 47 | }; 48 | 49 | # Function to describe target SSL proxies (SSL Proxy Load Balancers) 50 | describe_ssl_proxies() { 51 | local PROXY_NAME=$1 52 | local REGION 53 | 54 | # Attempt to get as a global target SSL proxy 55 | PROXY_DETAILS=$(gcloud compute target-ssl-proxies describe $PROXY_NAME --global --format="json" 2>/dev/null) 56 | 57 | # If global target SSL proxy not found, try to get regional 58 | if [[ $? -ne 0 ]]; then 59 | # Get all regions 60 | REGIONS=$(gcloud compute regions list --format="value(name)") 61 | 62 | # Loop over regions to find target SSL proxy 63 | for REGION in $REGIONS; do 64 | PROXY_DETAILS=$(gcloud compute target-ssl-proxies describe $PROXY_NAME --region="$REGION" --format="json" 2>/dev/null) 65 | if [[ $? -eq 0 ]]; then 66 | echo $PROXY_DETAILS 67 | break 68 | fi 69 | done 70 | else 71 | echo $PROXY_DETAILS 72 | fi 73 | }; 74 | 75 | # Function to check if any load balancer is found in the project for the given load balancer type 76 | check_load_balancer_found() { 77 | local load_balancer_type=$1 78 | local lb_list_var=$2 79 | 80 | if [ "$(echo "$lb_list_var" | jq -rc 'length')" -eq "0" ]; then 81 | if [[ $CSV != "True" ]]; then 82 | echo "No $load_balancer_type load balancer found for Project $PROJECT_ID"; 83 | echo $BLANK_LINE; 84 | fi 85 | return 1 86 | fi 87 | 88 | return 0 89 | } 90 | 91 | # Function to describe backend services 92 | describe_backend_services() { 93 | local BACKEND_SERVICE_NAME=$1 94 | local REGION 95 | 96 | # Attempt to get as a global backend service 97 | BACKEND_SERVICE=$(gcloud compute backend-services describe $BACKEND_SERVICE_NAME --global --format="json" 2>/dev/null) 98 | 99 | # If global backend service not found, try to get regional 100 | if [[ $? -ne 0 ]]; then 101 | # Get all regions 102 | REGIONS=$(gcloud compute regions list --format="value(name)") 103 | 104 | # Loop over regions to find backend service 105 | for REGION in $REGIONS; do 106 | BACKEND_SERVICE=$(gcloud compute backend-services describe $BACKEND_SERVICE_NAME --region="$REGION" --format="json" 2>/dev/null) 107 | if [[ $? -eq 0 ]]; then 108 | echo $BACKEND_SERVICE 109 | break 110 | fi 111 | done 112 | else 113 | echo $BACKEND_SERVICE 114 | fi 115 | }; 116 | 117 | # Print debug output 118 | function printDebugOutput() { 119 | echo "Debug: Backend Service Details for $BACKEND_SERVICE_NAME" 120 | BACKEND_SERVICE_JSON=$(describe_backend_services $BACKEND_SERVICE_NAME) 121 | echo $BACKEND_SERVICE_JSON | jq 122 | echo "End of Backend Service Details for $BACKEND_SERVICE_NAME" 123 | echo $BLANK_LINE; 124 | }; 125 | 126 | # Function to describe url maps (load balancers) 127 | describe_url_maps() { 128 | local LOAD_BALANCER_NAME=$1 129 | local REGION 130 | 131 | # Attempt to get as a global url map 132 | URL_MAP_DETAILS=$(gcloud compute url-maps describe $LOAD_BALANCER_NAME --global --format="json" 2>/dev/null) 133 | 134 | # If global url map not found, try to get regional 135 | if [[ $? -ne 0 ]]; then 136 | # Get all regions 137 | REGIONS=$(gcloud compute regions list --format="value(name)") 138 | 139 | # Loop over regions to find url map 140 | for REGION in $REGIONS; do 141 | URL_MAP_DETAILS=$(gcloud compute url-maps describe $LOAD_BALANCER_NAME --region="$REGION" --format="json" 2>/dev/null) 142 | if [[ $? -eq 0 ]]; then 143 | echo $URL_MAP_DETAILS 144 | break 145 | fi 146 | done 147 | else 148 | echo $URL_MAP_DETAILS 149 | fi 150 | }; 151 | 152 | # Function to process backend services for a load balancer 153 | function processBackendServices() { 154 | local BACKEND_SERVICES="$1" 155 | 156 | declare -A PROCESSED_BACKEND_SERVICES 157 | 158 | # Check if backend services exist 159 | if [[ -z $BACKEND_SERVICES ]]; then 160 | BACKEND_SERVICE_NAME="This load balancer has no backends configured" 161 | IS_LOGGING_ENABLED="N/A" 162 | IS_LOGGING_ENABLED_MESSAGE="N/A" 163 | # Print output in CSV format if CSV output is enabled, else print regular output 164 | printOutput 165 | else 166 | for BACKEND_SERVICE_NAME in $BACKEND_SERVICES; do 167 | # Skip backend service if it was already processed 168 | if [[ ${PROCESSED_BACKEND_SERVICES[$BACKEND_SERVICE_NAME]} ]]; then 169 | continue 170 | fi 171 | PROCESSED_BACKEND_SERVICES[$BACKEND_SERVICE_NAME]=1 172 | 173 | # Get all backend services for the load balancer 174 | BACKEND_SERVICE_DETAILS=$(describe_backend_services $BACKEND_SERVICE_NAME) 175 | 176 | if [[ -z "$BACKEND_SERVICE_DETAILS" ]]; then 177 | echo "Could not fetch details for backend service: $BACKEND_SERVICE_NAME" 178 | continue 179 | fi; 180 | 181 | # Check if logging is enabled for the backend service 182 | IS_LOGGING_ENABLED=$(echo $BACKEND_SERVICE_DETAILS | jq -rc '.logConfig.enable // "false"') 183 | if [[ $IS_LOGGING_ENABLED == "true" ]]; then 184 | IS_LOGGING_ENABLED_MESSAGE="The Load Balancer has logging enabled" 185 | else 186 | IS_LOGGING_ENABLED_MESSAGE="VIOLATION: The Load Balancer does not have logging enabled" 187 | fi; 188 | 189 | # Print debug output if debugging is enabled 190 | if [[ $DEBUG == "True" ]]; then 191 | printDebugOutput 192 | fi; 193 | 194 | # Print output in CSV format if CSV output is enabled, else print regular output 195 | printOutput 196 | done 197 | fi; 198 | } 199 | 200 | # Function to print the separator 201 | function printSeparator() { 202 | if [[ $CSV != "True" ]]; then 203 | echo "---------------------------------------------------------------------------------" 204 | fi 205 | } 206 | 207 | declare PROJECT_IDS=""; 208 | declare DEBUG="False"; 209 | declare CSV="False"; 210 | declare HELP=$(cat << EOL 211 | $0 [-p, --project PROJECT] [-c, --csv] [-d, --debug] [-h, --help] 212 | EOL 213 | ); 214 | 215 | # Parse the script arguments 216 | for arg in "$@"; do 217 | shift 218 | case "$arg" in 219 | "--help") set -- "$@" "-h" ;; 220 | "--debug") set -- "$@" "-d" ;; 221 | "--csv") set -- "$@" "-c" ;; 222 | "--project") set -- "$@" "-p" ;; 223 | *) set -- "$@" "$arg" 224 | esac 225 | done 226 | 227 | # Process the parsed arguments 228 | while getopts "hdcp:" option 229 | do 230 | case "${option}" 231 | in 232 | p) PROJECT_IDS=${OPTARG};; 233 | d) DEBUG="True";; 234 | c) CSV="True";; 235 | h) echo "$HELP"; 236 | exit 0;; 237 | esac; 238 | done 239 | 240 | # If no projects are specified, get all projects 241 | if [[ $PROJECT_IDS == "" ]]; then 242 | declare PROJECT_IDS=$(get_projects); 243 | fi 244 | 245 | # Print CSV header if CSV output is enabled 246 | if [[ $CSV == "True" ]]; then 247 | echo "\"PROJECT_NAME\", \"PROJECT_APPLICATION\", \"PROJECT_OWNER\", \"LOAD_BALANCER_NAME\", \"BACKEND_SERVICE_NAME\", \"IS_LOGGING_ENABLED\", \"IS_LOGGING_ENABLED_MESSAGE\""; 248 | fi 249 | 250 | if [[ $PROJECTS != "[]" ]]; then 251 | # Iterate over each project 252 | for PROJECT_ID in $PROJECT_IDS; do 253 | set_project $PROJECT_ID 254 | 255 | # Check if Compute Engine API is enabled for the project 256 | if ! api_enabled compute.googleapis.com; then 257 | if [[ $CSV != "True" ]]; then 258 | echo "Compute Engine API is not enabled for Project $PROJECT_ID."; 259 | fi 260 | continue 261 | fi 262 | 263 | # Get project details 264 | get_project_details $PROJECT_ID 265 | 266 | # Get list of HTTP and HTTPS load balancers for the project 267 | HTTP_LOAD_BALANCER_LIST=$(gcloud compute url-maps list --format=json) 268 | 269 | # Check if any HTTP(S) load balancer is found in the project 270 | check_load_balancer_found "HTTP(S)" "$HTTP_LOAD_BALANCER_LIST" 271 | 272 | # Iterate over each HTTP(S) load balancer in the list 273 | if [ $? -eq 0 ]; then 274 | echo "$HTTP_LOAD_BALANCER_LIST" | jq -rc '.[]' | while IFS='' read LOAD_BALANCER; do 275 | LOAD_BALANCER_NAME=$(echo "$LOAD_BALANCER" | jq -rc '.name') 276 | URL_MAP_DETAILS=$(describe_url_maps "$LOAD_BALANCER_NAME") 277 | 278 | # Get all backend services for the load balancer 279 | BACKEND_SERVICES=$(echo $URL_MAP_DETAILS | jq -rc '[.defaultService, .pathMatchers[]?.defaultService?, .pathMatchers[]?.pathRules[]?.service?] | map(select(.!=null))[]' | awk -F '/' '{print $NF}') 280 | 281 | printSeparator 282 | processBackendServices "$BACKEND_SERVICES" 283 | done 284 | fi; 285 | 286 | # Get list of TCP proxies (TCP Load Balancers) in the project 287 | TCP_PROXY_LIST=$(gcloud compute target-tcp-proxies list --format="json") 288 | 289 | # Check if any TCP load balancer is found in the project 290 | check_load_balancer_found "TCP" "$TCP_PROXY_LIST" 291 | 292 | # Iterate over each TCP load balancer in the list 293 | if [ $? -eq 0 ]; then 294 | echo "$TCP_PROXY_LIST" | jq -rc '.[]' | while IFS='' read LOAD_BALANCER; do 295 | LOAD_BALANCER_NAME=$(echo "$LOAD_BALANCER" | jq -rc '.name') 296 | 297 | # Get all backend services for the TCP load balancer 298 | BACKEND_SERVICES=$(describe_tcp_proxies "$LOAD_BALANCER_NAME" | jq -rc '.proxyHeader' | awk -F '/' '{print $NF}') 299 | 300 | printSeparator 301 | processBackendServices "$BACKEND_SERVICES" 302 | 303 | done 304 | fi; 305 | 306 | # Get list of SSL proxies (SSL Load Balancers) in the project 307 | SSL_PROXY_LIST=$(gcloud compute target-ssl-proxies list --format="json") 308 | 309 | # Check if any SSL load balancer is found in the project 310 | check_load_balancer_found "SSL" "$SSL_PROXY_LIST" 311 | 312 | # Iterate over each SSL load balancer in the list 313 | if [ $? -eq 0 ]; then 314 | echo "$SSL_PROXY_LIST" | jq -rc '.[]' | while IFS='' read LOAD_BALANCER; do 315 | LOAD_BALANCER_NAME=$(echo "$LOAD_BALANCER" | jq -rc '.name') 316 | 317 | # Get all backend services for the SSL load balancer 318 | BACKEND_SERVICES=$(describe_ssl_proxies "$LOAD_BALANCER_NAME" | jq -rc '.proxyHeader' | awk -F '/' '{print $NF}') 319 | 320 | printSeparator 321 | processBackendServices "$BACKEND_SERVICES" 322 | 323 | done 324 | fi; 325 | printSeparator; 326 | 327 | sleep $SLEEP_SECONDS; 328 | done; 329 | else # if no projects 330 | if [[ $CSV != "True" ]]; then 331 | echo "No projects found" 332 | echo $BLANK_LINE; 333 | fi 334 | fi 335 | --------------------------------------------------------------------------------