mirror of
https://gitlab.com/psuapp/psu.git
synced 2024-08-30 18:12:34 +00:00
Add sensible information masking through --masked-variables flag
In debug and/or verbose mode, the value of sensitive variables will be hidden, useful to avoid leaking passwords or tokens in your logs. Possible values: true|extended|false. Defaults to 'true'. Useful when running 'psu' in a CI tool, to avoid leaking passwords or tokens in the logs.
This commit is contained in:
parent
18827605c8
commit
55b45769d3
114
psu
114
psu
@ -43,6 +43,7 @@ main() {
|
||||
"insecure;-i;--insecure;Skip the host's SSL certificate verification, use at your own risk. Defaults to false"
|
||||
"verbose;-v;--verbose;Increase the verbosity of messages. Defaults to false"
|
||||
"debug;-d;--debug;Print as much information as possible to help diagnosing a malfunction. Defaults to false"
|
||||
"masked-variables;-m;--masked-variables;In debug and/or verbose mode, the value of sensitive variables will be hidden, useful to avoid leaking passwords or tokens in your logs. Possible values: true|extended|false. Defaults to true"
|
||||
"quiet;-q;--quiet;Display the minimum of information or nothing, UNIX/Linux friendly. Defaults to false"
|
||||
"strict;-t;--strict;Never updates an existent stack nor removes an inexistent one, and instead exits with an error. Defaults to false"
|
||||
"help;-h;--help;Display help message. To display help of a given action, run: 'psu <action> --help'"
|
||||
@ -54,17 +55,17 @@ main() {
|
||||
|
||||
ACTIONS_TABLE=(
|
||||
# action_name;description[;required_option_key1|required_option_key2...][;optional_option_key1|optional_option_key2...]
|
||||
"deploy;Deploy the stack;url|user|password|name|stack-file;endpoint|env-file|prune|insecure|verbose|debug|strict"
|
||||
"undeploy;Undeploy/remove the stack;url|user|password|name;endpoint|insecure|verbose|debug|strict"
|
||||
"list;Lists of the stacks already deployed;url|user|password;endpoint|quiet|insecure|verbose|debug|help"
|
||||
"info;Stack information;url|user|password|name;endpoint|quiet|insecure|verbose|debug"
|
||||
"status;Check if the stack is running/deployed correctly;url|user|password|name;endpoint|service|detect-job|timeout|insecure|verbose|debug"
|
||||
"system;Display Docker system-wide information;url|user|password;endpoint|insecure|verbose|debug"
|
||||
"services;Lists services already deployed for the current stack;url|user|password|name;endpoint|quiet|insecure|verbose|debug"
|
||||
"tasks;Lists tasks for the current stack;url|user|password|name;endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug"
|
||||
"tasks:healthy;Lists tasks who are running correctly for the current stack;url|user|password|name;endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug"
|
||||
"containers;Lists containers running for the current stack;url|user|password|name;endpoint|service|quiet|insecure|verbose|debug"
|
||||
"actions;Lists available actions of this program;;verbose|debug"
|
||||
"deploy;Deploy the stack;url|user|password|name|stack-file;endpoint|env-file|prune|insecure|verbose|debug|masked-variables|strict"
|
||||
"undeploy;Undeploy/remove the stack;url|user|password|name;endpoint|insecure|verbose|debug|masked-variables|strict"
|
||||
"list;Lists of the stacks already deployed;url|user|password;endpoint|quiet|insecure|verbose|debug|masked-variables|help"
|
||||
"info;Stack information;url|user|password|name;endpoint|quiet|insecure|verbose|debug|masked-variables"
|
||||
"status;Check if the stack is running/deployed correctly;url|user|password|name;endpoint|service|detect-job|timeout|insecure|verbose|debug|masked-variables"
|
||||
"system;Display Docker system-wide information;url|user|password;endpoint|insecure|verbose|debug|masked-variables"
|
||||
"services;Lists services already deployed for the current stack;url|user|password|name;endpoint|quiet|insecure|verbose|debug|masked-variables"
|
||||
"tasks;Lists tasks for the current stack;url|user|password|name;endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables"
|
||||
"tasks:healthy;Lists tasks who are running correctly for the current stack;url|user|password|name;endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables"
|
||||
"containers;Lists containers running for the current stack;url|user|password|name;endpoint|service|quiet|insecure|verbose|debug|masked-variables"
|
||||
"actions;Lists available actions of this program;;verbose|debug|masked-variables"
|
||||
"help;Display help message"
|
||||
"version;Display this program version"
|
||||
)
|
||||
@ -166,12 +167,12 @@ main() {
|
||||
"$PORTAINER_URL/api/stacks" \
|
||||
"Authorization: Bearer $AUTH_TOKEN")
|
||||
check_for_errors $? "$STACKS"
|
||||
echo_debug "Get stacks response -> $(echo $STACKS | jq -C .)"
|
||||
echo_debug_safe_json "Get stacks response -> $(echo $STACKS | jq -C .)" "$STACKS"
|
||||
|
||||
# Get desired stack from stacks list by it's name
|
||||
STACK=$(echo "$STACKS" \
|
||||
| jq --arg PORTAINER_STACK_NAME "$PORTAINER_STACK_NAME" -jc '.[] | select(.Name == $PORTAINER_STACK_NAME)')
|
||||
echo_debug "Stack ${PORTAINER_STACK_NAME} -> $(echo $STACK | jq -C .)"
|
||||
echo_debug_safe_json "Stack ${PORTAINER_STACK_NAME} -> $(echo $STACK | jq -C .)" "$STACK"
|
||||
fi
|
||||
|
||||
if [ $ACTION == "deploy" ]; then
|
||||
@ -356,6 +357,7 @@ main() {
|
||||
# DEBUG_MODE #
|
||||
# QUIET_MODE #
|
||||
# STRICT_MODE #
|
||||
# MASKED_VARIABLES #
|
||||
# Arguments: #
|
||||
# None #
|
||||
# Returns: #
|
||||
@ -382,6 +384,7 @@ set_globals() {
|
||||
DEBUG_MODE=${DEBUG_MODE:-"false"}
|
||||
QUIET_MODE=${QUIET_MODE:-"false"}
|
||||
STRICT_MODE=${STRICT_MODE:-"false"}
|
||||
MASKED_VARIABLES=${MASKED_VARIABLES:-"true"}
|
||||
|
||||
# Set arguments through argument and options (overwrite envvars)
|
||||
inputs "$@"
|
||||
@ -404,6 +407,7 @@ set_globals() {
|
||||
echo_debug "DEBUG_MODE -> $DEBUG_MODE"
|
||||
echo_debug "QUIET_MODE -> $QUIET_MODE"
|
||||
echo_debug "STRICT_MODE -> $STRICT_MODE"
|
||||
echo_debug "MASKED_VARIABLES -> $MASKED_VARIABLES"
|
||||
|
||||
# Check required arguments have been provided
|
||||
check_argument "$ACTION" "action" "ACTION" "a" "action"
|
||||
@ -526,6 +530,13 @@ inputs() {
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-m|--masked-variables|-m=*|--masked-variables=*)
|
||||
MASKED_VARIABLES=$(input_flag "$1" "$2" "true extended false")
|
||||
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
|
||||
# When the second argument is the value of the current option
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-q|--quiet|-q=*|--quiet=*)
|
||||
QUIET_MODE=$(input_flag "$1" "$2")
|
||||
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
|
||||
@ -777,6 +788,42 @@ check_for_errors() {
|
||||
fi
|
||||
}
|
||||
|
||||
echo_debug_safe_json() {
|
||||
echo_safe_json "debug" "$1" "$2"
|
||||
}
|
||||
|
||||
echo_verbose_safe_json() {
|
||||
echo_safe_json "verbose" "$1" "$2"
|
||||
}
|
||||
|
||||
echo_safe_json() {
|
||||
local type="$1"
|
||||
local message="$2"
|
||||
local json="$3"
|
||||
local temp_file
|
||||
|
||||
# If the $json variable has an '.Env' entry
|
||||
# We parse its content to mask sensitive values
|
||||
if [ "$MASKED_VARIABLES" == "extended" ] && [ -n "$(echo "$json" | jq -j 'if type == "array" then .[] else . end | .Env // ""')" ]; then
|
||||
temp_file=".stack_envs-$(echo "$(date +%s%3N)")"
|
||||
(
|
||||
echo "$json" | jq -r 'if type == "array" then .[] else . end | .Env | map(.name = .name + "=" + .value) | map (.name) | .[]' | sed "s/\"/\\\\\"/g" | sed "s/^\([^=]\{1,\}=\)\(.* .*\)$/\1\"\2\"/" > $temp_file && \
|
||||
. $temp_file && rm -f $temp_file && \
|
||||
if [ "$type" == "debug" ]; then \
|
||||
echo_debug "$message"; \
|
||||
elif [ "$type" == "verbose" ]; then \
|
||||
echo_verbose "$message"; \
|
||||
fi
|
||||
)
|
||||
else
|
||||
if [ "$type" == "debug" ]; then
|
||||
echo_debug "$message"
|
||||
elif [ "$type" == "verbose" ]; then
|
||||
echo_verbose "$message"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Print message if verbose mode is active #
|
||||
# Globals: #
|
||||
@ -791,7 +838,9 @@ echo_verbose() {
|
||||
local yellow='\033[1;33m'
|
||||
local nc='\033[0m'
|
||||
if [ $VERBOSE_MODE == "true" ]; then
|
||||
echo -e "${yellow}${message}${nc}"
|
||||
local verbose_message
|
||||
verbose_message=$(mask_variables "$message")
|
||||
echo -e "${yellow}${verbose_message}${nc}"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -807,10 +856,36 @@ echo_verbose() {
|
||||
echo_debug() {
|
||||
local message=$1
|
||||
if [ $DEBUG_MODE == "true" ]; then
|
||||
echo -e "${message}"
|
||||
local debug_message
|
||||
debug_message=$(mask_variables "$message")
|
||||
|
||||
echo -e "${debug_message}"
|
||||
fi
|
||||
}
|
||||
|
||||
mask_variables() {
|
||||
local message="$1"
|
||||
|
||||
if [ "$MASKED_VARIABLES" == "true" ]; then
|
||||
# Mask PORTAINER_PASSWORD variable value
|
||||
message=$(echo "$message" | sed "s/$PORTAINER_PASSWORD/[MASKED]/g")
|
||||
elif [ "$MASKED_VARIABLES" == "extended" ]; then
|
||||
# Mask all variable values with PASSWORD or TOKEN in their name
|
||||
local masked_vars
|
||||
# Get all declared variable names and filtering by specific terms
|
||||
masked_vars="$(compgen -v | grep -i 'PASSWORD\|TOKEN')"
|
||||
for masked_var in $masked_vars; do
|
||||
if [ -n "$masked_var" ] && [ -n "${!masked_var}" ]; then
|
||||
# Converts multi lines value to one line value
|
||||
masked_value="$(echo ${!masked_var})"
|
||||
message="$(echo "$message" | sed "s/$masked_value/[MASKED]/g")"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "$message"
|
||||
}
|
||||
|
||||
################################
|
||||
# Create/update a stack #
|
||||
# Globals: #
|
||||
@ -864,8 +939,7 @@ deploy() {
|
||||
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\","
|
||||
local data_suffix=",\"Env\":"$stack_envvars"}"
|
||||
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
|
||||
# FIXME: Hide any value of variables who ends by 'PASSWORD' or 'TOKEN', for security reasons
|
||||
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
|
||||
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
|
||||
|
||||
# Create stack for single Docker instance
|
||||
echo_verbose "Creating stack $PORTAINER_STACK_NAME..."
|
||||
@ -895,8 +969,7 @@ deploy() {
|
||||
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\",\"SwarmID\":\"$swarm_id\","
|
||||
local data_suffix=",\"Env\":"$stack_envvars"}"
|
||||
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
|
||||
# FIXME: Hide any value of variables who ends by 'PASSWORD' or 'TOKEN', for security reasons
|
||||
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
|
||||
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
|
||||
|
||||
# Create stack for Docker swarm
|
||||
echo_verbose "Creating stack $PORTAINER_STACK_NAME..."
|
||||
@ -937,8 +1010,7 @@ deploy() {
|
||||
local data_prefix="{\"Id\":\"$stack_id\","
|
||||
local data_suffix=",\"Env\":"$stack_envvars",\"Prune\":$PORTAINER_PRUNE}"
|
||||
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
|
||||
# FIXME: Hide any value of variables who ends by 'PASSWORD' or 'TOKEN', for security reasons
|
||||
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
|
||||
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
|
||||
|
||||
# Update stack
|
||||
echo_verbose "Updating stack $PORTAINER_STACK_NAME..."
|
||||
|
Loading…
Reference in New Issue
Block a user