Skip to content

Commit

Permalink
Rotate scripts with Vulnerability scanner reader+writer (#1203)
Browse files Browse the repository at this point in the history
* Added rotating secrets script

* added vuln-scan-writer secret (WIP)

* added vuln-scan-writer secret (WIP), upgrade lib security to use machine identity

* add script to rotate reader user password

* cleanup

* add secret to radix app, restart deployment

* cleanup

* move common code to lib

* add not before tag

* lowercase y

* slightly better feedback if RADIX_ZONE_ENV is missing

* slightly better feedback if RADIX_ZONE_ENV is missing

* slightly better feedback if RADIX_ZONE_ENV is missing

* cleanup

* cleanup

* cleanup

* reinstall vuln scan

* fix bugs in vulnerability-scan-reader.sh

* cleanup rotate secrets

* set user-prompt to false for individual scripts
  • Loading branch information
Richard87 authored Feb 19, 2024
1 parent 7a5cd88 commit d1fce56
Show file tree
Hide file tree
Showing 16 changed files with 677 additions and 723 deletions.
4 changes: 2 additions & 2 deletions scripts/azure-sql/lib_firewall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -31,4 +31,4 @@ delete_sql_firewall_rule() {
--output none \
--only-show-errors ||
{ echo "ERROR: Failed to delete firewall rule $ruleName." >&2; return 1; }
}
}
20 changes: 6 additions & 14 deletions scripts/azure-sql/lib_security.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,17 @@ 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
echo "ERROR: serverName not set" >&2
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
Expand All @@ -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; }
}
}
6 changes: 2 additions & 4 deletions scripts/cost-allocation/bootstrap_api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 \
Expand All @@ -201,4 +199,4 @@ delete_sql_firewall_rule \
$whitelistRuleName \
|| exit

echo "Done."
echo "Done."
12 changes: 5 additions & 7 deletions scripts/cost-allocation/bootstrap_collector.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 \
Expand All @@ -241,23 +239,23 @@ 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}
password: ${SQL_DB_PASSWORD}" > radix-cost-allocation-values.yaml

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."
echo "Done."
8 changes: 4 additions & 4 deletions scripts/radix-zone/radix_zone_dev.env
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -252,4 +252,4 @@ RADIX_ERROR_PAGE=radix_error_page.html
###

LETS_ENCRYPT_ACME_ACCOUNT_EMAIL=[email protected]
DIGICERT_EXTERNAL_ACCOUNT_KV_SECRET="digicert-external-account-$RADIX_ZONE"
DIGICERT_EXTERNAL_ACCOUNT_KV_SECRET="digicert-external-account-$RADIX_ZONE"
3 changes: 3 additions & 0 deletions scripts/rotate-secrets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Rotating Secrets

This tool looks for all soon to be, or expired secrets and let you rotate them.
62 changes: 62 additions & 0 deletions scripts/rotate-secrets/lib_keyvault.sh
Original file line number Diff line number Diff line change
@@ -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)
}
128 changes: 128 additions & 0 deletions scripts/rotate-secrets/rotate-secrets.sh
Original file line number Diff line number Diff line change
@@ -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"
Loading

0 comments on commit d1fce56

Please sign in to comment.