From 2e134ed9475a9e8207bd6b287ea6265941012717 Mon Sep 17 00:00:00 2001 From: Nacho Rivera <59198746+n4ch04@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:15:14 +0200 Subject: [PATCH] feat(db_connector): Create a PostgreSQL connector for Prowler (#1171) * chore(db providers): db providers first version * chore(db provider): added db provider setup into Readme * fix(csv_line): csv_line out of conditional * fix(README): text instead of varchar in table * fix(help): help message extended Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com> * fix(typo): Update README.md Co-authored-by: Pepe Fagoaga * fix(table): add if not exists Co-authored-by: Pepe Fagoaga * fix(typo): Readme postgreSQL Co-authored-by: Pepe Fagoaga * fix(db_connector): details to add a new provider * fix(typo): Uppercase Prowler Co-authored-by: Toni de la Fuente * fix(prowler): deleted unused variable * chore(checks): test db connector previous to send data * chore(input tests): input tests moved to main * fix(typo): Readme typos * chore(table): table name from pgpass file * fix(grep test): Added missing -E flag * chore(table): check of table name and Readme * chore(error colors): Added error colors * fix(tablename): table name in readme * fix(typo) * fix(db_provider): Exact match * fix(error): One line message * chore(pgpass check): Check added for pgpass file * fix(pgpass): pgpass file and permissions test * fix(unused vars): Deleted unused vars * fix(TOP_PID): Deleted TOP_PID unused var and comment * chore(db tests): Credentials, database and table tests added * fix(empty pgpass): Look for empty fields at pgpass file Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Co-authored-by: Pepe Fagoaga Co-authored-by: Toni de la Fuente --- README.md | 46 +++++++++++++++++++++++++++++- include/db_connector | 40 +++++++++++++++++++++++++++ include/outputs | 34 +++++++++++++++++++++-- prowler | 66 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 176 insertions(+), 10 deletions(-) create mode 100755 include/db_connector diff --git a/README.md b/README.md index 19d5d9c095..6f3fdd1dd8 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,51 @@ Prowler has two parameters related to regions: `-r` that is used query AWS servi ``` ./prowler -h ``` +## Database providers connector + +You can send the Prowler's output to different databases (right now only PostgreSQL is supported). + +Jump into the section for the database provider you want to use and follow the required steps to configure it. +### PostgreSQL +- Install psql + - Mac -> `brew install libpq` + - Ubuntu -> `sudo apt-get install postgresql-client ` + - RHEL/Centos -> `sudo yum install postgresql10` +- Configure a `~/.pgpass` file into the root folder of the user that is going to launch Prowler ([pgpass file doc](https://www.postgresql.org/docs/current/libpq-pgpass.html)), including an extra field at the end of the line, separated by `:`, to name the table, for instance: + `hostname:port:database:username:password:prowler_findings` +- Create a table in your PostgreSQL database to store the Prowler's data. You can use the following SQL statement to create the table: +``` +CREATE TABLE IF NOT EXISTS prowler_findings ( +profile TEXT, +account_number TEXT, +region TEXT, +check_id TEXT, +result TEXT, +item_scored TEXT, +item_level TEXT, +check_title TEXT, +result_extended TEXT, +check_asff_compliance_type TEXT, +severity TEXT, +service_name TEXT, +check_asff_resource_type TEXT, +check_asff_type TEXT, +risk TEXT, +remediation TEXT, +documentation TEXT, +check_caf_epic TEXT, +resource_id TEXT, +prowler_start_time TEXT, +account_details_email TEXT, +account_details_name TEXT, +account_details_arn TEXT, +account_details_org TEXT, +account_details_tags TEXT +); +``` +- Execute Prowler with `-d` flag, for example: + `./prowler -M csv -d postgres` + > *Note*: This command creates a `csv` output file and stores the Prowler output in the configured PostgreSQL DB. It's an example, `-d` flag **does not** require `-M` to run. ## Advanced Usage @@ -490,7 +535,6 @@ Either to run Prowler once or based on a schedule this template makes it pretty The Cloud Formation template that helps you doing that is [here](https://github.com/prowler-cloud/prowler/blob/master/util/codebuild/codebuild-prowler-audit-account-cfn.yaml). > This is a simple solution to monitor one account. For multiples accounts see [Multi Account and Continuous Monitoring](util/org-multi-account/README.md). - ## Allowlist or remove a fail from resources Sometimes you may find resources that are intentionally configured in a certain way that may be a bad practice but it is all right with it, for example an S3 bucket open to the internet hosting a web site, or a security group with an open port needed in your use case. Now you can use `-w allowlist_sample.txt` and add your resources as `checkID:resourcename` as in this command: diff --git a/include/db_connector b/include/db_connector new file mode 100755 index 0000000000..bfa3d6ddbc --- /dev/null +++ b/include/db_connector @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +# DB connectors +# Supported connectors list is stored in SUPPORTED_DB_PROVIDERS +# Once the logic of the connector is added it is needed to add it to the list also +# Example: adding oracle to the list +# SUPPORTED_DB_PROVIDERS="postgresql|oracle" +# oracle_connector() { ... } + +SUPPORTED_DB_PROVIDERS="postgresql" +export SUPPORTED_DB_PROVIDERS + +# Connectors for different databases + +#POSTGRESQL connector +postgresql_connector () { + + CSV_REGISTRY="${1}" + psql -q -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -c "copy ${PSQL_TABLE} from stdin with null as E'\'\'' delimiter ','" <<< "${CSV_REGISTRY}" + +} + +db_exit_abnormally() { + DB_PROVIDER="${1}" + MESSAGE=${2} + + echo "${OPTRED}ERROR!$OPTNORMAL Database connector for '${DB_PROVIDER}' failed. ${MESSAGE}" + exit 2 +} diff --git a/include/outputs b/include/outputs index c839386823..69c5764938 100644 --- a/include/outputs +++ b/include/outputs @@ -44,6 +44,7 @@ HTML_LOGO_IMG="https://github.com/prowler-cloud/prowler/raw/master/util/html/pro TIMESTAMP=$(get_iso8601_timestamp) PROWLER_PARAMETERS=$@ + # Available parameters for outputs formats (implemented this in CSV from v2.4): # $PROFILE profile used to run Prowler (--profile in AWS CLI) @@ -70,6 +71,8 @@ PROWLER_PARAMETERS=$@ # $ACCOUNT_DETAILS_ORG # $ACCOUNT_DETAILS_TAGS + + # Ensure that output directory always exists when -M is used if [[ $MODE ]];then mkdir -p "${OUTPUT_DIR}" @@ -107,9 +110,11 @@ textPass(){ REPREGION=$REGION fi + CSV_LINE="${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" + if [[ "${MODES[@]}" =~ "csv" ]]; then # Writting csv file replacing commas - echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV + echo "${CSV_LINE}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV fi if [[ "${MODES[@]}" =~ "json" ]]; then generateJsonOutput "$1" "Pass" "$CHECK_RESOURCE_ID" >> ${OUTPUT_FILE_NAME}.$EXTENSION_JSON @@ -134,6 +139,13 @@ textPass(){ if [[ "${MODES[@]}" =~ "html" ]]; then generateHtmlOutput "$1" "PASS" fi + + #checking database provider + if [[ ${DATABASE_PROVIDER} == 'postgresql' ]] + then + postgresql_connector "${CSV_LINE}" + fi + } textInfo(){ @@ -151,9 +163,11 @@ textInfo(){ REPREGION=$REGION fi + CSV_LINE="${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" + if [[ "${MODES[@]}" =~ "csv" ]]; then # Writing csv file replacing commas - echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV + echo "${CSV_LINE}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV fi if [[ "${MODES[@]}" =~ "json" ]]; then generateJsonOutput "$1" "Info" "$CHECK_RESOURCE_ID" >> ${OUTPUT_FILE_NAME}.${EXTENSION_JSON} @@ -170,6 +184,12 @@ textInfo(){ if [[ "${MODES[@]}" =~ "html" ]]; then generateHtmlOutput "$1" "INFO" "$CHECK_RESOURCE_ID" fi + + #checking database provider + if [[ ${DATABASE_PROVIDER} == 'postgresql' ]] + then + postgresql_connector "${CSV_LINE}" + fi } textFail(){ @@ -217,9 +237,11 @@ textFail(){ REPREGION=$REGION fi + CSV_LINE="${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" + if [[ "${MODES[@]}" =~ "csv" ]]; then # Writing csv file replacing commas - echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV + echo "${CSV_LINE}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV fi if [[ "${MODES[@]}" =~ "json" ]]; then generateJsonOutput "$1" "${level}" "$CHECK_RESOURCE_ID">> ${OUTPUT_FILE_NAME}.${EXTENSION_JSON} @@ -247,6 +269,12 @@ textFail(){ if [[ "${MODES[@]}" =~ "html" ]]; then generateHtmlOutput "$1" "${level}" "$CHECK_RESOURCE_ID" fi + + #checking database provider + if [[ ${DATABASE_PROVIDER} == 'postgresql' ]] + then + postgresql_connector "${CSV_LINE}" + fi } textTitle(){ diff --git a/prowler b/prowler index 99bf0a48c7..5fecd9ccf4 100755 --- a/prowler +++ b/prowler @@ -46,6 +46,7 @@ TITLE_TEXT="CALLER ERROR - UNSET TITLE" ALLOWLIST_FILE="" TOTAL_CHECKS=() + # Ensures command output will always be set to JSON. # If the default value is already set, ORIGINAL_OUTPUT will be used to store it and reset it at cleanup if [[ -z "${AWS_DEFAULT_OUTPUT}" ]]; then @@ -82,11 +83,11 @@ USAGE: -l List all available checks only (does not perform any check). Add -g to only list checks within the specified group. -L List all groups (does not perform any check). -e Exclude group extras. - -E Execute all tests except a list of specified checks separated by comma. + -E Execute all tests except a list of specified checks separated by comma. (i.e. check21,check31) -b Do not print Prowler banner. -s Show scoring report (it is included by default in the html report). - -S Send check output to AWS Security Hub. Only valid when the output mode is json-asff + -S Send check output to AWS Security Hub. Only valid when the output mode is json-asff (i.e. "-M json-asff -S"). -x Specify external directory with custom checks. S3 URI is supported. (i.e. /my/own/checks or s3://bucket/prefix/checks, files must start by "check"). @@ -112,16 +113,17 @@ USAGE: (i.e.: "-Z check11,check12" will cause check11 and/or check12 to trigger exit code 3) -O Specify AWS Organizations management account ID. Used to get account details, requires -R. (requires organizations:ListAccounts* and organizations:ListTagsForResource) - -a Build your own on-the-fly custom check by specifying the AWS CLI command to execute. Requires "-c extra9999". Omit the "aws" command and only use its parameters within quotes. + -a Build your own on-the-fly custom check by specifying the AWS CLI command to execute. Requires "-c extra9999". Omit the "aws" command and only use its parameters within quotes. Do not nest quotes in the aws parameter. Note that --output text is already included in the check. - i,e. -a 'ec2 describe-security-groups --filters Name=ip-permission.to-port,Values=80 --query SecurityGroups[*].GroupId[]]' + i,e. -a 'ec2 describe-security-groups --filters Name=ip-permission.to-port,Values=80 --query SecurityGroups[*].GroupId[]]' -V Show version number & exit. -h This help. + -d Send output to database through database connectors supported, currently only PostgreSQL. Prowler will get the credentials and table name from your ~/.pgpass file. " exit } -while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:" OPTION; do +while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OPTION; do case $OPTION in h ) usage @@ -234,6 +236,9 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:" OPTI a ) CUSTOM_CMD=$OPTARG ;; + d ) + DATABASE_PROVIDER=$OPTARG + ;; : ) echo "" echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument" @@ -258,7 +263,7 @@ clean_up() { addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML fi # puts the AWS_DEFAULT_OUTPUT back to what it was at the start - if [ -z "$ORIGINAL_OUTPUT"]; then + if [ -z "$ORIGINAL_OUTPUT" ]; then export AWS_DEFAULT_OUTPUT="$ORIGINAL_OUTPUT" else unset AWS_DEFAULT_OUTPUT @@ -301,6 +306,10 @@ unset AWS_DEFAULT_OUTPUT . $PROWLER_DIR/include/organizations_metadata . $PROWLER_DIR/include/custom_checks . $PROWLER_DIR/include/allowlist +. $PROWLER_DIR/include/db_connector + + + # Parses the check file into CHECK_ID's. if [[ -n "$CHECK_FILE" ]]; then @@ -315,6 +324,51 @@ if [[ -n "$CHECK_FILE" ]]; then fi fi +# Check database providers +if [[ ${DATABASE_PROVIDER} ]] +then + # Check if input provider is supported + if ! grep -w -q -E "${DATABASE_PROVIDER}" <<< "${SUPPORTED_DB_PROVIDERS}" + then + db_exit_abnormally "${DATABASE_PROVIDER}" "DB provider not supported, providers supported: ${SUPPORTED_DB_PROVIDERS} - EXITING!" + elif ! command -v "psql" > /dev/null 2>&1 + then + db_exit_abnormally "postgresql" "psql tool not installed or not found- EXITING!" + elif [[ $(find "${HOME}/.pgpass" -perm 600 2>/dev/null) != "$HOME/.pgpass" ]] + then + db_exit_abnormally "postgresql" ".pgpass file not found at $HOME/.pgpass or .pgpass file permissions not matching 600 - EXITING!" + else + IFS=':' read -r PSQL_HOSTNAME PSQL_PORT PSQL_DATABASE PSQL_USER PSQL_PASSWORD PSQL_TABLE < "$HOME/.pgpass" + if [[ ! ${PSQL_HOSTNAME} ]] || [[ ! ${PSQL_PORT} ]] || [[ ! ${PSQL_DATABASE} ]] || [[ ! ${PSQL_USER} ]] || [[ ! ${PSQL_PASSWORD} ]] || [[ ! ${PSQL_TABLE} ]] + then + db_exit_abnormally "postgresql" "Empty field into ${HOME}/.pgpass file, all fields must be filled. Please check Prowler README about .pgpass file format - EXITING!" + fi + export PSQL_USER + export PSQL_HOSTNAME + export PSQL_TABLE + + # Once all the variables are defined and exported test if database instance is reachable + if ! pg_isready -q -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -p "${PSQL_PORT}" + then + db_exit_abnormally "postgresql" "Database listening on host ${PSQL_HOSTNAME} on port ${PSQL_PORT} connected as user ${PSQL_USER} is unreachable - EXITING!" + # If database instance is ready time to check credentials + elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -c "\q" > /dev/null 2>&1 + then + db_exit_abnormally "postgresql" "User ${PSQL_USER} invalid or invalid credentials, please check ${HOME}/.pgpass file - EXITING!" + # If credentials are ok -> database exists? + elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "\q" > /dev/null 2>&1 + then + db_exit_abnormally "postgresql" "Database not exists, please check ${HOME}/.pgpass file - EXITING!" + # and finally, if database exists -> table exists ? + elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "SELECT * FROM ${PSQL_TABLE};" > /dev/null 2>&1 + then + db_exit_abnormally "postgresql" "Table ${PSQL_TABLE} not exists, please check ${HOME}/.pgpass file - EXITING!" + fi + + + fi +fi + # Pre-process allowlist file if supplied if [[ -n "$ALLOWLIST_FILE" ]]; then allowlist