From 8a7c85dcf0f7c2f6c7fdb2373cd76e496b3ae614 Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Wed, 15 Nov 2023 13:44:41 +0100 Subject: [PATCH 1/8] Added bump-tag from nunoc-ops that has multiple improvements and checks for signed commits, makes sure that important script are not tampered with and much more. --- bump-tag | 292 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 247 insertions(+), 45 deletions(-) diff --git a/bump-tag b/bump-tag index d14dcaa..e4af126 100755 --- a/bump-tag +++ b/bump-tag @@ -1,66 +1,268 @@ #!/bin/bash -set -e - -echo "Fetching any updates from server:" -git pull +echo "Fetching updates from $(git remote get-url origin) ..." echo "" +git pull --verify-signatures + +if [[ ${?} -ne 0 ]]; then + echo "WARNING: git pull did not exit successfully." + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 +fi if [[ -f ./cosmos.conf ]]; then - . ./cosmos.conf + # shellcheck disable=SC1091 + source ./cosmos.conf fi -if [[ -z ${1} ]]; then - deftag=$(basename "${PWD}") + +# A tab will be used in multiple commands for git +t=$'\t' + +# Set the default tag according to the repo +# or by entering a name as the first argument. +if [[ -z "${1}" ]]; then + deftag="$(basename "${PWD}")" else - deftag="${1}" + deftag="${1}" fi -tagpfx=${tag:="${deftag}"} -last_tag=$(git tag -l "${tagpfx}-*" | sort | tail -1) -if [[ -n ${last_tag} ]]; then - echo "Verifying last tag ${last_tag}:" - (git tag -v "${last_tag}" | grep ^gpg:) || true - # again to not mask exit status of git with grep - git tag -v "${last_tag}" >/dev/null 2>&1 - echo "" +# Set the tag prefix according to: +# 1. $tag, if specified in cosmos.conf, +# 2. or $deftag, as specified above. +# shellcheck disable=SC2154 +if [[ ! -z "${tag}" ]]; then + tagpfx="${tag}" +else + tagpfx="${deftag}" +fi - echo "Differences between tag ${last_tag} and what you are about to sign:" - this_branch=$(git rev-parse --abbrev-ref HEAD) - env PAGER=cat git diff --color "${last_tag}..${this_branch}" +# Check why the tag couldn't be verified +# First argument: the tag to investigate +check_tag_sig_failure() +{ + local __tag_to_check="${1}" - iter=1 - ok= - while test -z "$ok"; do - this_tag=$(date "+${tagpfx}-%Y-%m-%d-v$(printf "%02d" ${iter})") - iter=$(( iter + 1)) - case $( ( - echo "${this_tag}" - echo "${last_tag}" - ) | sort | tail -1) in - "${last_tag}") ;; + # shellcheck disable=SC2155 + local __verify_tag_output="$(git verify-tag --raw "${__tag_to_check}" 2>&1)" - "${this_tag}") - ok=yes - ;; + if echo "${__verify_tag_output}" | grep -q "VALIDSIG"; then + + if echo "${__verify_tag_output}" | grep -q "EXPKEYSIG"; then + + echo "" + echo "WARNING: The tag was correctly signed, but the copy of" + echo "the key that you have stored on your computer has expired." + echo "Check for an updated key in:" + echo "global/overlay/etc/cosmos/keys/" + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + else + + echo "" + echo "WARNING: The tag was probably correctly signed," + echo "but it still didn't pass the verification check." + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + fi + + else + + echo "" + echo "WARNING: The signature of the tag could not be verified." + echo "Please make sure that you have imported the key and that" + echo "the key is signed by a trusted party." + echo "Keys used for signing in a Cosmos repo can be found at:" + echo "global/overlay/etc/cosmos/keys/" + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + fi +} + +check_commit_sig_failure() +{ + local __commit_to_check="${1}" + local __file_related_to_commit="${2}" + + # shellcheck disable=SC2155 + local __verify_commit_output="$(git verify-commit --raw "${__commit_to_check}" 2>&1)" + + if echo "${__verify_commit_output}" | grep -q "VALIDSIG"; then + + if echo "${__verify_commit_output}" | grep -q "EXPKEYSIG"; then + + echo "WARNING: The commit to ${__file_related_to_commit}" + echo "was correctly signed, but the copy of the key that" + echo "you have stored on your computer has expired." + echo "Check for an updated key in:" + echo "global/overlay/etc/cosmos/keys/" + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + else + + echo "WARNING: The commit to ${__file_related_to_commit}" + echo "was probably correctly signed, but it still didn't" + echo "pass the verification check." + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + fi + + else + + echo "WARNING: The commit to ${__file_related_to_commit}" + echo "could not be verified. Please make sure that you have" + echo "imported the key and that the key is signed by a trusted party." + echo "" + echo "EXITING the script. In order to tag your changes," + echo "investigate and then run bump-tag again." + exit 1 + + fi +} + +# Verify the last commit of a file +# First argument: the file to verify +verify_last_commit() +{ + local __file_to_verify="${1}" + + if [[ ! -f "${__file_to_verify}" ]]; then + return 1 + fi + + if [[ ! -z "$(git status --porcelain "${__file_to_verify}")" ]]; then + echo "" + echo "INFO: local changes detected in ${__file_to_verify}," + echo "Not checking the signature of the last commit to ${__file_to_verify}." + echo "" + return 1 + fi + + # shellcheck disable=SC2155 + local __last_commit="$(git log -n 1 --pretty=format:%H -- "${__file_to_verify}")" + + if ! git verify-commit "${__last_commit}" 2> /dev/null; then + echo "" + echo "WARNING: Untrusted modification to ${__file_to_verify}:" + echo "----------------------------" + git verify-commit "$(git log -n 1 --pretty=format:%H -- "${__file_to_verify}")" + echo "----------------------------" + + check_commit_sig_failure "${__last_commit}" "${__file_to_verify}" + fi +} + +tag_list="$(git tag -l "${tagpfx}-*")" +if [[ ${?} -ne 0 ]] || [[ -z "${tag_list}" ]]; then + + echo "No tags found, verifying all commits instead." + # %H = commit hash + # %G? = show "G" for a good (valid) signature + git_log="$(git log --pretty="format:%H${t}%G?" \ + --first-parent \ + | grep -v "${t}G$")" + +else + + last_tag="$(echo "${tag_list}" | sort | tail -1)" + echo "Verifying last tag: ${last_tag} and the commits after that" + + git verify-tag "${last_tag}" + if [[ ${?} -ne 0 ]]; then + check_tag_sig_failure "${last_tag}" + fi + + tag_object="$(git verify-tag -v "${last_tag}" 2>&1 | grep ^object | cut -d' ' -f2)" + + # The commits after the last valid signed git tag that we need to check + revision_range="${tag_object}..HEAD" + + # Filter out the commits that are unsigned or untrusted + # %H = commit hash + # %G? = show "G" for a good (valid) signature + git_log="$(git log --pretty="format:%H${t}%G?" "${revision_range}" \ + --first-parent \ + | grep -v "${t}G$")" + +fi + +if [[ ! -z "${git_log}" ]]; then + echo "" + echo -e "------WARNING: unsigned or untrusted commits after the last tag------" + echo "${git_log}" + echo -e "---------------------------------------------------------------------" + echo "Quick referens on how to configure signing of commits in ~/.gitconfig:" + echo "[user]" + echo " signingkey = your-prefered-key-id" + echo "[commit]" + echo " gpgsign = true" + echo "" + echo "EXITING the script. In order to tag your changes," + echo "please make sure that you have configured signing of" + echo "your own commits and that the listed unsigned commits" + echo "have been made by a trusted party and are not malicous." + exit 1 +fi + +# Always check that the last commit of certain +# sensitive files is trusted, without taking into +# account whether the last tag was trusted or not. +verify_last_commit "./scripts/jsonyaml-no-output.py" +verify_last_commit "./bump-tag" + +# Test the syntax of each YAML-file to be tagged. +for file in $(git diff --name-only "${last_tag}..${this_branch}" | egrep "^.*\.(yaml|yml)$"); do + if [[ -f "${file}" ]]; then + ./scripts/jsonyaml-no-output.py yaml "${file}" + fi +done + +echo "Differences between tag ${last_tag} and what you are about to sign:" +# With PAGER=cat, git diff will simply dump the output to the screen. +# shellcheck disable=SC2037 +PAGER=cat git diff --color "${last_tag}..${this_branch}" + +# Iterate over the $last_tag until $this_tag is set to a later version +iter=1 +ok= +while [[ -z "${ok}" ]]; do + this_tag="$(date +"${tagpfx}-%Y-%m-%d-v$(printf "%02d" "${iter}")")" + iter="$(( iter + 1))" + + case "$( (echo "${this_tag}"; echo "${last_tag}") | sort | tail -1 )" in + "${last_tag}") + ;; + "${this_tag}") + ok=yes + ;; esac - done +done - if [ "${deftag}" != "${tagpfx}" ]; then +if [ "${deftag}" != "${tagpfx}" ]; then echo -e "Using new tag \e[94m${this_tag}\e[0m according to pattern in cosmos.conf" - else - echo -e "Using new tag \e[94m${this_tag}\e[0m" - fi - - echo -e "\e[1mONLY SIGN IF YOU APPROVE OF VERIFICATION AND DIFF ABOVE\e[0m" else - echo -e "\e[1mCOULD NOT FIND LAST TAG, ASSUMING NEW TAG\e[0m" - iter=1 - this_tag=$(date "+${tagpfx}-%Y-%m-%d-v$(printf "%02d" ${iter})") + echo -e "Using new tag \e[94m${this_tag}\e[0m" fi -# GITTAGEXTRA is for putting things like "-u 2117364A" -# shellcheck disable=SC2086 -git tag ${GITTAGEXTRA} -s "${this_tag}" -m bump. +echo -e "\e[1mONLY SIGN IF YOU APPROVE OF VERIFICATION AND DIFF ABOVE\e[0m" + +git tag $GITTAGEXTRA -m bump. -s "${this_tag}" git push git push --tags From 53c58b413e32b95bb3f46bfdec80d54ae4c03285 Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 11:56:49 +0100 Subject: [PATCH 2/8] Changed from if [[ ${?} ]] to if cmd --- bump-tag | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bump-tag b/bump-tag index e4af126..8708935 100755 --- a/bump-tag +++ b/bump-tag @@ -2,9 +2,7 @@ echo "Fetching updates from $(git remote get-url origin) ..." echo "" -git pull --verify-signatures - -if [[ ${?} -ne 0 ]]; then +if ! git pull --verify-signatures; then echo "WARNING: git pull did not exit successfully." echo "" echo "EXITING the script. In order to tag your changes," @@ -183,8 +181,7 @@ else last_tag="$(echo "${tag_list}" | sort | tail -1)" echo "Verifying last tag: ${last_tag} and the commits after that" - git verify-tag "${last_tag}" - if [[ ${?} -ne 0 ]]; then + if ! git verify-tag "${last_tag}"; then check_tag_sig_failure "${last_tag}" fi From 826b8edf827838bf035ad57f6e424a304bd9bdc2 Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 11:59:33 +0100 Subject: [PATCH 3/8] Changed from [[ ! -z ... to [[ -n ... --- bump-tag | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bump-tag b/bump-tag index 8708935..2c06d7e 100755 --- a/bump-tag +++ b/bump-tag @@ -30,7 +30,7 @@ fi # 1. $tag, if specified in cosmos.conf, # 2. or $deftag, as specified above. # shellcheck disable=SC2154 -if [[ ! -z "${tag}" ]]; then +if [[ -n "${tag}" ]]; then tagpfx="${tag}" else tagpfx="${deftag}" @@ -144,7 +144,7 @@ verify_last_commit() return 1 fi - if [[ ! -z "$(git status --porcelain "${__file_to_verify}")" ]]; then + if [[ -n "$(git status --porcelain "${__file_to_verify}")" ]]; then echo "" echo "INFO: local changes detected in ${__file_to_verify}," echo "Not checking the signature of the last commit to ${__file_to_verify}." @@ -199,7 +199,7 @@ else fi -if [[ ! -z "${git_log}" ]]; then +if [[ -n "${git_log}" ]]; then echo "" echo -e "------WARNING: unsigned or untrusted commits after the last tag------" echo "${git_log}" From 5a47b1a3f74153c7f7a29b3f475b4613c4dc5227 Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 12:04:30 +0100 Subject: [PATCH 4/8] Readded this_branch=$(git rev-parse --abbrev-ref HEAD) since it wasn't included in change to check against the current branch instead of master --- bump-tag | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bump-tag b/bump-tag index 2c06d7e..8a9b6e6 100755 --- a/bump-tag +++ b/bump-tag @@ -36,6 +36,9 @@ else tagpfx="${deftag}" fi +# This is the current branch that Git will diff against. +this_branch=$(git rev-parse --abbrev-ref HEAD) + # Check why the tag couldn't be verified # First argument: the tag to investigate check_tag_sig_failure() From cb9e1f867036d5b7cdcde918d3019ef19a21f2b0 Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 12:07:10 +0100 Subject: [PATCH 5/8] Added shellcheck exceptions for misplaced warning. --- bump-tag | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bump-tag b/bump-tag index 8a9b6e6..74fc23e 100755 --- a/bump-tag +++ b/bump-tag @@ -170,6 +170,7 @@ verify_last_commit() } tag_list="$(git tag -l "${tagpfx}-*")" +# shellcheck disable=SC2181 if [[ ${?} -ne 0 ]] || [[ -z "${tag_list}" ]]; then echo "No tags found, verifying all commits instead." @@ -262,7 +263,10 @@ fi echo -e "\e[1mONLY SIGN IF YOU APPROVE OF VERIFICATION AND DIFF ABOVE\e[0m" -git tag $GITTAGEXTRA -m bump. -s "${this_tag}" +# GITTAGEXTRA is for putting things like "-u 2117364A" +# Note that this variable cannot be quoted if left empty. +# shellcheck disable=SC2086 +git tag ${GITTAGEXTRA} -m bump. -s "${this_tag}" git push git push --tags From fd4523308f261b77e1532a35466bcaea82ea36fe Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 12:09:02 +0100 Subject: [PATCH 6/8] Replaced 'egrep' that is now deprecated. --- bump-tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bump-tag b/bump-tag index 74fc23e..d69695f 100755 --- a/bump-tag +++ b/bump-tag @@ -228,7 +228,7 @@ verify_last_commit "./scripts/jsonyaml-no-output.py" verify_last_commit "./bump-tag" # Test the syntax of each YAML-file to be tagged. -for file in $(git diff --name-only "${last_tag}..${this_branch}" | egrep "^.*\.(yaml|yml)$"); do +for file in $(git diff --name-only "${last_tag}..${this_branch}" | grep -E "^.*\.(yaml|yml)$"); do if [[ -f "${file}" ]]; then ./scripts/jsonyaml-no-output.py yaml "${file}" fi From dc1df6671cd220a03da15a53c5870dbd7f0bc07d Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 12:11:09 +0100 Subject: [PATCH 7/8] Shellcheck needs to have the PAGER quoted in order to correctly interpret the meaning according to it's wiki. --- bump-tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bump-tag b/bump-tag index d69695f..a918b12 100755 --- a/bump-tag +++ b/bump-tag @@ -237,7 +237,7 @@ done echo "Differences between tag ${last_tag} and what you are about to sign:" # With PAGER=cat, git diff will simply dump the output to the screen. # shellcheck disable=SC2037 -PAGER=cat git diff --color "${last_tag}..${this_branch}" +PAGER="cat" git diff --color "${last_tag}..${this_branch}" # Iterate over the $last_tag until $this_tag is set to a later version iter=1 From 21c0cad8a026358c4708f4180389c9173a2b72ff Mon Sep 17 00:00:00 2001 From: John Van de Meulebrouck Brendgard Date: Thu, 16 Nov 2023 12:12:36 +0100 Subject: [PATCH 8/8] Consistently use [[ for if statements. --- bump-tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bump-tag b/bump-tag index a918b12..cf2c8ad 100755 --- a/bump-tag +++ b/bump-tag @@ -255,7 +255,7 @@ while [[ -z "${ok}" ]]; do esac done -if [ "${deftag}" != "${tagpfx}" ]; then +if [[ "${deftag}" != "${tagpfx}" ]]; then echo -e "Using new tag \e[94m${this_tag}\e[0m according to pattern in cosmos.conf" else echo -e "Using new tag \e[94m${this_tag}\e[0m"