Skip to content

Instantly share code, notes, and snippets.

@augustohp
Last active August 15, 2023 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save augustohp/b8483ca619f99682cc4c4d6b6e313818 to your computer and use it in GitHub Desktop.
Save augustohp/b8483ca619f99682cc4c4d6b6e313818 to your computer and use it in GitHub Desktop.
Lists secrets for all your namespaces in kubernetes, allows some filtering using "grep" patterns.
#!/usr/bin/env sh
#
# Aids listing/filtering secrets from kubernetes using kubectl. Aims to be minimal
# and portable. The only thing you need is this file and kubectl.
#
# Author: Augusto Pascutti <augusto.hp+oss@gmail>
# License: MIT
# URL: https://gist.github.com/augustohp/b8483ca619f99682cc4c4d6b6e313818
#
# "The only way to make sense out of change
# is to plunge into it, move with it, and join the dance." - Alan Watts
# shellcheck disable=SC3043
# vim: set ft=sh ts=4 sw=4 tw=0 noet:
APP_NAME=$(basename "$0")
APP_VERSION="1.0.0"
APP_AUTHOR="augusto.hp+oss@gmail.com"
APP_DEPENDENCIES="awk grep column kubectl"
TMP_FILE=$(mktemp /tmp/kube-secrets.XXXXXX)
OPTION_DECODE_SECRET="false"
OPTION_KEEP_TMP_FILE="false"
OPTION_FILTER_NS=""
OPTION_FILTER_SECRET=""
OPTION_FILTER_ENV=""
set -e # Stops on failure
trap cleanup INT TERM EXIT
# Usage: cleanup
cleanup()
{
if [ "$OPTION_KEEP_TMP_FILE" != "true" ]
then
rm -f "$TMP_FILE" 2> /dev/null || true
else
echo "Keeping '$TMP_FILE'." >&2
fi
exit 1
}
# Usage: err <string>
err()
{
local msg="$1"
echo "Error: $msg" >&2
}
# Usage: assert_env
assert_env()
{
for dependency in $APP_DEPENDENCIES
do
if [ -n "$(command -v "$dependency")" ]
then
continue
fi
err "Missing dependency '$dependency', please install it."
exit 2
done
}
# ------------------------------------------------------------------------------
# Context
# Usage: k8s_get_namespaces
k8s_get_namespaces()
{
kubectl get ns \
| grep -v "^NAME" \
| awk '{ print $1 }' \
| grep "$OPTION_FILTER_NS"
}
# Usage: k8s_get_secrets <namespace>
k8s_get_secrets()
{
local ns="$1"
kubectl get secrets -n "$ns" 2>/dev/null \
| grep -v "^NAME" \
| awk '{ print $1 }' \
| grep "$OPTION_FILTER_SECRET"
}
# Usage: k8s_describe_secrets <namespace> <secret>
k8s_describe_secrets()
{
local ns="$1"
local secret="$2"
kubectl describe secret -n "$ns" "$secret" \
| grep -A 9999 "====" \
| grep -v "====" \
| awk -F ":" '{ print $1 }' \
| grep "$OPTION_FILTER_ENV"
}
# Usage: no_content_to_display && echo "No content"
no_content_to_display()
{
[ ! -s "$TMP_FILE" ]
}
# Usage: decoded_secret_wanted && echo "Needs to decode secret"
decoded_secret_wanted()
{
[ "$OPTION_DECODE_SECRET" = "true" ]
}
# Usage: command_list_secrets <namespace> <secret>
command_list_secrets()
{
local ns="$1"
local secret="$2"
local separator="||"
local decoded_secret=""
local append_string=""
for ns in $(k8s_get_namespaces)
do
for secret in $(k8s_get_secrets "$ns")
do
for varname in $(k8s_describe_secrets "$ns" "$secret")
do
append_string=""
if decoded_secret_wanted
then
decoded_secret="$(command_decode_secret "$ns" "$secret" "$varname")"
append_string="$separator$decoded_secret"
fi
echo "$ns$separator$secret$separator$varname$append_string" >> "$TMP_FILE"
done
done
done
if no_content_to_display
then
return 0
fi
if decoded_secret_wanted
then
append_string=",Value"
else
append_string=""
fi
# shellcheck disable=SC2002 # `column` and stdin are weird friends
cat "$TMP_FILE" \
| column --table --separator "$separator" --table-columns "Namespace,Secret,Name$append_string"
}
# Usage: command_decode_secret <namespace> <secret> <envvar>
command_decode_secret()
{
local ns="$1"
local secret="$2"
local envvar="$3"
kubectl get secret -n "$ns" "$secret" -o jsonpath="{.data.$envvar}" \
| base64 --decode
}
# ------------------------------------------------------------------------------
# Execution
# Usage: display_help_and_exit
display_help_and_exit()
{
cat <<-EOF
Usage: $APP_NAME [options] <command>
$APP_NAME --help
$APP_NAME --version
$APP_NAME -n "acme*" -e "PROXY*" -d list-secrets
$APP_NAME -n "acme-corp" -s "api" -e "PASSWORD" decode-secret
Will help filtering a secret value among k8s secrets.
Commands:
ls, list-secrets List all secrets from all namespaces.
ds, decode-secret Decode a secret value.
Options:
-h, --help Print this help (this message).
-v, --version Prints version.
-x, --verbose Displays debug information (-x).
-d, --decode Decode the secret value while listing secrets. "grep" argument.
-e, --env Variable (envvar) name to filter values from. "grep" argument.
-n, --namespace Namespace name to filter secrets from.
-s, --secret Secret name to filter values from. Passed as "grep" argument.
-k, --keep Keep temporary file after execution, containing the contents
of the secrets.
You can send bugs and/or suggestions to $APP_AUTHOR.
EOF
exit 1
}
# Usage: main "$@"
main()
{
while :; do
case "${1-}" in
# Options ---------------------------
-h | --help)
display_help_and_exit
;;
-x | --verbose)
set -x
;;
-v | --version)
echo "$APP_VERSION"
exit 1
;;
-n | -ns | --ns | --namespace)
OPTION_FILTER_NS="$2"
shift
;;
-s | --secret)
OPTION_FILTER_SECRET="$2"
shift
;;
-e | --env)
OPTION_FILTER_ENV="$2"
shift
;;
-d | --decode)
OPTION_DECODE_SECRET="true"
;;
-k | --keep)
OPTION_KEEP_TMP_FILE="true"
;;
# Commands --------------------------
ls | list-secrets)
command_list_secrets "$OPTION_FILTER_NS" "$OPTION_FILTER_SECRET"
exit 0
;;
ds | decode-secret)
command_decode_secret "$OPTION_FILTER_NS" "$OPTION_FILTER_SECRET" "$OPTION_FILTER_ENV"
echo ""
exit 0
;;
# Treating mistakes -----------------
-?*)
err "Unknown option '$1'."
exit 3
;;
*)
break
;;
esac
shift
done
return 0
}
assert_env
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment