|
#!/usr/bin/env bash |
|
|
|
set -euo pipefail |
|
|
|
SCRIPT="${0##*/}" |
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
|
|
EXAMPLES_DIR="${SCRIPT_DIR}/examples" |
|
DEV_AGE_KEY="age-dev-key.txt" |
|
PRD_AGE_KEY="age-prod-key.txt" |
|
ALL_AGE_KEY="age-all-keys.txt" |
|
|
|
SECRET_SIMPLE_HELM_DEV="\ |
|
db_host: mysecret_db_host_dev |
|
db_user: mysecret_db_user_dev |
|
db_pass: mysecret_db_pass_dev" |
|
SECRET_SIMPLE_HELM_PROD="\ |
|
db_host: mysecret_db_host_prd |
|
db_user: mysecret_db_user_prd |
|
db_pass: mysecret_db_pass_prd" |
|
SECRET_SIMPLE_DEV="\ |
|
password: mysecretpassword_DEV |
|
apikey: mysecretapikey_DEV |
|
this_is_unencrypted: this will be unencrypted_DEV" |
|
SECRET_SIMPLE_PRD="\ |
|
password: mysecretpassword_PRD |
|
apikey: mysecretapikey_PRD |
|
this_is_unencrypted: this will be unencrypted_PRD" |
|
SECRET_SIMPLE="\ |
|
password: mysecretpassword |
|
apikey: mysecretapikey |
|
this_is_unencrypted: this will be unencrypted" |
|
SECRET_UNENCRYPTED="$SECRET_SIMPLE |
|
this_is_plaintext: this will also be unencrypted" |
|
SECRET_ENCRYPTED="$SECRET_SIMPLE |
|
just_encryptme: this will be encrypted but nothing else" |
|
|
|
## Set up example data |
|
rm -rf "${EXAMPLES_DIR}/helm" |
|
rm -f "${EXAMPLES_DIR}/terraform/*" "${EXAMPLES_DIR}/terraform-aws/*" |
|
rm -f "${EXAMPLES_DIR}/"secrets* "${EXAMPLES_DIR}/"enc* "${EXAMPLES_DIR}/"*.tf "${EXAMPLES_DIR}/"*.txt "${EXAMPLES_DIR}/"ex* |
|
mkdir -p "${EXAMPLES_DIR}/terraform" |
|
mkdir -p "${EXAMPLES_DIR}/terraform-aws" |
|
mkdir -p "${EXAMPLES_DIR}/helm" |
|
|
|
|
|
cd "${EXAMPLES_DIR}" |
|
|
|
echo "# >>> Create the identity keys..." |
|
[ -f "${DEV_AGE_KEY}" ] || age-keygen -o "${DEV_AGE_KEY}" |
|
[ -f "${PRD_AGE_KEY}" ] || age-keygen -o "${PRD_AGE_KEY}" |
|
[ -f "${ALL_AGE_KEY}" ] || cat "${DEV_AGE_KEY}" "${PRD_AGE_KEY}" > "${ALL_AGE_KEY}" |
|
|
|
# make them secure-ish (better still, add to a vault of sorts) |
|
chmod 600 "${DEV_AGE_KEY}" |
|
chmod 600 "${PRD_AGE_KEY}" |
|
chmod 600 "${ALL_AGE_KEY}" |
|
|
|
echo "# >>> Create an .env file" |
|
cat <<EOF > .env |
|
EXAMPLES_DIR="${EXAMPLES_DIR}" |
|
DEV_AGE_KEY="${DEV_AGE_KEY}" |
|
DEV_DEC_KEY=\$(cat "${DEV_AGE_KEY}") |
|
DEV_ENC_KEY=$(age-keygen -y "${DEV_AGE_KEY}") |
|
PRD_AGE_KEY="${PRD_AGE_KEY}" |
|
PRD_DEC_KEY=\$(cat "${PRD_AGE_KEY}") |
|
PRD_ENC_KEY=$(age-keygen -y "${PRD_AGE_KEY}") |
|
ALL_AGE_KEY="${ALL_AGE_KEY}" |
|
ALL_DEC_KEY=\$(cat "${ALL_AGE_KEY}") |
|
ALL_ENC_KEY=$(age-keygen -y "${DEV_AGE_KEY}"),$(age-keygen -y "${PRD_AGE_KEY}") |
|
MY_HELM_CHART=helm/dbmigrator |
|
echoI() { echo "$(tput setaf 4)$(tput setab 7)\$@$(tput sgr 0)"; } |
|
EOF |
|
source .env |
|
|
|
echo "# >>> Create an .sops.yaml config file" |
|
cat <<EOF > .sops.yaml |
|
creation_rules: |
|
- path_regex: k8s\..*\.yaml$ |
|
encrypted_regex: ^(data|stringData)$ |
|
age: ${ALL_ENC_KEY} |
|
- path_regex: \.dev\.yaml$ |
|
age: ${DEV_ENC_KEY} |
|
- path_regex: \.prd\.yaml$ |
|
age: ${PRD_ENC_KEY} |
|
- path_regex: \.common\.yaml$ |
|
age: ${ALL_ENC_KEY} |
|
- path_regex: \.unencrypted\.yaml$ |
|
unencrypted_regex: (_unencrypted|_plaintext)$ |
|
age: ${ALL_ENC_KEY} |
|
- path_regex: \.encrypted\.yaml$ |
|
encrypted_suffix: _encryptme |
|
age: ${ALL_ENC_KEY} |
|
- path_regex: \.nokey\.yaml$ |
|
EOF |
|
|
|
echo "# >>> Create secret yamls..." |
|
echo "$SECRET_SIMPLE_DEV" > secrets.01-simple.dev.yaml |
|
echo "$SECRET_SIMPLE_PRD" > secrets.02-simple.prd.yaml |
|
echo "$SECRET_SIMPLE" > secrets.03-simple.common.yaml |
|
echo "$SECRET_SIMPLE" > secrets.04-simple.nokey.yaml |
|
echo "$SECRET_UNENCRYPTED" > secrets.05-custom.unencrypted.yaml |
|
echo "$SECRET_ENCRYPTED" > secrets.06-custom.encrypted.yaml |
|
kubectl create secret generic my-secrets \ |
|
-n my-namespace \ |
|
--from-literal=username=admin \ |
|
--from-literal=password=mysecretpassword2022 \ |
|
-oyaml --dry-run=client > secrets.07-k8s.dev.yaml |
|
echo "$SECRET_SIMPLE_HELM_DEV" > helm/secrets.dev.yaml |
|
echo "$SECRET_SIMPLE_HELM_PROD" > helm/secrets.prd.yaml |
|
|
|
ls -1 secrets*.yaml |
|
|
|
echo "# >>> Add terraform example" |
|
cp ../_terraform-main.tf terraform/main.tf |
|
cp ../_terraform-main-aws.tf terraform-aws/main.tf |
|
|
|
echo "# >>> Add helm secret example" |
|
helm create $MY_HELM_CHART |
|
cp ../_helm-example-secret.yaml $MY_HELM_CHART/templates/secrets.yaml |
|
cp ../_helm-helmfile.yaml $MY_HELM_CHART/../helmfile.yaml |
|
echo 'db_host: ""' >> $MY_HELM_CHART/values.yaml |
|
echo 'db_user: ""' >> $MY_HELM_CHART/values.yaml |
|
echo 'db_pass: ""' >> $MY_HELM_CHART/values.yaml |
|
|
|
echo "# >>> Create example scripts..." |
|
cat <<'EOF' > ex01-encryption-config.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
|
|
echoI "# >>> SOPS Config..." |
|
yq .sops.yaml |
|
|
|
for f in $(find . -type f -name "secrets\.*\.yaml"); do |
|
echo |
|
echoI "# >>> Processing ${f}..." |
|
set -x |
|
sops --verbose -e "${f}" > "${f//secrets/enc}" |
|
set +x |
|
echo "# >>> Before..." |
|
yq '.' "${f}" |
|
echo "# >>> After..." |
|
yq 'del(.sops)' "${f//secrets/enc}" |
|
echo "# >>> SOPS file ENCRYPTION config..." |
|
grep -E "(sops:|*encrypted_regex:|*encrypted_suffix:)" "${f//secrets/enc}" | yq - |
|
echo "# >>> SOPS file AGE config..." |
|
yq '.sops.age' "${f//secrets/enc}" |
|
done |
|
|
|
EOF |
|
|
|
cat <<'EOF' > ex02-encryption-overrides.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
echoI "# >>> Overriding - encrypting PROD yaml with DEV key..." |
|
|
|
set -x |
|
SOPS_AGE_RECIPIENTS="${DEV_ENC_KEY}" \ |
|
sops -e secrets.02-simple.prd.yaml > enc.overridden.simple.prd.yaml |
|
set +x |
|
|
|
echoI "# >>> SOPS Config..." |
|
yq .sops.yaml |
|
|
|
echoI "# >>> Encrypted prod file using DEV key..." |
|
yq '.sops.age' enc.overridden.simple.prd.yaml |
|
EOF |
|
|
|
cat <<'EOF' > ex03-helm.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
|
|
MY_SECRET_DEV_FILE='helm/enc.dev.yaml' |
|
echoI "# >>> Helm - see https://github.com/jkroepke/helm-secrets/wiki/Usage" |
|
helm secrets --version &> /dev/null || { echo "Please install the helm secrets plugin"; exit 1; } |
|
echo |
|
echo "# >>> Helm - looking at the value..." |
|
set -x |
|
grep -r db_ ./$MY_HELM_CHART |
|
set +x |
|
echo |
|
echoI "# >>> Helm template - without the secrets file..." |
|
set -x |
|
helm template ./$MY_HELM_CHART \ |
|
| grep -B 2 db_ |
|
set +x |
|
echo |
|
echoI "# >>> Helm template - attempting to use wrong key..." |
|
set -x |
|
helm template ./$MY_HELM_CHART \ |
|
-f "secrets+age-import://${PRD_AGE_KEY}?${MY_SECRET_DEV_FILE}" \ |
|
|| echo "ERROR: Command failed" |
|
set +x |
|
echo |
|
echoI "# >>> Helm template - using correct key..." |
|
set -x |
|
helm template ./$MY_HELM_CHART \ |
|
-f "secrets+age-import://${DEV_AGE_KEY}?${MY_SECRET_DEV_FILE}" \ |
|
| grep -B 2 db_ |
|
set +x |
|
EOF |
|
|
|
cat <<'EOF' > ex04-helmfile.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
|
|
export SOPS_AGE_KEY_FILE=../age-all-keys.txt |
|
|
|
echoI "# >>> helmfile - dev environment" |
|
set -x |
|
MY_ENV=dev helmfile --selector job=dbmigrator -f helm/helmfile.yaml template | grep db_ |
|
set +x |
|
|
|
echoI "# >>> helmfile - prod environment" |
|
set -x |
|
MY_ENV=prd helmfile --selector job=dbmigrator -f helm/helmfile.yaml template | grep db_ |
|
set +x |
|
|
|
EOF |
|
|
|
cat <<'EOF' > ex05-terraform.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
|
|
echoI "# >>> Terraform... (SOPS_AGE_KEY_FILE=...)" |
|
echo |
|
echo "# >>> Terraform - init..." |
|
set -x |
|
terraform -chdir=terraform init |
|
set +x |
|
echo |
|
echoI "# >>> Terraform - using dev key file..." |
|
set -x |
|
SOPS_AGE_KEY_FILE=../$DEV_AGE_KEY terraform -chdir=terraform refresh |
|
set +x |
|
echo |
|
echoI "# >>> Terraform - using common key file..." |
|
set -x |
|
SOPS_AGE_KEY_FILE=../$ALL_AGE_KEY terraform -chdir=terraform refresh |
|
set +x |
|
echo |
|
echoI "# >>> Terraform - using INCORRECT prod key..." |
|
set -x |
|
SOPS_AGE_KEY_FILE=../$PRD_AGE_KEY terraform -chdir=terraform refresh \ |
|
|| echo "ERROR: Command failed" |
|
set +x |
|
EOF |
|
|
|
cat <<'EOF' > ex06-git.sh |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
|
. .env |
|
|
|
TEMP_DIR=$(mktemp -d) |
|
cd $TEMP_DIR |
|
echoI "# >>> Git setup..." |
|
echo |
|
set -x |
|
git init |
|
echo "my_key: 1234" > mysecrets.yaml |
|
export SOPS_AGE_RECIPIENTS="${DEV_ENC_KEY}" |
|
export SOPS_AGE_KEY_FILE="${EXAMPLES_DIR}/${DEV_AGE_KEY}" |
|
sops -e -i mysecrets.yaml |
|
git add mysecrets.yaml |
|
git commit -m "Init secrets yaml" mysecrets.yaml |
|
echo "my_key: 5678" > mysecrets.yaml |
|
sops -e -i mysecrets.yaml |
|
set +x |
|
echo |
|
echoI "# >>> Git WITHOUT sopsdiffer..." |
|
echo |
|
set -x |
|
git --no-pager diff |
|
set +x |
|
echo |
|
echoI "# >>> Git WITH sopsdiffer..." |
|
echo |
|
set -x |
|
git config diff.sopsdiffer.textconv "sops -d" |
|
cat .git/config |
|
set +x |
|
echo "*.yaml diff=sopsdiffer" > .gitattributes |
|
echo |
|
set -x |
|
cat .gitattributes |
|
set +x |
|
echo |
|
git --no-pager diff |
|
set +x |
|
cd - |
|
EOF |
|
|
|
|
|
# create instructions |
|
cat <<'EOF' >> "${EXAMPLES_DIR}/instructions.txt" |
|
|
|
|
|
#======================== |
|
# Example commands: |
|
#======================== |
|
|
|
EOF |
|
for f in $(ls ex*.sh); do |
|
chmod +x $f |
|
echo "./examples/${f}" >> "${EXAMPLES_DIR}/instructions.txt" |
|
done |
|
|
|
cat <<'EOF' >> "${EXAMPLES_DIR}/instructions.txt" |
|
|
|
|
|
#======================== |
|
# Advanced commands: |
|
#======================== |
|
|
|
|
|
# checkout |
|
cd examples/ |
|
SOPS_AGE_KEY_FILE=age-dev-key.txt sops -d enc.01-simple.dev.yaml |
|
export SOPS_AGE_KEY_FILE=age-dev-key.txt |
|
|
|
# enter demo env (to set variables) |
|
sops --config /dev/null -d ~/.secrets/demo.yaml | yq 'keys|.[]' - |
|
sops exec-env ~/.secrets/demo.yaml zsh |
|
|
|
# check |
|
sops -r --add-kms $DEMO_AWS_KEY_ARN --add-pgp $DEMO_GPG_PUB enc.01-simple.dev.yaml |
|
vim enc.01-simple.dev.yaml |
|
|
|
# change in-place |
|
sops -r -i --add-kms $DEMO_AWS_KEY_ARN --add-pgp $DEMO_GPG_PUB enc.01-simple.dev.yaml |
|
vim enc.01-simple.dev.yaml |
|
|
|
# running with remote keys |
|
unset SOPS_AGE_KEY_FILE |
|
sops -d enc.01-simple.dev.yaml |
|
|
|
# terraform |
|
cd terraform-aws |
|
|
|
# look at the main.tf |
|
vim main.tf |
|
diff ../terraform/main.tf main.tf |
|
|
|
# check before |
|
aws secretsmanager list-secrets | grep "as/Code" |
|
|
|
# set the following environment variables |
|
export TF_VAR_kms_key_id="$DEMO_AWS_KEY_ID" |
|
|
|
# create |
|
terraform init |
|
terraform plan -out tfplan |
|
terraform apply -auto-approve tfplan |
|
|
|
# check after |
|
aws secretsmanager list-secrets | grep "as/Code" |
|
|
|
# look, Ma! - no secrets! |
|
terraform state show \ |
|
aws_secretsmanager_secret.secrets\[\"password\"\] |
|
|
|
# destroy |
|
terraform destroy -auto-approve |
|
unset TF_VAR_kms_key_id |
|
|
|
# Additional clean up |
|
# - destroying only schedules keys for deletion |
|
# - trying to create the same keys will fail because the key still exists |
|
# - we need to delete them without recovery |
|
sops -d ../enc.01-simple.dev.yaml 2> /dev/null | yq 'keys|.[]' | xargs -I{} echo aws secretsmanager delete-secret --secret-id "Secrets/as/Code/{}" --force-delete-without-recovery |
|
sops -d ../enc.01-simple.dev.yaml 2> /dev/null | yq 'keys|.[]' | xargs -I{} aws secretsmanager delete-secret --secret-id "Secrets/as/Code/{}" --force-delete-without-recovery |
|
EOF |
|
|
|
cat "${EXAMPLES_DIR}/instructions.txt" |