diff --git a/scripts/azure-sql/lib_firewall.sh b/scripts/azure-sql/lib_firewall.sh index a428aa9d1..e80a9cc80 100755 --- a/scripts/azure-sql/lib_firewall.sh +++ b/scripts/azure-sql/lib_firewall.sh @@ -5,7 +5,7 @@ add_local_computer_sql_firewall_rule() { resourceGroup=$2 ruleName=$3 - myip=$(curl http://ifconfig.me/ip) || + myip=$(curl http://ifconfig.me/ip --silent) || { echo "ERROR: Failed to get IP address." >&2; return 1; } az sql server firewall-rule create \ @@ -31,4 +31,4 @@ delete_sql_firewall_rule() { --output none \ --only-show-errors || { echo "ERROR: Failed to delete firewall rule $ruleName." >&2; return 1; } -} \ No newline at end of file +} diff --git a/scripts/azure-sql/lib_security.sh b/scripts/azure-sql/lib_security.sh index 3f988c4dc..2e2c51fca 100755 --- a/scripts/azure-sql/lib_security.sh +++ b/scripts/azure-sql/lib_security.sh @@ -71,12 +71,10 @@ generate_password_and_store() { create_or_update_sql_user() { local serverName=$1 - local connectLoginName=$2 # A SQL login with permissions to manager logins and users - local connectPassword=$3 # Password for $connectLoginName - local databaseName=$4 # Database name where user should be created - local userName=$5 # Database user name to map to login - local password=$6 # Password to set for $loginName - local roles=$7 # Comma separated list of database role names to add the user to + local databaseName=$2 # Database name where user should be created + local userName=$3 # Database user name to map to login + local password=$4 # Password to set for $loginName + local roles=$5 # Comma separated list of database role names to add the user to local script_dir_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [[ -z $serverName ]]; then @@ -84,11 +82,6 @@ create_or_update_sql_user() { return 1 fi - if [[ -z $connectLoginName ]]; then - echo "ERROR: connectLoginName not set" >&2 - return 1 - fi - if [[ -z $databaseName ]]; then echo "ERROR: databaseName not set" >&2 return 1 @@ -105,10 +98,9 @@ create_or_update_sql_user() { fi userName=$userName password=$password roles=$roles sqlcmd -b \ + --authentication-method ActiveDirectoryDefault \ -S $serverName \ -d $databaseName \ - -U $connectLoginName \ - -P $connectPassword \ -i "${script_dir_path}/create_or_update_user.sql" \ || { echo "ERROR: Could not update SQL user." >&2; return 1; } -} \ No newline at end of file +} diff --git a/scripts/cost-allocation/bootstrap_api.sh b/scripts/cost-allocation/bootstrap_api.sh index b52a3fa90..3d1f0d79d 100755 --- a/scripts/cost-allocation/bootstrap_api.sh +++ b/scripts/cost-allocation/bootstrap_api.sh @@ -168,7 +168,7 @@ echo "Generate password for API SQL user and store in KV" generate_password_and_store $AZ_RESOURCE_KEYVAULT $KV_SECRET_COST_ALLOCATION_DB_API $REGENERATE_SQL_PASSWORD || exit API_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_DB_API | jq -r .value) -ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_SQL_ADMIN | jq -r .value) +ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_SQL_ADMIN | jq -r .value) if [[ -z $ADMIN_SQL_PASSWORD ]]; then printf "ERROR: SQL admin password not set" >&2 @@ -187,8 +187,6 @@ add_local_computer_sql_firewall_rule \ echo "Creating/updating SQL user for Radix Cost Allocation API" create_or_update_sql_user \ $COST_ALLOCATION_SQL_SERVER_FQDN \ - $COST_ALLOCATION_SQL_ADMIN_LOGIN \ - $ADMIN_SQL_PASSWORD \ $COST_ALLOCATION_SQL_DATABASE_NAME \ $COST_ALLOCATION_SQL_API_USER \ $API_SQL_PASSWORD \ @@ -201,4 +199,4 @@ delete_sql_firewall_rule \ $whitelistRuleName \ || exit -echo "Done." \ No newline at end of file +echo "Done." diff --git a/scripts/cost-allocation/bootstrap_collector.sh b/scripts/cost-allocation/bootstrap_collector.sh index ebd278bb9..51abc7c31 100755 --- a/scripts/cost-allocation/bootstrap_collector.sh +++ b/scripts/cost-allocation/bootstrap_collector.sh @@ -202,7 +202,7 @@ generate_password_and_store $AZ_RESOURCE_KEYVAULT $KV_SECRET_COST_ALLOCATION_DB_ # Create/update SQL user and roles COLLECTOR_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_DB_WRITER | jq -r .value) -ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_SQL_ADMIN | jq -r .value) +ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_SQL_ADMIN | jq -r .value) if [[ -z $ADMIN_SQL_PASSWORD ]]; then printf "ERROR: SQL admin password not set" @@ -221,8 +221,6 @@ add_local_computer_sql_firewall_rule \ echo "Creating/updating SQL user for Radix Cost Allocation" create_or_update_sql_user \ $COST_ALLOCATION_SQL_SERVER_FQDN \ - $COST_ALLOCATION_SQL_ADMIN_LOGIN \ - $ADMIN_SQL_PASSWORD \ $COST_ALLOCATION_SQL_DATABASE_NAME \ $COST_ALLOCATION_SQL_COLLECTOR_USER \ $COLLECTOR_SQL_PASSWORD \ @@ -241,7 +239,7 @@ echo "Install Radix Cost Allocation resources for flux" SQL_DB_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_COST_ALLOCATION_DB_WRITER | jq -r .value) || { echo "ERROR: Could not get secret '${KV_SECRET_COST_ALLOCATION_DB_WRITER}' in '${AZ_RESOURCE_KEYVAULT}'." >&2; exit; } -echo "db: +echo "db: server: ${COST_ALLOCATION_SQL_SERVER_FQDN} database: ${COST_ALLOCATION_SQL_DATABASE_NAME} user: ${COST_ALLOCATION_SQL_COLLECTOR_USER} @@ -249,15 +247,15 @@ echo "db: kubectl create ns radix-cost-allocation --dry-run=client --save-config -o yaml | kubectl apply -f - - + kubectl create secret generic cost-db-secret --namespace radix-cost-allocation \ --from-file=./radix-cost-allocation-values.yaml \ --dry-run=client -o yaml | kubectl apply -f - flux reconcile helmrelease --namespace radix-cost-allocation radix-cost-allocation -kubectl rollout restart deployment radix-cost-allocation --namespace radix-cost-allocation +kubectl rollout restart deployment radix-cost-allocation --namespace radix-cost-allocation rm -f radix-cost-allocation-values.yaml -echo "Done." \ No newline at end of file +echo "Done." diff --git a/scripts/radix-zone/radix_zone_dev.env b/scripts/radix-zone/radix_zone_dev.env index 3b4ae222d..e0391caa6 100644 --- a/scripts/radix-zone/radix_zone_dev.env +++ b/scripts/radix-zone/radix_zone_dev.env @@ -184,10 +184,10 @@ RADIX_API_REQUIRE_APP_AD_GROUPS=true ### Radix Vulnerability Scanner Environment ### -AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL=vulnerability-scan +AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL="vulnerability-scan-$RADIX_ZONE" KV_SECRET_VULNERABILITY_SCAN_SQL_ADMIN=radix-vulnerability-scan-db-admin -KV_SECRET_VULNERABILITY_SCAN_DB_WRITER="radix-vulnerability-scan-db-writer-$RADIX_ZONE" -KV_SECRET_VULNERABILITY_SCAN_DB_API="radix-vulnerability-scan-db-api-$RADIX_ZONE" +KV_SECRET_VULNERABILITY_SCAN_DB_WRITER="radix-vulnerability-scan-db-writer" +KV_SECRET_VULNERABILITY_SCAN_DB_API="radix-vulnerability-scan-db-api" VULNERABILITY_SCAN_SQL_SERVER_NAME="sql-radix-vulnerability-scan-$RADIX_ZONE" VULNERABILITY_SCAN_SQL_SERVER_FQDN="$VULNERABILITY_SCAN_SQL_SERVER_NAME.database.windows.net" VULNERABILITY_SCAN_SQL_DATABASE_NAME=radix-vulnerability-scan @@ -252,4 +252,4 @@ RADIX_ERROR_PAGE=radix_error_page.html ### LETS_ENCRYPT_ACME_ACCOUNT_EMAIL=Radix@StatoilSRM.onmicrosoft.com -DIGICERT_EXTERNAL_ACCOUNT_KV_SECRET="digicert-external-account-$RADIX_ZONE" \ No newline at end of file +DIGICERT_EXTERNAL_ACCOUNT_KV_SECRET="digicert-external-account-$RADIX_ZONE" diff --git a/scripts/rotate-secrets/README.md b/scripts/rotate-secrets/README.md new file mode 100644 index 000000000..371980b2e --- /dev/null +++ b/scripts/rotate-secrets/README.md @@ -0,0 +1,3 @@ +# Rotating Secrets + +This tool looks for all soon to be, or expired secrets and let you rotate them. diff --git a/scripts/rotate-secrets/lib_keyvault.sh b/scripts/rotate-secrets/lib_keyvault.sh new file mode 100644 index 000000000..e4d8e11b5 --- /dev/null +++ b/scripts/rotate-secrets/lib_keyvault.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +red=$'\e[1;31m' +grn=$'\e[1;32m' +yel=$'\e[1;33m' +normal=$(tput sgr0) + + +keyvault_secret_exist() { + local keyvault=$1 + local secretName=$2 + + secret_exists=$(az keyvault secret list --vault-name $keyvault --query "contains([].id, 'https://$keyvault.vault.azure.net/secrets/$secretName')" 2> /dev/null) + if [ $secret_exists == "true" ]; then + echo 0 + else echo 1; + fi; +} + +keyvault_secret_ttl_days() { + local keyvault=$1 + local secretName=$2 + + local expiry=$(az keyvault secret show --vault-name $keyvault --name $secretName 2> /dev/null | jq '.attributes.expires' -r) + local dateNow=$(date +%s) + local dateExpires=$(date -d $expiry +%s) + local days="$((($dateExpires-$dateNow)/86400))" + + echo $days +} + +keyvault_secret_save() { + local keyvault=$1 + local secretName=$2 + local value=$3 + + local expiry=$(date -d "+365 days" -u +"%Y-%m-%dT%H:%M:%SZ") + local notBefore=$(date -d "-5 minutes" -u +"%Y-%m-%dT%H:%M:%SZ") + az keyvault secret set --vault-name "${keyvault}" --name "${secretName}" --value "${value}" --not-before "${notBefore}" --expires "${expiry}" --output none --only-show-errors || + { echo "ERROR: Could not get secret '$secretName' in '${keyvault}'." >&2; return 1; } +} + +keyvault_list_secrets() { + local keyvault=$1 + local warning=$2 + local fmt="%-50s %s%-20s%s\n" + printf "${fmt}" "Secret" "Days TTL" $normal $normal + while read NAME EXPDATE; do + local dateNow=$(date +%s) + local dateExpires=$(date -d "${EXPDATE}" +%s) + local days="$((($dateExpires-$dateNow)/86400))" + local color=$normal + + if [ $days -le 31 ]; then + color=$yel + fi; + if [ $days -le 7 ]; then + color=$red + fi; + + printf "${fmt}" "${NAME}" $color "${days}" $normal + done < <(az keyvault secret list --vault-name radix-keyv-dev | jq ".[] | [.name, .attributes.expires] | @tsv" -r) +} diff --git a/scripts/rotate-secrets/rotate-secrets.sh b/scripts/rotate-secrets/rotate-secrets.sh new file mode 100755 index 000000000..6af3c03f0 --- /dev/null +++ b/scripts/rotate-secrets/rotate-secrets.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +red=$'\e[1;31m' +grn=$'\e[1;32m' +yel=$'\e[1;33m' +normal=$(tput sgr0) + +####################################################################################### +### PURPOSE +### + +# This tool looks for all soon to be, or expired secrets and let you rotate them. + +####################################################################################### +### INPUTS +### + +# Required: +# - RADIX_ZONE_ENV : Path to *.env file +# - CLUSTER_NAME : Ex: "test-2", "weekly-93" + +# Optional: +# - UPDATE_SECRETS : Rotate expired secrets. Defaults to false. +# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. + +####################################################################################### +### HOW TO USE +### + +# NORMAL +# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env CLUSTER_NAME=weekly-07 UPDATE_SECRETS=true ./rotate-secrets.sh + +####################################################################################### +### Check for prerequisites binaries +### + +printf "Loading dependencies... " +source "${RADIX_ZONE_ENV:-'!!!! No RADIX_ZONE_ENV Provided !!!!'}" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/util.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_firewall.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/rotate-secrets/lib_keyvault.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_security.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/lib_dependencies.sh" +printf "Done.\n" + +assert_cli_tools || exit 1 +has_env_name "CLUSTER_NAME" || exit 1 +prepare_azure_session || exit 1 +setup_cluster_access "$AZ_RESOURCE_GROUP_CLUSTERS" "$CLUSTER_NAME" || + { echo "ERROR: Unable to connect to cluster" >&2; exit 1; } + + +####################################################################################### +### Set default values for optional input +### + +USER_PROMPT=${USER_PROMPT:=true} +UPDATE_SECRETS=${UPDATE_SECRETS:=true} +KEY_VAULT="radix-keyv-${RADIX_ZONE}" +if [[ "${RADIX_ZONE}" == "prod" ]]; then + KEY_VAULT="radix-keyv-platform" +fi; + + +####################################################################################### +### Ask user to verify inputs and az login +### + +echo -e "" +echo -e "Bootstrap Radix Vulnerability Scanner and API with the following configuration:" +echo -e "" +echo -e " > WHERE:" +echo -e " ------------------------------------------------------------------" +echo -e " - RADIX_ZONE : $RADIX_ZONE" +echo -e " - CLUSTER_NAME : $CLUSTER_NAME" +echo -e " - KEY_VAULT : $KEY_VAULT" +echo -e "" +echo -e " > WHAT:" +echo -e " -------------------------------------------------------------------" +echo -e " - UPDATE_SECRETS : $UPDATE_SECRETS" +echo -e "" +echo -e " > WHO:" +echo -e " -------------------------------------------------------------------" +echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" +echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" +echo -e "" + +echo "" + +user_prompt_continue || exit 1 + +####################################################################################### +### Start +### + +printf "initializing...\n" +printf "Getting public ip... " +myip=$(curl http://ifconfig.me/ip 2> /dev/null) || +{ echo "ERROR: Failed to get IP address." >&2; exit 1; } +printf "Done.\n" + +printf "Adding %s to %s firewall... " $myip $KEY_VAULT +az keyvault network-rule add --name "${KEY_VAULT}" --ip-address "$myip" --only-show-errors > /dev/null +printf "Done.\n" + +keyvault_list_secrets "${KEY_VAULT}" "31" +printf "%s► Running scripts... %s%s\n" "${grn}" "$script" "${normal}" + +scripts=`ls ./services/*.sh` +for script in $scripts +do + + printf "%s► Execute %s%s\n" "${grn}" "$script" "${normal}" + + (RADIX_ZONE_ENV=${RADIX_ZONE_ENV} CLUSTER_NAME=${CLUSTER_NAME} UPDATE_SECRETS=${UPDATE_SECRETS} KEY_VAULT=${KEY_VAULT} USER_PROMPT=false source $script) + status=$? + if [ $status -ne 0 ]; then + printf "%s💥 Exited with code: %d %s\n" ${red} $status ${normal} + else + printf "%s► %s Completed %s\n" ${grn} ${script} ${normal} + fi; +done + +keyvault_list_secrets "${KEY_VAULT}" "31" + +printf "\n%s► Cleaning up... %s\n" "${grn}" "${normal}" +printf "Removing %s to %s firewall... " $myip $KEY_VAULT +az keyvault network-rule add --name "${KEY_VAULT}" --ip-address "$myip" --only-show-errors > /dev/null +printf "Done.\n" diff --git a/scripts/rotate-secrets/services/vulnerability-scan-reader.sh b/scripts/rotate-secrets/services/vulnerability-scan-reader.sh new file mode 100755 index 000000000..0bd310ac8 --- /dev/null +++ b/scripts/rotate-secrets/services/vulnerability-scan-reader.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +red=$'\e[1;31m' +grn=$'\e[1;32m' +yel=$'\e[1;33m' +normal=$(tput sgr0) + +####################################################################################### +### PURPOSE +### + +# Rotates secret for radix-vulnerability-scanner's reader user in a radix cluster + +####################################################################################### +### PRECONDITIONS +### + +# - AKS cluster is available +# - User has role cluster-admin + +####################################################################################### +### INPUTS +### + +# Required: +# - RADIX_ZONE_ENV : Path to *.env file +# - CLUSTER_NAME : Ex: "test-2", "weekly-93" + +# Optional: +# - UPDATE_SECRETS : Rotate expired secrets. Defaults to false. +# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. + +####################################################################################### +### HOW TO USE +### + +# NORMAL +# RADIX_ZONE_ENV=../../radix-zone/radix_zone_dev.env CLUSTER_NAME=weekly-07 UPDATE_SECRETS=false ./vulnerability-scan-reader.sh + +####################################################################################### +### Check dependencies +### + +echo "" +printf "Start rotating secrets for radix-vulnerability-scanner reader... \n" + +printf "Loading dependencies... " +source "${RADIX_ZONE_ENV:-'!!!! No RADIX_ZONE_ENV Provided !!!!'}" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/util.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_firewall.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/rotate-secrets/lib_keyvault.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_security.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/lib_dependencies.sh" +printf "Done.\n" + +assert_cli_tools || exit 1 +has_env_name "CLUSTER_NAME" || exit 1 +prepare_azure_session || exit 1 +setup_cluster_access "$AZ_RESOURCE_GROUP_CLUSTERS" "$CLUSTER_NAME" || + { echo "ERROR: Unable to connect to cluster" >&2; exit 1; } + +USER_PROMPT=${USER_PROMPT:=true} +UPDATE_SECRETS=${UPDATE_SECRETS:=false} + +KEY_VAULT="radix-keyv-${RADIX_ZONE}" +if [[ "${RADIX_ZONE}" == "prod" ]]; then + KEY_VAULT="radix-keyv-platform" +fi; + +echo -e "" +echo -e "Bootstrap Radix Vulnerability Scanner with the following configuration:" +echo -e "" +echo -e " > WHERE:" +echo -e " ------------------------------------------------------------------" +echo -e " - RADIX_ZONE : $RADIX_ZONE" +echo -e " - CLUSTER_NAME : $CLUSTER_NAME" +echo -e " - KEY_VAULT : $KEY_VAULT" +echo -e " - VULNERABILITY_SCAN_SQL_SERVER_NAME : $VULNERABILITY_SCAN_SQL_SERVER_NAME" +echo -e " - VULNERABILITY_SCAN_SQL_DATABASE_NAME : $VULNERABILITY_SCAN_SQL_DATABASE_NAME" +echo -e "" +echo -e " > WHAT:" +echo -e " ------------------------------------------------------------------" +echo -e " - UPDATE_SECRETS : $UPDATE_SECRETS" +echo -e " - DB_USER : $VULNERABILITY_SCAN_SQL_API_USER" +echo -e " - SECRET : $KV_SECRET_VULNERABILITY_SCAN_DB_API" +echo -e "" +echo -e " > WHO:" +echo -e " -------------------------------------------------------------------" +echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" +echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" +echo -e "" + +echo "" + +user_prompt_continue || exit 1 + + +####################################################################################### +### Control Secret +### + +secretShouldUpdate=false + +secretExists=$(keyvault_secret_exist ${KEY_VAULT} "${KV_SECRET_VULNERABILITY_SCAN_DB_API}test") + +if [ $secretExists -eq 1 ]; then + secretShouldUpdate=true + printf "%sSecret %s missing, should be created%s\n" ${yel} ${KV_SECRET_VULNERABILITY_SCAN_DB_API} ${normal} +elif [ $secretExists -eq 0 ]; then + expiry=$(keyvault_secret_ttl_days ${KEY_VAULT} ${KV_SECRET_VULNERABILITY_SCAN_DB_API}) + if [ $expiry -le 31 ]; then + secretShouldUpdate=true + printf "%sSecret %s expires in %s days, should be updated %s \n" ${yel} ${KV_SECRET_VULNERABILITY_SCAN_DB_API} ${expiry} ${normal} + fi; +else + printf "Unknown response from secret exists: %s\n" $secretExists + exit 2 +fi; + + +if [ $secretShouldUpdate == "false" ]; then + printf "No outdated secrets. Secret expires in %s%s%s days\n" $grn "${expiry}" $normal + exit 0 +fi; + +if [ $UPDATE_SECRETS != "true" ]; then + printf "Secrets should be updated Run with UPDATE_SECRETS=true to update.\n" + exit 1 +fi; + +printf "Generating password... " +SCANNER_SQL_PASSWORD=$(head -c 32 /dev/urandom | base64 --wrap 0) || + { echo "ERROR: Could not generate password." >&2; exit 1; } +printf "Done.\n" + +printf "Saving password to %s... " "${KEY_VAULT}" +keyvault_secret_save "${KEY_VAULT}" "${KV_SECRET_VULNERABILITY_SCAN_DB_API}" "${SCANNER_SQL_PASSWORD}" || + { echo "ERROR: Could not save password." >&2; exit 1; } +printf "Done.\n" + +printf "Whitelist IP in firewall rule for SQL Server... " +whitelistRuleName="ClientIpAddress_$(date +%Y%m%d%H%M%S)" + +add_local_computer_sql_firewall_rule \ + "${VULNERABILITY_SCAN_SQL_SERVER_NAME}" \ + "${AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL}" \ + "${whitelistRuleName}" \ + || { echo "ERROR: Could not add IP to firewall." >&2; exit 1; } +printf "Done.\n" + +printf "Creating/updating SQL user for Radix Vulnerability Scanner... " +create_or_update_sql_user \ + "${VULNERABILITY_SCAN_SQL_SERVER_FQDN}" \ + "${VULNERABILITY_SCAN_SQL_DATABASE_NAME}" \ + "${VULNERABILITY_SCAN_SQL_API_USER}" \ + "${SCANNER_SQL_PASSWORD}" \ + "radixreader" || + { echo "ERROR: Could not update password in database." >&2; exit 1; } +printf "Done.\n" + +printf "Remove IP in firewall rule for SQL Server... " +delete_sql_firewall_rule \ + $VULNERABILITY_SCAN_SQL_SERVER_NAME \ + $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ + $whitelistRuleName \ + || exit +printf "Done.\n" + + +VULNERABILITY_SCANNER_API_SECRET_NAME_QA=$(kubectl get secret --namespace "radix-vulnerability-scanner-api-qa" --selector radix-component="server" -ojson | jq -r .items[0].metadata.name) +VULNERABILITY_SCANNER_API_SECRET_NAME_PROD=$(kubectl get secret --namespace "radix-vulnerability-scanner-api-prod" --selector radix-component="server" -ojson | jq -r .items[0].metadata.name) + +printf "Patching %s\n" $VULNERABILITY_SCANNER_API_SECRET_NAME_QA +printf "Patching %s\n" VULNERABILITY_SCANNER_API_SECRET_NAME_PROD + +kubectl patch secret --namespace radix-vulnerability-scanner-api-qa ${VULNERABILITY_SCANNER_API_SECRET_NAME_QA} --type='json' -p="[{\"op\" : \"replace\" ,\"path\" : \"/data/SQL_PASSWORD\" ,\"value\" : \"$(echo ${SCANNER_SQL_PASSWORD} | base64)\"}]" +kubectl patch secret --namespace radix-vulnerability-scanner-api-prod ${VULNERABILITY_SCANNER_API_SECRET_NAME_PROD} --type='json' -p="[{\"op\" : \"replace\" ,\"path\" : \"/data/SQL_PASSWORD\" ,\"value\" : \"$(echo ${SCANNER_SQL_PASSWORD} | base64)\"}]" + +kubectl rollout restart deployment -n radix-vulnerability-scanner-api-qa +kubectl rollout restart deployment -n radix-vulnerability-scanner-api-prod diff --git a/scripts/rotate-secrets/services/vulnerability-scan-writer.sh b/scripts/rotate-secrets/services/vulnerability-scan-writer.sh new file mode 100755 index 000000000..a76e5d5d0 --- /dev/null +++ b/scripts/rotate-secrets/services/vulnerability-scan-writer.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash +red=$'\e[1;31m' +grn=$'\e[1;32m' +yel=$'\e[1;33m' +normal=$(tput sgr0) + +####################################################################################### +### PURPOSE +### + +# Rotates secret for radix-vulnerability-scanner's writer user in a radix cluster + +####################################################################################### +### PRECONDITIONS +### + +# - AKS cluster is available +# - User has role cluster-admin + +####################################################################################### +### INPUTS +### + +# Required: +# - RADIX_ZONE_ENV : Path to *.env file +# - CLUSTER_NAME : Ex: "test-2", "weekly-93" + +# Optional: +# - UPDATE_SECRETS : Rotate expired secrets. Defaults to false. +# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. + +####################################################################################### +### HOW TO USE +### + +# NORMAL +# RADIX_ZONE_ENV=../../radix-zone/radix_zone_dev.env CLUSTER_NAME=weekly-07 UPDATE_SECRETS=false ./vulnerability-scan-writer.sh + +####################################################################################### +### Check Environment +### + +echo "" +printf "Start rotating secrets for radix-vulnerability-scanner writer... \n" + +printf "Loading dependencies... " +source "${RADIX_ZONE_ENV:-'!!!! No RADIX_ZONE_ENV Provided !!!!'}" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/util.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_firewall.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/rotate-secrets/lib_keyvault.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/azure-sql/lib_security.sh" +source "${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/lib_dependencies.sh" +printf "Done.\n" + +assert_cli_tools || exit 1 +has_env_name "CLUSTER_NAME" || exit 1 +prepare_azure_session || exit 1 +setup_cluster_access "$AZ_RESOURCE_GROUP_CLUSTERS" "$CLUSTER_NAME" || + { echo "ERROR: Unable to connect to cluster" >&2; exit 1; } + +USER_PROMPT=${USER_PROMPT:=true} +UPDATE_SECRETS=${UPDATE_SECRETS:=false} + +KEY_VAULT="radix-keyv-${RADIX_ZONE}" +if [[ "${RADIX_ZONE}" == "prod" ]]; then + KEY_VAULT="radix-keyv-platform" +fi; + +echo -e "" +echo -e "Bootstrap Radix Vulnerability Scanner with the following configuration:" +echo -e "" +echo -e " > WHERE:" +echo -e " ------------------------------------------------------------------" +echo -e " - RADIX_ZONE : $RADIX_ZONE" +echo -e " - CLUSTER_NAME : $CLUSTER_NAME" +echo -e " - KEY_VAULT : $KEY_VAULT" +echo -e " - VULNERABILITY_SCAN_SQL_SERVER_NAME : $VULNERABILITY_SCAN_SQL_SERVER_NAME" +echo -e " - VULNERABILITY_SCAN_SQL_DATABASE_NAME : $VULNERABILITY_SCAN_SQL_DATABASE_NAME" +echo -e "" +echo -e " > WHAT:" +echo -e " ------------------------------------------------------------------" +echo -e " - UPDATE_SECRETS : $UPDATE_SECRETS" +echo -e " - DB_USER : $VULNERABILITY_SCAN_SQL_SCANNER_USER" +echo -e " - SECRET : $KV_SECRET_VULNERABILITY_SCAN_DB_WRITER" +echo -e "" +echo -e " > WHO:" +echo -e " -------------------------------------------------------------------" +echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" +echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" +echo -e "" + +echo "" + +user_prompt_continue || exit 1 + + +####################################################################################### +### Start +### + + +secretShouldUpdate=false +secretExists=$(keyvault_secret_exist ${KEY_VAULT} "${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER}") + +if [ $secretExists -eq 1 ]; then + secretShouldUpdate=true + printf "%sSecret %s missing, should be created%s\n" ${yel} ${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER} ${normal} +elif [ $secretExists -eq 0 ]; then + expiry=$(keyvault_secret_ttl_days ${KEY_VAULT} ${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER}) + if [ $expiry -le 31 ]; then + secretShouldUpdate=true + printf "%sSecret %s expires in %s days, should be updated %s \n" ${yel} ${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER} ${expiry} ${normal} + fi; +else + printf "Unknown response from secret exists: %s\n" $secretExists + exit 2 +fi; + + +if [ $secretShouldUpdate == "false" ]; then + printf "No outdated secrets. Secret expires in %s%s%s days\n" $grn "${expiry}" $normal + exit 0 +fi; + +if [ $UPDATE_SECRETS != "true" ]; then + printf "Secrets should be updated Run with UPDATE_SECRETS=true to update.\n" + exit 1 +fi; + +printf "Generating password... " +PASSWORD=$(head -c 32 /dev/urandom | base64 --wrap 0) || + { echo "ERROR: Could not generate password." >&2; exit 1; } +printf "Done.\n" + +printf "Saving password to %s... " ${KEY_VAULT} +keyvault_secret_save ${KEY_VAULT} ${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER} ${PASSWORD} || + { echo "ERROR: Failed to save password." >&2; exit 1; } +printf "Done.\n" + +printf "Whitelist IP in firewall rule for SQL Server... " +whitelistRuleName="ClientIpAddress_$(date +%Y%m%d%H%M%S)" + +add_local_computer_sql_firewall_rule \ + $VULNERABILITY_SCAN_SQL_SERVER_NAME \ + $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ + $whitelistRuleName \ + || { echo "ERROR: Could not add IP to firewall." >&2; exit 1; } +printf "Done.\n" + +printf "Creating/updating SQL user for Radix Vulnerability Scanner... " +create_or_update_sql_user \ + $VULNERABILITY_SCAN_SQL_SERVER_FQDN \ + $VULNERABILITY_SCAN_SQL_DATABASE_NAME \ + $VULNERABILITY_SCAN_SQL_SCANNER_USER \ + $PASSWORD \ + "radixwriter" || { echo "ERROR: Could not update password in database" >&2; exit 1; } +printf "Done.\n" + +printf "Remove IP in firewall rule for SQL Server... " +delete_sql_firewall_rule \ + $VULNERABILITY_SCAN_SQL_SERVER_NAME \ + $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ + $whitelistRuleName \ + || exit +printf "Done.\n" + + +printf "Refresh secret in cluster... " +kubectl annotate externalsecret --namespace=radix-vulnerability-scanner vulnerability-scanner-chart-values-test force-sync=$(date +%s) --overwrite > /dev/null || { echo "ERROR: Failed to trigger secret refresh" >&2; exit 1; } +sleep 1 # Lets give ESO some time to sync secret +printf "Done.\n" + +printf "Restarting deployment in cluster... " +flux delete helmrelease -n radix-vulnerability-scanner radix-vulnerability-scanner --silent || { echo "ERROR: Failed to delete helmrelease" >&2; exit 1; } +flux reconcile ks radix-vulnerability-scanner > /dev/null || { echo "ERROR: Failed to restart deployment" >&2; exit 1; } +printf "Done.\n" diff --git a/scripts/utility/lib_dependencies.sh b/scripts/utility/lib_dependencies.sh new file mode 100644 index 000000000..07f3aa658 --- /dev/null +++ b/scripts/utility/lib_dependencies.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + + +function assert_cli_tools() { + printf "Check for neccesary executables... " + + hash az 2>/dev/null || { + echo -e "ERROR: Azure-CLI not found in PATH. Exiting..." >&2 + return 1 + } + hash kubectl 2>/dev/null || { + echo -e "ERROR: kubectl not found in PATH. Exiting..." >&2 + return 1 + } + hash flux 2>/dev/null || { + echo -e "ERROR: flux not found in PATH. Exiting..." >&2 + return 1 + } + hash jq 2>/dev/null || { + echo -e "ERROR: jq not found in PATH. Exiting..." >&2 + return 1 + } + hash sqlcmd 2>/dev/null || { + echo -e "ERROR: sqlcmd not found in PATH. Exiting..." >&2 + return 1 + } + # https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility?view=sql-server-ver16&tabs=go%2Clinux&pivots=cs1-bash#find-out-which-version-you-have-installed + if [[ "$(sqlcmd --version)" = *"SQL Server Command Line Tool"* ]]; then + echo -e "ERROR: sqlcmd is old ODBC variant. Go variant is required." >&2 + echo -e "https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility?view=sql-server-ver16&tabs=go%2Clinux&pivots=cs1-bash#download-and-install-sqlcmd" >&2 + echo -e "Exiting..." >&2 + return 1 + fi + + printf "Done.\n" + return 0 +} + +function prepare_azure_session() { + printf "Logging you in to Azure if not already logged in... " + az account show >/dev/null || az login >/dev/null + az account set --subscription "$AZ_SUBSCRIPTION_ID" >/dev/null + printf "Done.\n" +} + +function has_env_name() { + if [[ -z $(printenv $1) ]]; then + echo "ERROR: Please provide ${1}" >&2 + return 1 + fi + + return 0 +} + +function user_prompt_continue() { + if [[ $USER_PROMPT == true ]]; then + while true; do + read -p "Is this correct? (y/n) " yn + case $yn in + [Yy]* ) break;; + [Nn]* ) echo ""; echo "Quitting."; return 1;; + * ) echo "Please answer yes or no.";; + esac + done + fi + + return 0 +} diff --git a/scripts/utility/util.sh b/scripts/utility/util.sh index abfbd51e2..239d2c1a1 100755 --- a/scripts/utility/util.sh +++ b/scripts/utility/util.sh @@ -4,7 +4,24 @@ function get_credentials() { printf "\nRunning az aks get-credentials...\n" local AZ_RESOURCE_GROUP_CLUSTERS="$1" local CLUSTER="$2" - + + az aks get-credentials \ + --overwrite-existing \ + --resource-group "$AZ_RESOURCE_GROUP_CLUSTERS" \ + --name "$CLUSTER" \ + --format exec \ + --only-show-errors || + { return; } + + if [[ -n $CI ]]; then + kubelogin convert-kubeconfig -l azurecli + fi + # TODO: if we get ResourceNotFound, don't print message. if we get any other error, like instructions to log in with browser, do print error +} +function get_credentials_silent() { + local AZ_RESOURCE_GROUP_CLUSTERS="$1" + local CLUSTER="$2" + az aks get-credentials \ --overwrite-existing \ --resource-group "$AZ_RESOURCE_GROUP_CLUSTERS" \ @@ -28,6 +45,27 @@ function verify_cluster_access() { printf " OK\n" } +function setup_cluster_access() { + local AZ_RESOURCE_GROUP_CLUSTERS="$1" + local CLUSTER_NAME="$2" + + get_credentials_silent "${AZ_RESOURCE_GROUP_CLUSTERS}" "${CLUSTER_NAME}" + kubectl_context="$(kubectl config current-context)" + if [ "${kubectl_context}" = "${CLUSTER_NAME}" ]; then + return 0 + else + echo "ERROR: Please set your kubectl current-context to be ${CLUSTER_NAME}" + exit 1 + fi + + kubectl cluster-info > /dev/null || { + echo "ERROR: Could not access cluster. Quitting..." + exit 1 + } + + exit 0; +} + function get_cluster_outbound_ip() { local migration_strategy=$1 local cluster_name=$2 @@ -39,7 +77,7 @@ function get_cluster_outbound_ip() { if [[ "${migration_strategy}" == "at" ]]; then ip_address=$(get_test_cluster_outbound_ip $cluster_name $az_subscription_id) if [[ -z "${ip_address}" ]]; then - printf "ERROR: Could not get outbound IP address for test cluster $cluster_name.\n" >&2 + printf "ERROR: Could not get outbound IP address for test cluster $cluster_name.\n" >&2 return 1 fi ip_prefix="$ip_address/32" @@ -75,7 +113,7 @@ function get_test_cluster_outbound_ip() { frontend_ip_configurations_file="/tmp/$(uuidgen)" cat $outbound_rules_file | jq -r .[0].frontendIPConfigurations > $frontend_ip_configurations_file if [[ $(jq length $frontend_ip_configurations_file) != "1" ]]; then - printf "ERROR: Expected exactly 1 frontendIPConfiguration associated with outbound rule in LB for $dest_cluster, but found $(jq length $frontend_ip_configurations_file)" >&2 + printf "ERROR: Expected exactly 1 frontendIPConfiguration associated with outbound rule in LB for $dest_cluster, but found $(jq length $frontend_ip_configurations_file)" >&2 rm $json_output_file $outbound_rules_file $frontend_ip_configurations_file return 1 fi @@ -132,4 +170,4 @@ get_latest_release() { # retrieves latest release version from a GitHub repository. Assumes the version has format v.. # this function does not use the more convenient GitHub API in order to circumvent rate limiting curl -sL https://github.com/$1/releases/latest | grep -E "/tree/" | grep -E "v[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}" -o | head -1 -} \ No newline at end of file +} diff --git a/scripts/vulnerability-scanner/README.md b/scripts/vulnerability-scanner/README.md deleted file mode 100644 index 110d88e1d..000000000 --- a/scripts/vulnerability-scanner/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Vulnerability Scanner - -## Table of contents - -- [Refresh secrets](#Refresh-secrets) - -### Refresh secrets - -Refreshing secrets for vulnerability-scanner can be done in 2 steps: -1. Run [bootstrap.sh](./ bootstrap.sh) with the desired parameters to regenerate password(s) and store it in KV -2. Run [update_secret_for_vulnerability_scanner_api.sh](../../ update_secret_for_vulnerability_scanner_api.sh) to restart the component pods and force it to read the updated k8s secret(s) diff --git a/scripts/vulnerability-scanner/bootstrap.sh b/scripts/vulnerability-scanner/bootstrap.sh deleted file mode 100755 index 1bba526ee..000000000 --- a/scripts/vulnerability-scanner/bootstrap.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env bash - -####################################################################################### -### PURPOSE -### - -# Bootstrap radix-vulnerability-scanner in a radix cluster - -####################################################################################### -### PRECONDITIONS -### - -# - AKS cluster is available -# - User has role cluster-admin - -####################################################################################### -### INPUTS -### - -# Required: -# - RADIX_ZONE_ENV : Path to *.env file -# - CLUSTER_NAME : Ex: "test-2", "weekly-93" - -# Optional: -# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. -# - REGENERATE_SCANNER_PASSWORD : Should existing password for radix-vulnerability-scanner be regenerated and stored in KV? true/false. default is false -# - REGENERATE_API_PASSWORD : Should existing password for radix-vulnerability-scanner-api be regenerated and stored in KV? true/false. default is false - -####################################################################################### -### HOW TO USE -### - -# NORMAL -# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env CLUSTER_NAME="weekly-2" ./bootstrap.sh - -####################################################################################### -### START -### - -echo "" -echo "Start bootstrap of radix-vulnerability-scanner and radix-vulnerability-scanner-api... " - -####################################################################################### -### Check for prerequisites binaries -### - -echo "" -printf "Check for neccesary executables... " -hash az 2>/dev/null || { - echo -e "\nERROR: Azure-CLI not found in PATH. Exiting..." >&2 - exit 1 -} -hash kubectl 2>/dev/null || { - echo -e "\nERROR: kubectl not found in PATH. Exiting..." >&2 - exit 1 -} -hash jq 2>/dev/null || { - echo -e "\nERROR: jq not found in PATH. Exiting..." >&2 - exit 1 -} -hash sqlcmd 2>/dev/null || { - echo -e "\nERROR: sqlcmd not found in PATH. Exiting... " >&2 - exit 1 -} -printf "All is good." -echo "" - -####################################################################################### -### Set default values for optional input -### - -USER_PROMPT=${USER_PROMPT:=true} - -REGENERATE_SCANNER_PASSWORD=${REGENERATE_SCANNER_PASSWORD:-false} - -REGENERATE_API_PASSWORD=${REGENERATE_API_PASSWORD:-false} - -####################################################################################### -### Read inputs and configs -### - -# Required inputs - -if [[ -z "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: Please provide RADIX_ZONE_ENV" >&2 - exit 1 -else - if [[ ! -f "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: RADIX_ZONE_ENV=$RADIX_ZONE_ENV is invalid, the file does not exist." >&2 - exit 1 - fi - source "$RADIX_ZONE_ENV" -fi - -if [[ -z "$CLUSTER_NAME" ]]; then - echo "ERROR: Please provide CLUSTER_NAME" >&2 - exit 1 -fi - -case $REGENERATE_SCANNER_PASSWORD in - true|false) ;; - *) - echo 'REGENERATE_SCANNER_PASSWORD must be true or false' >&2 - exit 1 - ;; -esac - -case $REGENERATE_API_PASSWORD in - true|false) ;; - *) - echo 'REGENERATE_API_PASSWORD must be true or false' >&2 - exit 1 - ;; -esac - -# Source util scripts - -source ${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/util.sh - -####################################################################################### -### Prepare az session -### - -printf "Logging you in to Azure if not already logged in... " -az account show >/dev/null || az login >/dev/null -az account set --subscription "$AZ_SUBSCRIPTION_ID" >/dev/null -printf "Done.\n" - -script_dir_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -####################################################################################### -### Ask user to verify inputs and az login -### - -echo -e "" -echo -e "Bootstrap Radix Vulnerability Scanner and API with the following configuration:" -echo -e "" -echo -e " > WHERE:" -echo -e " ------------------------------------------------------------------" -echo -e " - AZ_RESOURCE_KEYVAULT : $AZ_RESOURCE_KEYVAULT" -echo -e " - CLUSTER_NAME : $CLUSTER_NAME" -echo -e "" -echo -e " > WHAT:" -echo -e " ------------------------------------------------------------------" -echo -e " - REGENERATE_SCANNER_PASSWORD : $REGENERATE_SCANNER_PASSWORD" -echo -e " - REGENERATE_API_PASSWORD : $REGENERATE_API_PASSWORD" -echo -e "" -echo -e " > WHO:" -echo -e " -------------------------------------------------------------------" -echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" -echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" -echo -e "" - -echo "" - -if [[ $USER_PROMPT == true ]]; then - while true; do - read -p "Is this correct? (Y/n) " yn - case $yn in - [Yy]* ) break;; - [Nn]* ) echo ""; echo "Quitting."; exit 1;; - * ) echo "Please answer yes or no.";; - esac - done -fi - -####################################################################################### -### CLUSTER? -### - -printf "Connecting kubectl..." -get_credentials "$AZ_RESOURCE_GROUP_CLUSTERS" "$CLUSTER_NAME" -kubectl_context="$(kubectl config current-context)" - -if [ "$kubectl_context" = "$CLUSTER_NAME" ] || [ "$kubectl_context" = "${CLUSTER_NAME}" ]; then - echo "kubectl is ready..." -else - echo "ERROR: Please set your kubectl current-context to be $CLUSTER_NAME" >&2 - exit 1 -fi - -####################################################################################### -### Verify cluster access -### -verify_cluster_access - -(RADIX_ZONE_ENV="$RADIX_ZONE_ENV" CLUSTER_NAME="$CLUSTER_NAME" USER_PROMPT="$USER_PROMPT" REGENERATE_SQL_PASSWORD="$REGENERATE_SCANNER_PASSWORD" "${script_dir_path}/bootstrap_scanner.sh") -wait # wait for subshell to finish -printf "Done bootstrapping Radix Vulnerability Scanner prerequisites.\n" - -(RADIX_ZONE_ENV="$RADIX_ZONE_ENV" USER_PROMPT="$USER_PROMPT" REGENERATE_SQL_PASSWORD="$REGENERATE_API_PASSWORD" "${script_dir_path}/bootstrap_api.sh") - -wait # wait for subshell to finish -printf "Done bootstrapping Radix Vulnerability Scanner API prerequisites.\n" diff --git a/scripts/vulnerability-scanner/bootstrap_api.sh b/scripts/vulnerability-scanner/bootstrap_api.sh deleted file mode 100755 index 9161e8b02..000000000 --- a/scripts/vulnerability-scanner/bootstrap_api.sh +++ /dev/null @@ -1,204 +0,0 @@ - -####################################################################################### -### PURPOSE -### - -# Bootstrap requirements for radix-vulnerability-scanner-api in a radix cluster - -####################################################################################### -### PRECONDITIONS -### - -# - AKS cluster is available -# - User has role cluster-admin - -####################################################################################### -### INPUTS -### - -# Required: -# - RADIX_ZONE_ENV : Path to *.env file - -# Optional: -# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. -# - REGENERATE_SQL_PASSWORD : Should existing password for SQL login be regenerated and stored in KV? true/false. default is false - -####################################################################################### -### HOW TO USE -### - -# NORMAL -# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env ./bootstrap_api.sh - -# Generate and store new SQL user password - new password is stored in KV and updated for SQL user -# The SQL_PASSWORD secret for radix-vulnerability-scanner-api app in Radix must be updated and restarted -# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env REGENERATE_SQL_PASSWORD=true ./bootstrap_api.sh - -####################################################################################### -### START -### - -echo "" -echo "Start bootstrap of radix-vulnerability-scanner-api... " - -####################################################################################### -### Check for prerequisites binaries -### - -echo "" -printf "Check for neccesary executables... " -hash az 2>/dev/null || { - echo -e "\nERROR: Azure-CLI not found in PATH. Exiting..." >&2 - exit 1 -} -hash jq 2>/dev/null || { - echo -e "\nERROR: jq not found in PATH. Exiting..." >&2 - exit 1 -} -hash sqlcmd 2>/dev/null || { - echo -e "\nERROR: sqlcmd not found in PATH. Exiting... " >&2 - exit 1 -} -printf "All is good." -echo "" - -####################################################################################### -### Set default values for optional input -### - -USER_PROMPT=${USER_PROMPT:=true} - -REGENERATE_SQL_PASSWORD=${REGENERATE_SQL_PASSWORD:-false} - -####################################################################################### -### Read inputs and configs -### - -# Required inputs - -if [[ -z "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: Please provide RADIX_ZONE_ENV" >&2 - exit 1 -else - if [[ ! -f "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: RADIX_ZONE_ENV=$RADIX_ZONE_ENV is invalid, the file does not exist." >&2 - exit 1 - fi - source "$RADIX_ZONE_ENV" -fi - -case $REGENERATE_SQL_PASSWORD in - true|false) ;; - *) - echo 'ERROR: REGENERATE_SQL_PASSWORD must be true or false' >&2 - exit 1 - ;; -esac - -# Load dependencies -LIB_AZURE_SQL_FIREWALL_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../azure-sql/lib_firewall.sh" -if [[ ! -f "$LIB_AZURE_SQL_FIREWALL_PATH" ]]; then - echo "ERROR: The dependency LIB_AZURE_SQL_FIREWALL_PATH=$LIB_AZURE_SQL_FIREWALL_PATH is invalid, the file does not exist." >&2 - exit 1 -else - source "$LIB_AZURE_SQL_FIREWALL_PATH" -fi - -LIB_AZURE_SQL_SECURITY_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../azure-sql/lib_security.sh" -if [[ ! -f "$LIB_AZURE_SQL_SECURITY_PATH" ]]; then - echo "ERROR: The dependency LIB_AZURE_SQL_SECURITY_PATH=$LIB_AZURE_SQL_SECURITY_PATH is invalid, the file does not exist." >&2 - exit 1 -else - source "$LIB_AZURE_SQL_SECURITY_PATH" -fi - -####################################################################################### -### Prepare az session -### - -printf "Logging you in to Azure if not already logged in... " -az account show >/dev/null || az login >/dev/null -az account set --subscription "$AZ_SUBSCRIPTION_ID" >/dev/null -printf "Done.\n" - -script_dir_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -####################################################################################### -### Ask user to verify inputs and az login -### - -echo -e "" -echo -e "Bootstrap Radix Vulnerability Scanner API with the following configuration:" -echo -e "" -echo -e " > WHERE:" -echo -e " ------------------------------------------------------------------" -echo -e " - AZ_RESOURCE_KEYVAULT : $AZ_RESOURCE_KEYVAULT" -echo -e " - VULNERABILITY_SCAN_SQL_SERVER_NAME : $VULNERABILITY_SCAN_SQL_SERVER_NAME" -echo -e " - VULNERABILITY_SCAN_SQL_DATABASE_NAME : $VULNERABILITY_SCAN_SQL_DATABASE_NAME" -echo -e "" -echo -e " > WHAT:" -echo -e " ------------------------------------------------------------------" -echo -e " - REGENERATE_SQL_PASSWORD : $REGENERATE_SQL_PASSWORD" -echo -e "" -echo -e " > WHO:" -echo -e " -------------------------------------------------------------------" -echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" -echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" -echo -e "" - -echo "" - -if [[ $USER_PROMPT == true ]]; then - while true; do - read -p "Is this correct? (Y/n) " yn - case $yn in - [Yy]* ) echo ""; break;; - [Nn]* ) echo ""; echo "Quitting."; exit 1;; - * ) echo "Please answer yes or no.";; - esac - done -fi - -####################################################################################### -### Get/generate password for database user and create/update user in Azure SQL Database -### - -echo "Generate password for API SQL user and store in KV" - -generate_password_and_store $AZ_RESOURCE_KEYVAULT $KV_SECRET_VULNERABILITY_SCAN_DB_API $REGENERATE_SQL_PASSWORD || exit - -API_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_VULNERABILITY_SCAN_DB_API | jq -r .value) -ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_VULNERABILITY_SCAN_SQL_ADMIN | jq -r .value) - -if [[ -z $ADMIN_SQL_PASSWORD ]]; then - printf "ERROR: SQL admin password not set" >&2 - exit 1 -fi - -echo "Whitelist IP in firewall rule for SQL Server" -whitelistRuleName="ClientIpAddress_$(date +%Y%m%d%H%M%S)" - -add_local_computer_sql_firewall_rule \ - $VULNERABILITY_SCAN_SQL_SERVER_NAME \ - $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ - $whitelistRuleName \ - || exit - -echo "Creating/updating SQL user for Radix Vulnerability Scanner API" -create_or_update_sql_user \ - $VULNERABILITY_SCAN_SQL_SERVER_FQDN \ - $VULNERABILITY_SCAN_SQL_ADMIN_LOGIN \ - $ADMIN_SQL_PASSWORD \ - $VULNERABILITY_SCAN_SQL_DATABASE_NAME \ - $VULNERABILITY_SCAN_SQL_API_USER \ - $API_SQL_PASSWORD \ - "radixreader" - -echo "Remove IP in firewall rule for SQL Server" -delete_sql_firewall_rule \ - $VULNERABILITY_SCAN_SQL_SERVER_NAME \ - $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ - $whitelistRuleName \ - || exit - -echo "Done." \ No newline at end of file diff --git a/scripts/vulnerability-scanner/bootstrap_scanner.sh b/scripts/vulnerability-scanner/bootstrap_scanner.sh deleted file mode 100755 index dbe0bcfbf..000000000 --- a/scripts/vulnerability-scanner/bootstrap_scanner.sh +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env bash - -####################################################################################### -### PURPOSE -### - -# Bootstrap requirements for radix-vulnerability-scanner in a radix cluster - -####################################################################################### -### PRECONDITIONS -### - -# - AKS cluster is available -# - User has role cluster-admin - -####################################################################################### -### INPUTS -### - -# Required: -# - RADIX_ZONE_ENV : Path to *.env file -# - CLUSTER_NAME : Ex: "test-2", "weekly-93" - -# Optional: -# - USER_PROMPT : Is human interaction required to run script? true/false. Default is true. -# - REGENERATE_SQL_PASSWORD : Should existing password for SQL login be regenerated and stored in KV? true/false. default is false - -####################################################################################### -### HOW TO USE -### - -# NORMAL -# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env CLUSTER_NAME="weekly-2" ./bootstrap_scanner.sh - -# Generate and store new SQL user password - new password is stored in KV and updated for SQL user -# RADIX_ZONE_ENV=../radix-zone/radix_zone_dev.env CLUSTER_NAME="weekly-2" REGENERATE_SQL_PASSWORD=true ./bootstrap_scanner.sh - -####################################################################################### -### START -### - -echo "" -echo "Start bootstrap of radix-vulnerability-scanner... " - -####################################################################################### -### Check for prerequisites binaries -### - -echo "" -printf "Check for neccesary executables... " -hash az 2>/dev/null || { - echo -e "\nERROR: Azure-CLI not found in PATH. Exiting..." >&2 - exit 1 -} -hash kubectl 2>/dev/null || { - echo -e "\nERROR: kubectl not found in PATH. Exiting..." >&2 - exit 1 -} -hash flux 2>/dev/null || { - echo -e "\nERROR: flux not found in PATH. Exiting..." >&2 - exit 1 -} -hash jq 2>/dev/null || { - echo -e "\nERROR: jq not found in PATH. Exiting..." >&2 - exit 1 -} -hash sqlcmd 2>/dev/null || { - echo -e "\nERROR: sqlcmd not found in PATH. Exiting..." >&2 - exit 1 -} -printf "All is good." -echo "" - -####################################################################################### -### Set default values for optional input -### - -USER_PROMPT=${USER_PROMPT:=true} - -REGENERATE_SQL_PASSWORD=${REGENERATE_SQL_PASSWORD:-false} - -####################################################################################### -### Read inputs and configs -### - -# Required inputs - -if [[ -z "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: Please provide RADIX_ZONE_ENV" >&2 - exit 1 -else - if [[ ! -f "$RADIX_ZONE_ENV" ]]; then - echo "ERROR: RADIX_ZONE_ENV=$RADIX_ZONE_ENV is invalid, the file does not exist." >&2 - exit 1 - fi - source "$RADIX_ZONE_ENV" -fi - -if [[ -z "$CLUSTER_NAME" ]]; then - echo "ERROR: Please provide CLUSTER_NAME" >&2 - exit 1 -fi - -case $REGENERATE_SQL_PASSWORD in - true|false) ;; - *) - echo 'ERROR: REGENERATE_SQL_PASSWORD must be true or false' >&2 - exit 1 - ;; -esac - -# Source util scripts - -source ${RADIX_PLATFORM_REPOSITORY_PATH}/scripts/utility/util.sh - -# Load dependencies -LIB_AZURE_SQL_FIREWALL_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../azure-sql/lib_firewall.sh" -if [[ ! -f "$LIB_AZURE_SQL_FIREWALL_PATH" ]]; then - echo "ERROR: The dependency LIB_AZURE_SQL_FIREWALL_PATH=$LIB_AZURE_SQL_FIREWALL_PATH is invalid, the file does not exist." >&2 - exit 1 -else - source "$LIB_AZURE_SQL_FIREWALL_PATH" -fi - -LIB_AZURE_SQL_SECURITY_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../azure-sql/lib_security.sh" -if [[ ! -f "$LIB_AZURE_SQL_SECURITY_PATH" ]]; then - echo "ERROR: The dependency LIB_AZURE_SQL_SECURITY_PATH=$LIB_AZURE_SQL_SECURITY_PATH is invalid, the file does not exist." >&2 - exit 1 -else - source "$LIB_AZURE_SQL_SECURITY_PATH" -fi - -####################################################################################### -### Prepare az session -### - -printf "Logging you in to Azure if not already logged in... " -az account show >/dev/null || az login >/dev/null -az account set --subscription "$AZ_SUBSCRIPTION_ID" >/dev/null -printf "Done.\n" - -script_dir_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -####################################################################################### -### Ask user to verify inputs and az login -### - -echo -e "" -echo -e "Bootstrap Radix Vulnerability Scanner with the following configuration:" -echo -e "" -echo -e " > WHERE:" -echo -e " ------------------------------------------------------------------" -echo -e " - AZ_RESOURCE_KEYVAULT : $AZ_RESOURCE_KEYVAULT" -echo -e " - CLUSTER_NAME : $CLUSTER_NAME" -echo -e " - VULNERABILITY_SCAN_SQL_SERVER_NAME : $VULNERABILITY_SCAN_SQL_SERVER_NAME" -echo -e " - VULNERABILITY_SCAN_SQL_DATABASE_NAME : $VULNERABILITY_SCAN_SQL_DATABASE_NAME" -echo -e "" -echo -e " > WHAT:" -echo -e " ------------------------------------------------------------------" -echo -e " - REGENERATE_SQL_PASSWORD : $REGENERATE_SQL_PASSWORD" -echo -e "" -echo -e " > WHO:" -echo -e " -------------------------------------------------------------------" -echo -e " - AZ_SUBSCRIPTION : $(az account show --query name -otsv)" -echo -e " - AZ_USER : $(az account show --query user.name -o tsv)" -echo -e "" - -echo "" - -if [[ $USER_PROMPT == true ]]; then - while true; do - read -p "Is this correct? (Y/n) " yn - case $yn in - [Yy]* ) break;; - [Nn]* ) echo ""; echo "Quitting."; exit 1;; - * ) echo "Please answer yes or no.";; - esac - done -fi - -####################################################################################### -### CLUSTER? -### - -printf "Connecting kubectl..." -get_credentials "$AZ_RESOURCE_GROUP_CLUSTERS" "$CLUSTER_NAME" -kubectl_context="$(kubectl config current-context)" -if [ "$kubectl_context" = "$CLUSTER_NAME" ] || [ "$kubectl_context" = "${CLUSTER_NAME}" ]; then - echo "kubectl is ready..." -else - echo "Please set your kubectl current-context to be $CLUSTER_NAME" - exit 1 -fi - -####################################################################################### -### Verify cluster access -### -verify_cluster_access - -echo "Generate password for Scanner SQL user and store in KV" - -generate_password_and_store $AZ_RESOURCE_KEYVAULT $KV_SECRET_VULNERABILITY_SCAN_DB_WRITER $REGENERATE_SQL_PASSWORD || exit - -# Create/update SQL user and roles -SCANNER_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_VULNERABILITY_SCAN_DB_WRITER | jq -r .value) -ADMIN_SQL_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_VULNERABILITY_SCAN_SQL_ADMIN | jq -r .value) - -if [[ -z $ADMIN_SQL_PASSWORD ]]; then - printf "ERROR: SQL admin password not set" - exit 1 -fi - -echo "Whitelist IP in firewall rule for SQL Server" -whitelistRuleName="ClientIpAddress_$(date +%Y%m%d%H%M%S)" - -add_local_computer_sql_firewall_rule \ - $VULNERABILITY_SCAN_SQL_SERVER_NAME \ - $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ - $whitelistRuleName \ - || exit - -echo "Creating/updating SQL user for Radix Vulnerability Scanner" -create_or_update_sql_user \ - $VULNERABILITY_SCAN_SQL_SERVER_FQDN \ - $VULNERABILITY_SCAN_SQL_ADMIN_LOGIN \ - $ADMIN_SQL_PASSWORD \ - $VULNERABILITY_SCAN_SQL_DATABASE_NAME \ - $VULNERABILITY_SCAN_SQL_SCANNER_USER \ - $SCANNER_SQL_PASSWORD \ - "radixwriter" - -echo "Remove IP in firewall rule for SQL Server" -delete_sql_firewall_rule \ - $VULNERABILITY_SCAN_SQL_SERVER_NAME \ - $AZ_RESOURCE_GROUP_VULNERABILITY_SCAN_SQL \ - $whitelistRuleName \ - || exit - - -echo "Install Radix Vulnerability Scanner resources for flux" - -az keyvault secret download \ - --vault-name "$AZ_RESOURCE_KEYVAULT" \ - --name "${AZ_SYSTEM_USER_CONTAINER_REGISTRY_READER}" \ - --file acr_sp_credentials.json \ - || { echo "ERROR: Could not get secret '${AZ_SYSTEM_USER_CONTAINER_REGISTRY_READER}' in '${AZ_RESOURCE_KEYVAULT}'." >&2; exit; } - -SQL_DB_PASSWORD=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $KV_SECRET_VULNERABILITY_SCAN_DB_WRITER | jq -r .value) || - { echo "ERROR: Could not get secret '${KV_SECRET_VULNERABILITY_SCAN_DB_WRITER}' in '${AZ_RESOURCE_KEYVAULT}'." >&2; exit; } - -snykAccessTokenSecret=radix-snyk-sa-access-token-$RADIX_ZONE -SNYK_SA_TOKEN=$(az keyvault secret show --vault-name "$AZ_RESOURCE_KEYVAULT" --name $snykAccessTokenSecret | jq -r .value) || - { echo "ERROR: Could not get secret '${snykAccessTokenSecret}' in '${AZ_RESOURCE_KEYVAULT}'." >&2; exit; } - -echo "sql: - password: ${SQL_DB_PASSWORD} - serverName: ${VULNERABILITY_SCAN_SQL_SERVER_FQDN} - databaseName: ${VULNERABILITY_SCAN_SQL_DATABASE_NAME} - userName: ${VULNERABILITY_SCAN_SQL_SCANNER_USER} -dockerAuths: - - registry: ${AZ_RESOURCE_CONTAINER_REGISTRY}.azurecr.io - username: $(jq -r '.id' acr_sp_credentials.json) - password: $(jq -r '.password' acr_sp_credentials.json) -snykToken: ${SNYK_SA_TOKEN}" > vulnerability-scanner-chart-values.yaml - -kubectl create ns radix-vulnerability-scanner --dry-run=client --save-config -o yaml | - kubectl apply -f - - -kubectl create secret generic vulnerability-scanner-chart-values --namespace radix-vulnerability-scanner \ - --from-file=./vulnerability-scanner-chart-values.yaml \ - --dry-run=client -o yaml | - kubectl apply -f - - -flux reconcile helmrelease --namespace radix-vulnerability-scanner radix-vulnerability-scanner -kubectl rollout restart deployment radix-vulnerability-scanner --namespace radix-vulnerability-scanner - -rm -f vulnerability-scanner-chart-values.yaml -rm -f acr_sp_credentials.json -echo "Done." \ No newline at end of file