Skip to content

Instantly share code, notes, and snippets.

@jeffmccune
Created January 5, 2021 18:13
Show Gist options
  • Save jeffmccune/3d781c62462813b3fec9772f742ce057 to your computer and use it in GitHub Desktop.
Save jeffmccune/3d781c62462813b3fec9772f742ce057 to your computer and use it in GitHub Desktop.
Execute a command with Google Service Account credentials and cleanup after
#! /bin/bash
#
# Execute a command with GOOGLE_APPLICATION_CREDENTIALS set to point to a
# Google Service Account key JSON file. The key data is managed by vault and
# the lease is released on exit.
#
# Requires: vault, jq, base64
: "${VAULT_ADDR:=https://vault.example}"
: "${DEPLOY_ENV:=dev}"
export DEPLOY_ENV
rval=100
# Arguments to pass to vault
declare -a vault_args
while [[ $# -gt 0 ]]; do
[[ "$1" == '--' ]] && break
vault_args+=("$1")
shift
done
shift
usage() {
cat <<EOF
Usage: $(basename $0) gcp/key/myroleset -- command
GOOGLE_APPLICATION_CREDENTIALS is set to the service account key file
associated with the Vault roleset myroleset. The vault lease is revoked and
the credentials file deleted after command completes. The return value of
command is returned.
EOF
}
notoken() {
# shellcheck disable=SC2016
echo "Error: VAULT_TOKEN is unset" >&2
echo "Browse to ${VAULT_ADDR} select Copy Token after logging in."
}
if [[ $# -eq 0 ]]; then
usage
echo "Error: no command specified"
exit 99
fi
if [[ -z ${VAULT_TOKEN} ]]; then
notoken
exit 1
fi
tmpdir="$(mktemp -d)"
mytemp() {
TMPDIR="${tmpdir}" mktemp
}
renew() {
if [[ -n ${LEASE:-} ]]; then
vault lease renew "${LEASE}"
fi
}
revoke() {
if [[ -n ${LEASE:-} ]]; then
vault lease revoke "${LEASE}" >/dev/null
fi
}
finish() {
if [[ -e "${tmpdir:-}" ]]; then
rm -rf "${tmpdir}"
fi
revoke
exit ${rval:-100}
}
trap finish EXIT
set -eu -o pipefail
# Store the vault fields as json
json="$(mytemp)"
vault read -format=json "${vault_args[@]}" > "$json"
# Extract the Google Service Account key JSON into creds
creds="$(mytemp)"
jq -r .data.private_key_data "${json}" | base64 -d > "${creds}"
# Make the credentials available as Application Default Credentials.
# See: https://cloud.google.com/docs/authentication/production#passing_variable
export GOOGLE_APPLICATION_CREDENTIALS="${creds}"
# gcloud variables
export CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE="${creds}"
# for tools which can't read the filesystem, but can read the environment
GOOGLE_CREDENTIALS="$(<"${creds}")"
export GOOGLE_CREDENTIALS
# Store the lease for later revocation of the key
LEASE="$(jq -r .lease_id "${json}")"
# Reset VAULT_ADDR and VAULT_TOKEN based on the deployment environment. This
# allows credentials from one vault to be used to manage another vault instance
# using terraform.
eval 'addr=${VAULT_ADDR'_${DEPLOY_ENV}':-}'
eval 'tokn=${VAULT_TOKEN'_${DEPLOY_ENV}':-}'
if [[ -n "${DEBUG:-}" ]]; then
echo "# Identity: $(echo "${GOOGLE_CREDENTIALS}" | jq -r '.client_email')" >&2
fi
# Execute the passed command. VAULT_ADDR and VAULT_TOKEN are set in a subshell
# to preserve the address and token where the credentials need to be revoked.
if [[ -n "${addr:-}" ]] && [[ -n "${tokn:-}" ]]; then
(VAULT_ADDR="$addr"; VAULT_TOKEN="$tokn"; "$@")
rval=$?
else
"$@"
rval=$?
fi
@jeffmccune
Copy link
Author

Usage:

with_creds gcp/key/prod-admin -- terragrunt apply

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment