Skip to content

Commit

Permalink
Add basic support for HashiCorp Vault
Browse files Browse the repository at this point in the history
  • Loading branch information
Just-Insane committed Mar 16, 2019
1 parent 72fa948 commit bbd02c1
Showing 1 changed file with 186 additions and 29 deletions.
215 changes: 186 additions & 29 deletions secrets.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
#!/usr/bin/env bash
# shellcheck disable=SC1003

# Parts of this project are MIT Licensed, they will be denoted below.

# MIT License

# Original work Copyright (c) 2017 Jonathan Peres
# Modified work Copyright (c) 2019 Just_Insane

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# The suffix to use for decrypted files. The default can be overridden using
# the HELM_SECRETS_DEC_SUFFIX environment variable.
Expand Down Expand Up @@ -57,6 +83,18 @@ This allows you to first decrypt the file, edit it, then encrypt it again.
You can use plain sops to encrypt - https://github.com/mozilla/sops
Vault secrets
If VAULT_TOKEN env variable is set, automatically store values in Vault.
Secret values must be entered into the helm chart as `changeme`, for example
db:
name: nextcloud
user: nextcloud
password: changeme
would prompt to enter a value for the db password.
Example:
$ ${HELM_BIN} secrets enc <SECRET_FILE_PATH>
$ git add <SECRET_FILE_PATH>
Expand All @@ -75,6 +113,18 @@ Produces ${DEC_SUFFIX} file.
You can use plain sops to decrypt specific files - https://github.com/mozilla/sops
Vault secrets
If VAULT_TOKEN env variable is set, automatically pull values from Vault in a plaintext file.
Values must be pulled in order to update or install via Helm.
For example:
db:
name: nextcloud
user: nextcloud
password: <value entered during enc command>
Example:
$ ${HELM_BIN} secrets dec <SECRET_FILE_PATH>
Expand Down Expand Up @@ -208,6 +258,100 @@ is_help() {
esac
}

# Parses yaml document
# Based on https://github.com/jasperes/bash-yaml
parse_yaml() {
local yaml_file=$1
local s
local w
local fs

s='[[:space:]]*'
w='[a-zA-Z0-9_.-]*'
fs="$(echo @|tr @ '\034')"

(
sed -e '/- [^\“]'"[^\']"'.*: /s|\([ ]*\)- \([[:space:]]*\)|\1-\'$'\n'' \1\2|g' |

sed -ne '/^--/s|--||g; s|\"|\\\"|g; s/[[:space:]]*$//g;' \
-e "/#.*[\"\']/!s| #.*||g; /^#/s|#.*||g;" \
-e "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)${s}[:-]$s\(.*\)$s\$|\1$fs\2$fs\3|p" |

awk -F"$fs" '{
indent = length($1)/2;
if (length($2) == 0) { conj[indent]="+";} else {conj[indent]="";}
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s%s=(\"%s\")\n", "",vn, $2, conj[indent-1],$3);
}
}' |

sed -e 's/_=/+=/g' |

awk 'BEGIN {
FS="=";
OFS="="
}
/(-|\.).*=/ {
gsub("-|\\.", "_", $1)
}
{ print }'
) < "$yaml_file"
}

# Created environment variables for secrets key invocations as well as the image repository
# Based on https://github.com/jasperes/bash-yaml
create_variables() {
local yaml_file="$1"
eval "$(parse_yaml "$yaml_file" | awk '/changeme/{print $0}')"
eval "$(parse_yaml "$yaml_file" | awk '/image_repository/{print $0}')"
}

# Prompts user for secret material and uploads to vault K/V Store
set_secrets() {
report () { echo "${1%%=*}"; };

envsarray=()
while IFS= read -r line; do
envsarray+=( "$line" )
done < <( set -o posix +o allexport; set | grep "changeme" | awk 'match($0, "\.=") {print substr($0, 1, RSTART)}' )

for env in "${envsarray[@]}";
do
echo "Enter a secret value for $image_repository/$env"
read -r usersecret
vault kv put secret/helm/$image_repository/$env value=$usersecret
done
}

# Pulls secret material from vault K/V store and saves it to a .dec file, needed by helm to update or deploy
get_secrets() {
report () { echo "${1%%=*}"; };

envsarray=()
while IFS= read -r line; do
envsarray+=( "$line" )
done < <( set -o posix +o allexport; set | grep "changeme" | awk 'match($0, "\.=") {print substr($0, 1, RSTART)}' )

yml_dec="$yml.dec"
cp $yml $yml_dec

for env in "${envsarray[@]}";
do
sec_values=`vault kv get secret/helm/$image_repository/$env | grep "value" | awk '/value/{print $2}'`
echo "Secret Values = $sec_values"
for sec in "${sec_values[@]}";
do
#this will fail if "changeme" is on the first line of the file, but is required for GNU sed
sed -i.dec "1,// s/changeme/$sec/" $yml_dec
rm "$yml_dec.dec"
done
done
}

encrypt_helper() {
local dir=$(dirname "$1")
local yml=$(basename "$1")
Expand All @@ -216,18 +360,24 @@ encrypt_helper() {
local ymldec=$(sed -e "s/\\.yaml$/${DEC_SUFFIX}/" <<<"$yml")
[[ -e $ymldec ]] || ymldec="$yml"

if [[ $(grep -C10000 'sops:' "$ymldec" | grep -c 'version:') -gt 0 ]]
if [[ -z "${VAULT_TOKEN}" ]]
then
echo "Already encrypted: $ymldec"
return
fi
if [[ $yml == $ymldec ]]
then
sops --encrypt --input-type yaml --output-type yaml --in-place "$yml"
echo "Encrypted $yml"
if [[ $(grep -C10000 'sops:' "$ymldec" | grep -c 'version:') -gt 0 ]]
then
echo "Already encrypted: $ymldec"
return
fi
if [[ $yml == $ymldec ]]
then
sops --encrypt --input-type yaml --output-type yaml --in-place "$yml"
echo "Encrypted $yml"
else
sops --encrypt --input-type yaml --output-type yaml "$ymldec" > "$yml"
echo "Encrypted $ymldec to $yml"
fi
else
sops --encrypt --input-type yaml --output-type yaml "$ymldec" > "$yml"
echo "Encrypted $ymldec to $yml"
create_variables $yml
set_secrets
fi
}

Expand Down Expand Up @@ -265,27 +415,34 @@ decrypt_helper() {

__dec=0
[[ -e "$yml" ]] || { echo "File does not exist: $yml"; exit 1; }
if [[ $(grep -C10000 'sops:' "$yml" | grep -c 'version:') -eq 0 ]]
then
echo "Not encrypted: $yml"
__ymldec="$yml"
else
__ymldec=$(sed -e "s/\\.yaml$/${DEC_SUFFIX}/" <<<"$yml")
if [[ -e $__ymldec && $__ymldec -nt $yml ]]
then
echo "$__ymldec is newer than $yml"
else
sops --decrypt --input-type yaml --output-type yaml "$yml" > "$__ymldec" || { rm "$__ymldec"; exit 1; }
__dec=1
fi
fi

if [[ ${BASH_VERSINFO[0]} -lt 4 ]]
if [[ -z "${VAULT_TOKEN}" ]]
then
[[ $__ymldec_var ]] && eval $__ymldec_var="'$__ymldec'"
[[ $__dec_var ]] && eval $__dec_var="'$__dec'"
if [[ $(grep -C10000 'sops:' "$yml" | grep -c 'version:') -eq 0 ]]
then
echo "Not encrypted: $yml"
__ymldec="$yml"
else
__ymldec=$(sed -e "s/\\.yaml$/${DEC_SUFFIX}/" <<<"$yml")
if [[ -e $__ymldec && $__ymldec -nt $yml ]]
then
echo "$__ymldec is newer than $yml"
else
sops --decrypt --input-type yaml --output-type yaml "$yml" > "$__ymldec" || { rm "$__ymldec"; exit 1; }
__dec=1
fi
fi

if [[ ${BASH_VERSINFO[0]} -lt 4 ]]
then
[[ $__ymldec_var ]] && eval $__ymldec_var="'$__ymldec'"
[[ $__dec_var ]] && eval $__dec_var="'$__dec'"
fi
true # just so that decrypt_helper will exit with a true status on no error
else
create_variables $yml
get_secrets
fi
true # just so that decrypt_helper will exit with a true status on no error
}


Expand Down Expand Up @@ -495,4 +652,4 @@ case "${1:-help}" in
;;
esac

exit 0
exit 0

0 comments on commit bbd02c1

Please sign in to comment.