From af49f47b6db7fa1d596288dcafe1cd4882e0c997 Mon Sep 17 00:00:00 2001 From: Shubham Hibare Date: Sun, 21 Jan 2024 17:26:13 +0530 Subject: [PATCH] feat(signature): Checksum signature verification --- README.md | 7 +++++ install.sh | 84 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c0d6dc615d2d..b6458dc245ea 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,13 @@ You can also choose another destination directory and release version for the in curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b ``` +#### Signature verification + +To verify artifact signature before installation, pass `-v` flag to script execution as follow: +```bash +curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin -v +``` + ### Homebrew ```bash diff --git a/install.sh b/install.sh index 63f8ee4c62eb..648e7f5e62f7 100755 --- a/install.sh +++ b/install.sh @@ -8,6 +8,10 @@ REPO="${PROJECT_NAME}" GITHUB_DOWNLOAD_PREFIX=https://github.com/${OWNER}/${REPO}/releases/download INSTALL_SH_BASE_URL=https://raw.githubusercontent.com/${OWNER}/${PROJECT_NAME} PROGRAM_ARGS=$@ +COSIGN_BINARY=cosign +VERIFY_SIGN=false +CERT_FORMAT=pem +SIG_FORMAT=sig # do not change the name of this parameter (this must always be backwards compatible) DOWNLOAD_TAG_INSTALL_SCRIPT=${DOWNLOAD_TAG_INSTALL_SCRIPT:-true} @@ -24,6 +28,7 @@ Usage: $this [-b] dir [-d] [tag] -b the installation directory (dDefaults to ./bin) -d turns on debug logging -dd turns on trace logging + -v verify checksum signature. Require cosign binary to be installed. [tag] the specific release to use (if missing, then the latest will be used) EOF exit 2 @@ -358,30 +363,55 @@ github_release_tag() ( echo "$tag" ) -# download_github_release_checksums [release-url-prefix] [name] [version] [output-dir] +# download_github_release_checksums_files [release-url-prefix] [name] [version] [output-dir] [filename] # -# outputs path to the downloaded checksums file +# outputs path to the downloaded checksums related file # -download_github_release_checksums() ( +download_github_release_checksums_files() ( download_url="$1" name="$2" version="$3" output_dir="$4" + filename="$5" - log_trace "download_github_release_checksums(url=${download_url}, name=${name}, version=${version}, output_dir=${output_dir})" + log_trace "download_github_release_checksums_files(url=${download_url}, name=${name}, version=${version}, output_dir=${output_dir}, filename=${filename})" - checksum_filename=${name}_${version}_checksums.txt - checksum_url=${download_url}/${checksum_filename} - output_path="${output_dir}/${checksum_filename}" + complete_filename="${name}_${version}_${filename}" + complete_url="${download_url}/${complete_filename}" + output_path="${output_dir}/${complete_filename}" - http_download "${output_path}" "${checksum_url}" "" + http_download "${output_path}" "${complete_url}" "" asset_file_exists "${output_path}" - log_trace "download_github_release_checksums() returned '${output_path}'" + log_trace "download_github_release_checksums_files() returned '${output_path}' for file '${complete_filename}'" echo "${output_path}" ) +# download_github_release_checksums [release-url-prefix] [name] [version] [output-dir] +# +# outputs path to the downloaded checksums file +# +download_github_release_checksums() ( + echo "$(download_github_release_checksums_files "$@" "checksums.txt")" +) + +# download_github_release_checksums_sig [release-url-prefix] [name] [version] [output-dir] +# +# outputs path to the downloaded checksums signature file +# +download_github_release_checksums_sig() ( + echo "$(download_github_release_checksums_files "$@" "checksums.txt.sig")" +) + +# download_github_release_checksums_cert [release-url-prefix] [name] [version] [output-dir] +# +# outputs path to the downloaded checksums certificate file +# +download_github_release_checksums_cert() ( + echo "$(download_github_release_checksums_files "$@" "checksums.txt.pem")" +) + # search_for_asset [checksums-file-path] [name] [os] [arch] [format] # # outputs name of the asset to download @@ -546,6 +576,16 @@ download_and_install_asset() ( install_asset "${asset_filepath}" "${install_path}" "${binary}" ) +verify_sign() { + log_trace "Verifying artifact $1" + ${COSIGN_BINARY} verify-blob "$1" \ + --certificate "$2" \ + --signature "$3" \ + --certificate-identity-regexp "https://github\.com/${OWNER}/${REPO}/\.github/workflows/.+" \ + --certificate-oidc-issuer "https://token.actions.githubusercontent.com" +} + + # download_asset [release-url-prefix] [download-path] [name] [os] [arch] [version] [format] [binary] # # outputs the path to the downloaded asset asset_filepath @@ -572,6 +612,29 @@ download_asset() ( return 1 fi + if [ "$VERIFY_SIGN" = true ]; then + log_trace "checking for ${COSIGN_BINARY} binary" + if is_command "${COSIGN_BINARY}"; then + log_trace "${COSIGN_BINARY} binary is installed" + else + log_err "${COSIGN_BINARY} binary is not installed. Follow steps from https://docs.sigstore.dev/system_config/installation/ to install it." + return 1 + fi + + checksum_sig_file_path=$(download_github_release_checksums_sig "${download_url}" "${name}" "${version}" "${destination}") + log_info "downloaded checksums signature file: ${checksum_sig_file_path}" + + checksums_cert_file_path=$(download_github_release_checksums_cert "${download_url}" "${name}" "${version}" "${destination}") + log_info "downloaded checksums certificate file: ${checksums_cert_file_path}" + + verify_sign "${checksums_filepath}" "${checksums_cert_file_path}" "${checksum_sig_file_path}" + if [ $? -ne 0 ]; then + log_err "checksum signature verification failed" + return 1 + fi + log_info "checksum signature verification succeeded" + fi + asset_url="${download_url}/${asset_filename}" asset_filepath="${destination}/${asset_filename}" http_download "${asset_filepath}" "${asset_url}" "" @@ -616,7 +679,7 @@ main() ( install_dir=${install_dir:-./bin} # note: never change the program flags or arguments (this must always be backwards compatible) - while getopts "b:dh?x" arg; do + while getopts "b:dvh?x" arg; do case "$arg" in b) install_dir="$OPTARG" ;; d) @@ -628,6 +691,7 @@ main() ( log_set_priority $log_trace_priority fi ;; + v) VERIFY_SIGN=true;; h | \?) usage "$0" ;; x) set -x ;; esac