Skip to content

Instantly share code, notes, and snippets.

@pbatey
Created July 26, 2023 17:45
Show Gist options
  • Save pbatey/f7c77dc24b0be88dcf2c390cd8f8b375 to your computer and use it in GitHub Desktop.
Save pbatey/f7c77dc24b0be88dcf2c390cd8f8b375 to your computer and use it in GitHub Desktop.
#
# This file should be sourced from ~/.bashrc or ~/.zshrc
#
# export VAULT_ADDR=<your vault URL>
# source ~/.yv-funcs
#
# It requires yq, gdate, and gum to be installed (brew install yq coreutils gum)
#
# It requires a yaml file ~/.yv-funcs.rc to contain entries:
#
# like
#
# <VAULT_ORG>:
# oidc-role: <OIDC_ROLE>
#
# or
#
# <VAULT_ORG>:
# github-token: <GITHUB_TOKEN>
#
# Create a GitHub Personal Access Token here:
#
# https://github.com/settings/tokens
#
# Then run something like
# yv ls
# yv get <secret> data.yaml
# yv put <secret> data.yaml
#
if [[ "$(type verbose 2>/dev/null | head -1 | awk '{ print $4 }')" != 'function' ]]; then
# not already defined
function verbose { echo -e "\033[0;33m$*\033[0;m"; } # yellow
fi
if [[ "$(type error 2>/dev/null | head -1 | awk '{ print $4 }')" != 'function' ]]; then
# not already defined
function error { echo -e "\033[0;31m$*\033[0;m"; } # red
fi
if [[ "$(type info 2>/dev/null | head -1 | awk '{ print $4 }')" != 'function' ]]; then
# not already defined
function info { echo -e "\033[0;34m$*\033[0;m"; } # blue
fi
function vault-logout () {
VAULT_ORG=
VAULT_DIR=
VAULT_EXPIRE_TIME=
verbose rm -f ~/.vault-token
rm -f ~/.vault-token
}
function vault-login () {
if [[ -z $VAULT_ORG ]]; then
org=$(yq 'keys|.[]' ~/.yv-funcs.rc)
if [[ $(echo $org | wc -l) -gt 1 ]]; then org=$(echo $org | gum choose); fi
VAULT_ORG=$org
fi
if [[ $(yq '.vital | has("oidc-role")' ~/.yv-funcs.rc) == true ]]; then
oidc_role=$(yq e ".$VAULT_ORG.oidc-role" ~/.yv-funcs.rc)
args=(-method=oidc role=${VAULT_ORG}_${oidc_role})
verbose_args=${args[@]}
elif [[ $(yq '.vital | has("github-token")' ~/.yv-funcs.rc) == true ]]; then
token=$(yq e ".$VAULT_ORG.github-token" ~/.yv-funcs.rc)
args=(-method=github -path=github_$VAULT_ORG token=$token)
verbose_args=(-method=github -path=github_$VAULT_ORG 'token=<HIDDEN>')
else
return 0
fi
quiet=false
if [[ "$1" == '-q' ]]; then
quiet=true
fi
$quiet || verbose "vault login ${verbose_args[@]}" 1>&2
vault login -no-print ${args[@]}
VAULT_EXPIRE_TIME=$(vault token lookup -format yaml 2> /dev/null | yq e '.data.expire_time // ""' -)
}
function vault-has-expired () {
if [[ -z $VAULT_EXPIRE_TIME ]]; then
VAULT_EXPIRE_TIME=$(vault token lookup -format yaml 2> /dev/null | yq e '.data.expire_time // ""' -)
fi
if [[ -z $VAULT_EXPIRE_TIME ]]; then
return 0
fi
expire_ts=$(gdate --date=${VAULT_EXPIRE_TIME:-0} +%s%N)
now_ts=$(gdate +%s%N)
if [[ $expire_ts -lt $now_ts ]]; then return 0; else return 1; fi
}
function yv-dump () {
local vflag=$1
if [[ "$vflag" == "-v" ]]; then
shift
else
vflag=""
fi
local indent=$3
local dir
if [[ $# -le 1 ]]; then
dir=${VAULT_DIR%/}/$1
echo ${dir%/}:
else
dir=${1%/}/$2
echo ${indent}${2%/}:
fi
indent+=" "
if [[ "$vflag" == "-v" ]]; then verbose "vault list $dir | grep '/$'"; fi
vault list $dir | grep '/$' | while read d; do
yv-dump $vflag $dir $d $indent
done
if [[ "$vflag" == "-v" ]]; then verbose "vault list $dir | tail +3 | grep -v '/$'"; fi
vault list $dir | tail +3 | grep -v '/$' | while read s; do
echo ${indent}${s}:
if [[ "$vflag" == "-v" ]]; then verbose "vault kv get $dir/$s"; fi
vault kv get -format yaml ${dir%/}/$s | yq e '.data' - | sed -e "s/^/$indent /"
done
}
function yv () {
quiet=false
if [[ -p /dev/stdout || ! -t 1 ]]; then
# pipe or redirect (not terminal)
quiet=true
q=-q
fi
fnc="yv "
if [[ "$1" == "--cli" ]]; then
shift
fnc=""
fi
cmd=$1
cmd=${cmd:-help}
shift 2> /dev/null
if [[ "$cmd" == 'login' ]]; then
vault-logout
vault-login
VAULT_DIR=${VAULT_DIR:-secret/$VAULT_ORG}
return
fi
if [[ "$cmd" != 'logout' ]]; then
if vault-has-expired; then vault-login $q; fi
VAULT_DIR=${VAULT_DIR:-secret/$VAULT_ORG}
fi
if [[ "$cmd" == 'get' ]]; then
if [[ $# -lt 1 ]]; then error "usage: ${fnc}get <secret> [<data.yaml>]" 1>&2; return 1; fi
dir=$VAULT_DIR/$1
dir=$(dirname $dir)/$(basename $dir)
file=$2
if [[ "$file" != "" ]]; then
$quiet || verbose "vault kv get -format yaml $dir | yq e '.data' - > $file" 1>&2
vault kv get -format yaml $dir | yq e '.data' - > $file
else
$quiet || verbose "vault kv get -format yaml $dir | yq e '.data' -" 1>&2
vault kv get -format yaml $dir | yq e '.data' -
fi
elif [[ "$cmd" == 'set-value' ]]; then
if [[ $# -ne 2 ]]; then error "usage: ${fnc}set-value <secret> <value>" 1>&2; return 1; fi
dir=$VAULT_DIR/$1
dir=$(dirname $dir)/$(basename $dir)
tmpfile=$(mktemp /tmp/yv.XXXXXX)
$quiet || verbose "echo value: \"$2\" | yq -o=json > $tmpfile && vault kv put $dir @$tmpfile" 1>&2
echo value: "$2" | yq -o=json > $tmpfile && vault kv put $dir @$tmpfile
rm -f $tmpfile
elif [[ "$cmd" == 'put' ]]; then
if [[ $# -ne 2 ]]; then error "usage: ${fnc}put <secret> <data.yaml>" 1>&2; return 1; fi
dir=$VAULT_DIR/$1
dir=$(dirname $dir)/$(basename $dir)
tmpfile=$(mktemp /tmp/yv.XXXXXX)
$quiet || verbose "yq e -o=json $2 > $tmpfile && vault kv put $dir @$tmpfile" 1>&2
yq e -o=json $2 > $tmpfile && vault kv put $dir @$tmpfile
rm -f $tmpfile
elif [[ "$cmd" == 'rm' || "$cmd" == 'delete' ]]; then
p=true
if [[ "$1" == '-f' ]]; then shift; p=false; fi
if [[ $# -ne 1 ]]; then error "usage: $fnc$cmd [-f] <secret>" 1>&2; return 1; fi
dir=$VAULT_DIR/$1
dir=$(dirname $dir)/$(basename $dir)
if [[ "$p" == true ]]; then
echo -n "remove $dir? " && read confirm && echo $confirm | grep -q '^[Yy]$'
if [[ $? == 1 ]]; then return 1; fi
fi
$quiet || verbose "vault kv delete $dir" 1>&2
vault kv delete $dir
elif [[ "$cmd" == 'ls' || "$cmd" == 'list' ]]; then
dir=$VAULT_DIR/$1
dn=$(dirname $dir)
if [[ "$dn" == "." ]]; then
dir=$(basename $dir)
else
dir=$dn/$(basename $dir)
fi
$quiet || verbose "vault list $dir" 1>&2
vault list $dir
elif [[ "$cmd" == 'pwd' ]]; then
echo $VAULT_DIR
elif [[ "$cmd" == 'logout' ]]; then
vault-logout
elif [[ "$cmd" == 'dump' ]]; then
yv-dump $@
elif [[ "$cmd" == 'ui' ]]; then
pbcopy < ~/.vault-token
verbose open "$VAULT_ADDR/ui/vault/secrets/secret/list/${VAULT_DIR#secret/}"
info "Note: The vault token has been copied to paste buffer."
open "$VAULT_ADDR/ui/vault/secrets/secret/list/${VAULT_DIR#secret/}"
elif [[ "$cmd" == 'cd' ]]; then
if [[ $# -lt 1 ]]; then
VAULT_DIR=secret/$VAULT_ORG
elif [[ $# -eq 1 ]]; then
for d in $(echo "$1" | sed -e "s+/+ +g"); do
if [[ $d == ".." ]]; then
VAULT_DIR=$(dirname $VAULT_DIR)
elif [[ $d != "." ]]; then
dir=$VAULT_DIR/$d
dir=$(dirname $dir)/$(basename $dir)
VAULT_DIR=$dir
fi
done
else
error "usage: ${fnc}cd <path>" 1>&2
fi
elif [[ "$cmd" == 'cli' ]]; then
while true; do
echo -n "> " && read cmd args
if [[ "$cmd" == "exit" ]]; then return; fi
if [[ "$cmd" != "" ]]; then
yv --cli $cmd $args
else
if echo -n "exit? " && read e && echo $e | grep -q "^[Yy]$"; then
return
fi
fi
done
else
cat << EOF
usage:
${fnc}(list|ls) [<secret-path>]
${fnc}get <secret> [<data.yaml>]
${fnc}put <secret> <data.yaml>
${fnc}set-value <secret> <value> \# set secret to a single \'value\'
${fnc}dump # get all secrets in the tree
${fnc}(rm|delete) [-f] [<path>]
${fnc}cd [<path>]
${fnc}pwd
${fnc}logout
${fnc}login
${fnc}ui # open the vault url in a browser and copy the vault token to the paste buffer
EOF
if [[ "$fnc" != "" ]]; then
echo " ${fnc}cli"
else
echo " exit"
fi
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment