Created
July 26, 2023 17:45
-
-
Save pbatey/f7c77dc24b0be88dcf2c390cd8f8b375 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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