diff --git a/.github/workflows/ci.yaml.example b/.github/workflows/ci.yaml.example new file mode 100644 index 0000000..2642fc5 --- /dev/null +++ b/.github/workflows/ci.yaml.example @@ -0,0 +1,40 @@ + +--- +name: CI + +env: + MAVEN_CLI_OPTS: '-Dspring.main.banner-mode=off --batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true' + MAVEN_OPTS: '-Dhttps.protocols=TLSv1.2 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true' + +on: + pull_request: + push: + branches: + - master + +jobs: + shellcheck: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Lint check + uses: azohra/shell-linter@v0.3.0 + with: + path: "*.sh" + + shfmt: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Download shfmt + run: curl -sSL $SHFMT_URL -o ~/shfmt && chmod +x ~/shfmt + env: + SHFMT_URL: https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 + - name: Run shfmt + run: ~/shfmt -l -w -i 2 . + - name: Test if syntax is correct + run: git diff --exit-code && echo "shfmt OK" diff --git a/install-binary.sh b/install-binary.sh index 2d38494..7b69723 100755 --- a/install-binary.sh +++ b/install-binary.sh @@ -1,85 +1,66 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh -set -ueo pipefail +set -eu -SOPS_VERSION="3.0.4" -SOPS_DEB_URL="https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops_${SOPS_VERSION}_amd64.deb" -SOPS_DEB_SHA="9d9f319882ba05e7050be91bdfc396167ac9b00e2e6f634a647d55ac97915bb6" +SOPS_VERSION="3.5.0" SOPS_LINUX_URL="https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux" -SOPS_LINUX_SHA="e185d2752defdcb18c054f67682a6684c72d6a6bf2341f6bef1dd7d33a110459" +SOPS_LINUX_SHA="610fca9687d1326ef2e1a66699a740f5dbd5ac8130190275959da737ec52f096" RED='\033[0;31m' -GREEN='\033[0;32m' +#GREEN='\033[0;32m' #BLUE='\033[0;34m' -YELLOW='\033[1;33m' +#YELLOW='\033[1;33m' NOC='\033[0m' -# Find some tools -case "${HELM_BIN}" in - helm) - HELM_DIR="$(dirname $(command -v helm))" - ;; - *) - HELM_DIR="$(dirname ${HELM_BIN})" - ;; -esac - -get_sha_256 () { - if command -v sha256sum > /dev/null; then res=$(sha256sum $1) - elif command -v shasum > /dev/null; then res=$(shasum -a 256 $1) - else res=$(/usr/bin/shasum -a 256 $1) - fi - echo $res | cut -d ' ' -f 1 +download() { + if command -v curl >/dev/null; then + curl -sSfL "$1" + elif command -v wget >/dev/null; then + wget -q -O- "$1" + else + return 1 + fi } -# Install the helm wrapper in the same dir as helm itself. That's not -# guaranteed to work, but it's better than hard-coding it. -HELM_WRAPPER="${HELM_DIR}/helm-wrapper" +get_sha_256() { + if command -v sha256sum >/dev/null; then + res=$(sha256sum "$1") + elif command -v shasum >/dev/null; then + res=$(shasum -a 256 "$1") + else + res='' + fi + + echo "$res" | cut -d ' ' -f 1 +} if hash sops 2>/dev/null; then - echo "sops is already installed:" - sops --version + echo "sops is already installed: " + sops --version else - - # Try to install sops. - - ### Mozilla SOPS binary install - if [ "$(uname)" == "Darwin" ]; - then - brew install sops - elif [ "$(uname)" == "Linux" ]; - then - if which dpkg; - then - curl -sL "${SOPS_DEB_URL}" > /tmp/sops - if [ "$(get_sha_256 /tmp/sops)" == "${SOPS_DEB_SHA}" ]; - then - sudo dpkg -i /tmp/sops; - else - echo -e "${RED}Wrong SHA256${NOC}" - fi - else - curl -sL "${SOPS_LINUX_URL}" > /tmp/sops - if [ "$(get_sha_256 /tmp/sops)" == "${SOPS_LINUX_SHA}" ]; - then - chmod +x /tmp/sops - mv /tmp/sops /usr/local/bin/ - else - echo -e "${RED}Wrong SHA256${NOC}" - fi - fi - rm /tmp/sops 2>/dev/null || true + # Try to install sops. + if [ "$(uname)" = "Darwin" ]; then + brew install sops + elif [ "$(uname)" = "Linux" ]; then + if ! download "${SOPS_LINUX_URL}" >/tmp/sops; then + printf "${RED}%s${NOC}\n" "Can't download SOPS ..." else - echo -e "${RED}No SOPS package available${NOC}" - exit 1 + SOPS_SHA256="$(get_sha_256 /tmp/sops)" + if [ "${SOPS_SHA256}" = "${SOPS_LINUX_SHA}" ] || [ "${SOPS_SHA256}" = "" ]; then + chmod +x /tmp/sops + mv /tmp/sops /usr/local/bin/ + else + printf "${RED}%s${NOC}\n" "Wrong SHA256" + fi + rm -f /tmp/sops fi + else + printf "${RED}%s${NOC}\n" "No SOPS package available" + exit 1 + fi fi -### git diff config -if [ -x "$(command -v git --version)" ]; -then - git config --global diff.sopsdiffer.textconv "sops -d" -else - echo -e "${RED}[FAIL]${NOC} Install git command" - exit 1 +# If git is no available, fail silent. +if hash git 2>/dev/null; then + git config --global diff.sopsdiffer.textconv "sops -d" fi diff --git a/secrets.sh b/secrets.sh index 599c867..49a0c77 100755 --- a/secrets.sh +++ b/secrets.sh @@ -1,4 +1,6 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +set -eu # The suffix to use for decrypted files. The default can be overridden using # the HELM_SECRETS_DEC_SUFFIX environment variable. @@ -7,39 +9,8 @@ DEC_SUFFIX="${HELM_SECRETS_DEC_SUFFIX:-.yaml.dec}" # Make sure HELM_BIN is set (normally by the helm command) HELM_BIN="${HELM_BIN:-helm}" -getopt --test > /dev/null -if [[ $? -ne 4 ]] -then - # Check if gnu-getopt is installed - if [ -x /usr/local/opt/gnu-getopt/bin/getopt ] - then - /usr/local/opt/gnu-getopt/bin/getopt --test > /dev/null - if [[ $? -ne 4 ]] - then - GNU_GETOPT=0 - else - GNU_GETOPT=1 - export PATH="/usr/local/opt/gnu-getopt/bin:$PATH" - fi - else - GNU_GETOPT=0 - fi - - if [ "${GNU_GETOPT}" -ne 1 ]; then - cat < wrapper that decrypts secrets[.*].yaml files before running helm EOF } enc_usage() { - cat < - -Typical usage: - $ ${HELM_BIN} secrets install -n i1 stable/nginx-ingress -f values.test.yaml -f secrets.test.yaml - -EOF -} - -template_usage() { - cat < - -Typical usage: - $ ${HELM_BIN} secrets template -n i1 stable/nginx-ingress -f values.test.yaml -f secrets.test.yaml - -EOF -} - -upgrade_usage() { - cat < -This is a wrapper for the "helm upgrade" command. It will detect -f and +This is a wrapper for "helm ". It will detect -f and --values options, and decrypt any secrets.*.yaml files before running "helm -upgrade". +". Example: $ ${HELM_BIN} secrets upgrade - -Typical usage: - $ ${HELM_BIN} secrets upgrade i1 stable/nginx-ingress -f values.test.yaml -f secrets.test.yaml - -EOF -} - -lint_usage() { - cat < Typical usage: + $ ${HELM_BIN} secrets upgrade i1 stable/nginx-ingress -f values.test.yaml -f secrets.test.yaml $ ${HELM_BIN} secrets lint ./my-chart -f values.test.yaml -f secrets.test.yaml EOF } -diff_usage() { - cat < - -Typical usage: - $ ${HELM_BIN} secrets diff upgrade i1 stable/nginx-ingress -f values.test.yaml -f secrets.test.yaml +is_help() { + case "$1" in + -h | --help | help) + return 0 + ;; + *) + return 1 + ;; + esac +} -EOF +is_file_encrypted() { + grep -q 'sops:' "${1}" && grep -q 'version:' "${1}" } -is_help() { - case "$1" in - -h|--help|help) - return 0 - ;; - *) - return 1 - ;; - esac +file_dec_name() { + echo "$(dirname "${1}")/$(basename "${1}" ".yaml")${DEC_SUFFIX}" } encrypt_helper() { - local yml=$1 - [[ -e "$yml" ]] || { echo "File does not exist: $yml"; exit 1; } - local ymldec=$(sed -e "s/\\.yaml$/${DEC_SUFFIX}/" <<<"$yml") - [[ -e $ymldec ]] || ymldec="$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 + dir=$(dirname "$1") + file=$(basename "$1") + + cd "$dir" + + if [ ! -f "${file}" ]; then + echo "File does not exist: $dir/${file}" + exit 1 + fi + + file_dec="$(file_dec_name "${file}")" + + if [ ! -f "${file_dec}" ]; then + file_dec="${file}" + fi + + if is_file_encrypted "${file_dec}"; then + echo "Already encrypted: ${file_dec}" + return + fi + + if [ "${file}" = "${file_dec}" ]; then + sops --encrypt --input-type yaml --output-type yaml --in-place "${file}" + echo "Encrypted ${file}" + else + sops --encrypt --input-type yaml --output-type yaml "${file_dec}" >"${file}" + echo "Encrypted ${file_dec} to ${file}" + fi } enc() { - if is_help "$1" - then - enc_usage - return - fi - yml="$1" - if [[ ! -f "$yml" ]] - then - echo "$yml doesn't exist." - else - echo "Encrypting $yml" - encrypt_helper "$yml" - fi + if is_help "$1"; then + enc_usage + return + fi + + file="$1" + + if [ ! -f "${file}" ]; then + echo "${file} doesn't exist." + else + echo "Encrypting ${file}" + encrypt_helper "${file}" + fi } -# Name references ("declare -n" and "local -n") are a Bash 4.3+ feature. -# For previous versions, work around using eval. decrypt_helper() { - local yml="$1" __ymldec __dec - - if [[ ${BASH_VERSINFO[0]} -lt 4 || ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 3 ]] - then - local __ymldec_var='' __dec_var='' - [[ $# -ge 2 ]] && __ymldec_var=$2 - [[ $# -ge 3 ]] && __dec_var=$3 - [[ $__dec_var ]] && eval $__dec_var=0 - else - [[ $# -ge 2 ]] && local -n __ymldec=$2 - [[ $# -ge 3 ]] && local -n __dec=$3 - fi - - __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 || ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 3 ]] - 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 -} + file="${1}" + + if [ ! -f "$file" ]; then + echo "File does not exist: ${file}" + exit 1 + fi + + if ! is_file_encrypted "${file}"; then + return 1 + fi + file_dec="$(file_dec_name "${file}")" + + if ! sops --decrypt --input-type yaml --output-type yaml --output "${file_dec}" "${file}"; then + echo "Error while decrypting file: ${file}" + exit 1 + fi + + return 0 +} dec() { - if is_help "$1" - then - dec_usage - return - fi - yml="$1" - if [[ ! -f "$yml" ]] - then - echo "$yml doesn't exist." - else - echo "Decrypting $yml" - decrypt_helper "$yml" - fi + if is_help "$1"; then + dec_usage + return + fi + + file="$1" + + if [ ! -f "${file}" ]; then + echo "${file} doesn't exist." + else + echo "Decrypting ${file}" + decrypt_helper "${file}" + fi } view_helper() { - local yml="$1" - [[ -e "$yml" ]] || { echo "File does not exist: $yml"; exit 1; } - sops --decrypt --input-type yaml --output-type yaml "$yml" + file="$1" + + if [ ! -f "${file}" ]; then + echo "File does not exist: ${file}" + exit 1 + fi + + exec sops --decrypt --input-type yaml --output-type yaml "${file}" } view() { - if is_help "$1" - then - view_usage - return - fi - local yml="$1" - view_helper "$yml" + if is_help "$1"; then + view_usage + return + fi + + view_helper "$1" } edit_helper() { - local yml="$1" - [[ -e "$yml" ]] || { echo "File does not exist: $yml"; exit 1; } - exec sops --input-type yaml --output-type yaml "$yml" < /dev/tty + file="$1" + + if [ ! -e "${file}" ]; then + echo "File does not exist: ${file}" + exit 1 + fi + + exec sops --input-type yaml --output-type yaml "${file}" /dev/stderr; rm -f "${decrypted_files}"' EXIT + + while [ $j -lt $argc ]; do + case "$1" in + --) + # skip --, and what remains are the cmd args + set -- "$1" + shift + break + ;; + -f | --values) + set -- "$@" "$1" + + file="${2}" + if decrypt_helper "${file}"; then + file_dec="$(file_dec_name "${file}")" + set -- "$@" "$file_dec" + + printf '%s\0' "${file_dec}" >>"${decrypted_files}" + else + set -- "$@" "$file" + fi + + shift + j=$((j + 1)) + ;; + *) + set -- "$@" "$1" + ;; + esac + shift - if [[ $cmd == diff ]] - then - subcmd="$1" - shift - cmd_version=$(${HELM_BIN} diff version) - fi - - # cache options for the helm command in a file so we don't need to parse the help each time - local helm_version=$(${HELM_BIN} version --client --short) - local cur_options_version="${helm_version}${cmd_version:+ ${cmd}: ${cmd_version}}" - local optfile="$HELM_PLUGIN_DIR/helm.${cmd}${subcmd:+.$subcmd}.options" options_version='' options='' longoptions='' - [[ -f $optfile ]] && . "$optfile" - - if [[ $cur_options_version != $options_version ]] - then - local re='(-([a-zA-Z0-9]), )?--([-_a-zA-Z0-9]+)( ([a-zA-Z0-9]+))?' line - options='' longoptions='' - - # parse the helm command options and option args from the help output - while read line - do - if [[ $line =~ $re ]] - then - local opt="${BASH_REMATCH[2]}" lopt="${BASH_REMATCH[3]}" optarg="${BASH_REMATCH[5]:+:}" - [[ $opt ]] && options+="${opt}${optarg}" - [[ $lopt ]] && longoptions+="${longoptions:+,}${lopt}${optarg}" - fi - done <<<"$(${HELM_BIN} "$cmd" $subcmd --help | sed -e '1,/^Flags:/d' -e '/^Global Flags:/,$d' )" - - cat >"$optfile" < /dev/null || exit 1 && \ -test_encryption "${secret}" - -echo -e "${YELLOW}+++${NOC} Test if 'Already Encrypted' feature works" -enc_res=$("${HELM_CMD}" secrets enc "${secret}" | grep "${ALREADY_ENC}") -test_already_encrypted "${enc_res}" - -echo -e "${YELLOW}+++${NOC} View encrypted Test" -test_view "${secret}" - -echo -e "${YELLOW}+++${NOC} Decrypt" -"${HELM_CMD}" secrets dec "${secret}" > /dev/null || exit 1 && \ -test_decrypt "${secret}" && \ -cp "${secret}.dec" "${secret}" - -echo -e "${YELLOW}+++${NOC} Cleanup Test" -"${HELM_CMD}" secrets clean "$(dirname ${secret})" > /dev/null || exit 1 -mode="specified directory" -test_clean "${secret}" "${mode}" && \ -cp "${secret}" "${secret}.dec" && \ -"${HELM_CMD}" secrets clean "${secret}.dec" > /dev/null || exit 1 -mode="specified .dec file" -test_clean "${secret}" "${mode}" # && \ -# cp "${secret}" "${secret}.dec" && \ -# "${HELM_CMD}" secrets clean "${secret}.dec" > /dev/null || exit 1 -# mode="specified encrypted secret file" -# test_clean "${secret}" "${mode}" -# The functionality above doesn't work, it only works with .dec in filename - -echo -e "${YELLOW}+++${NOC} Once again Encrypt and Test" -"${HELM_CMD}" secrets enc "${secret}" > /dev/null || exit 1 && \ -test_encryption "${secret}" + printf "${YELLOW}+++${NOC} ${BLUE}%s${NOC}\n" "Testing ${secret}" + + printf "${YELLOW}+++${NOC} %s\n" "Encrypt and Test" + "${HELM_CMD}" secrets enc "${secret}" >/dev/null || exit 1 && + test_encryption "${secret}" + + printf "${YELLOW}+++${NOC} %s\n" "Test if 'Already Encrypted' feature works" + enc_res=$("${HELM_CMD}" secrets enc "${secret}" | grep "${ALREADY_ENC}") + test_already_encrypted "${enc_res}" + + printf "${YELLOW}+++${NOC} %s\n" "View encrypted Test" + test_view "${secret}" + + printf "${YELLOW}+++${NOC} %s\n" "Decrypt" + "${HELM_CMD}" secrets dec "${secret}" >/dev/null || exit 1 && + test_decrypt "${secret}" && + cp "${secret}.dec" "${secret}" + + printf "${YELLOW}+++${NOC} %s\n" "Cleanup Test" + "${HELM_CMD}" secrets clean "$(dirname "${secret}")" >/dev/null || exit 1 + mode="specified directory" + test_clean "${secret}" "${mode}" && + cp "${secret}" "${secret}.dec" && + "${HELM_CMD}" secrets clean "${secret}.dec" >/dev/null || exit 1 + mode="specified .dec file" + test_clean "${secret}" "${mode}" # && \ + # cp "${secret}" "${secret}.dec" && \ + # "${HELM_CMD}" secrets clean "${secret}.dec" > /dev/null || exit 1 + # mode="specified encrypted secret file" + # test_clean "${secret}" "${mode}" + # The functionality above doesn't work, it only works with .dec in filename + + printf "${YELLOW}+++${NOC} %s\n" "Once again Encrypt and Test" + "${HELM_CMD}" secrets enc "${secret}" >/dev/null || exit 1 && + test_encryption "${secret}" } -echo -e "${YELLOW}+++${NOC} Installing helm-secrets plugin" -if [ "$(helm plugin list | tail -n +2 | cut -d ' ' -f 1 | grep -c "secrets")" -eq 1 ]; -then - echo -e "${GREEN}[OK]${NOC} helm-secrets plugin installed" -else - "${HELM_CMD}" plugin install "${SECRETS_REPO}" 2>/dev/null - echo -e "${RED}[FAIL]${NOC} No helm-secrets plugin aborting" - exit 1 -fi - -echo "" -if [ -x "$(command -v gpg --version)" ]; -then - echo -e "${YELLOW}+++${NOC} Importing private pgp key for projectx" - gpg --import example/pgp/projectx.asc - echo "" - echo -e "${YELLOW}+++${NOC} Importing private pgp key for projectx" - gpg --import example/pgp/projecty.asc - echo "" +printf "${YELLOW}+++${NOC} %s\n" "Installing helm-secrets plugin" +if [ "$(helm plugin list | tail -n +2 | cut -d ' ' -f 1 | grep -c "secrets")" -eq 1 ]; then + printf "${GREEN}%s${NOC} %s\n" "[OK]" "helm-secrets plugin installed" else - echo -e "${RED}[FAIL]${NOC} Install gpg" - exit 1 + "${HELM_CMD}" plugin install "${SECRETS_REPO}" 2>/dev/null + printf "${RED}%s${NOC} %s\n" "[FAIL]" "No helm-secrets plugin aborting" + exit 1 fi -echo -e "${YELLOW}+++${NOC} Show helm_vars tree from example" -if [ -x "$(command -v tree --version)" ]; -then - tree -Ca example/helm_vars/ +echo " " +if [ -x "$(command -v gpg --version)" ]; then + printf "${YELLOW}+++${NOC} %s\n" "Importing private pgp key for projectx" + gpg --import example/pgp/projectx.asc + echo " " + printf "${YELLOW}+++${NOC} %s\n" "Importing private pgp key for projectx" + gpg --import example/pgp/projecty.asc + echo " " else - echo -e "${RED}[FAIL]${NOC} Install tree command" - exit 1 + printf "${RED}%s${NOC} %s\n" "[FAIL]" "Install gpg" + exit 1 fi -echo "" -for secret in $(find . -type f -name secrets.yaml); -do test_helm_secrets "${secret}"; +echo " " +for secret in $(find . -type f -name secrets.yaml); do + test_helm_secrets "${secret}" done