diff --git a/tasks/download.json b/tasks/download.json index 50e6076a..13441b08 100644 --- a/tasks/download.json +++ b/tasks/download.json @@ -9,10 +9,20 @@ "type": "String", "description": "Where to save the downloaded file" }, - "check_download": { + "verify_download": { "type": "Boolean", "description": "Whether to check the integrity of the downloaded file", "default": true + }, + "key_id": { + "type": "String", + "description": "The GPG key ID to use when verifying the download", + "default": "4528B6CD9E61EF26" + }, + "key_server": { + "type": "String", + "description": "The GPG keyserver to retrieve the GPG key from", + "default": "hkp://keyserver.ubuntu.com:11371" } }, "input_method": "environment", diff --git a/tasks/download.sh b/tasks/download.sh index 72f71b49..e713c645 100755 --- a/tasks/download.sh +++ b/tasks/download.sh @@ -1,45 +1,90 @@ #!/bin/bash -urisize=$(curl -s -L --head "$PT_source" | sed -n 's/Content-Length: \([0-9]\+\)/\1/p' | tr -d '\012\015') -filesize=$(stat -c%s "$PT_path" 2>/dev/null) +# Exit code indicating "a service is unavailable", from +# /usr/include/sysexits.h. The verify-file function will return this code if +# prerequisites for verification are unavailable. +EX_UNAVAILABLE=69 -# Assume that if the file exists and is the same size, we don't have to -# re-download. -if [[ ! -z "$urisize" && ! -z "$filesize" && "$filesize" -eq "$urisize" ]]; then - exit 0 -else - printf '%s\n' "Downloading: ${PT_source}" >&2 - curl -f -L -o "$PT_path" "$PT_source" -fi +verify-file() { + local sig="$1" + local doc="$2" -if [[ "$PT_check_download" == "false" ]]; then - exit 0 -fi + # The GPG binary is required to be present in order to perform file download + # verification. If it is not present, return EX_UNAVAILABLE. + if ! command -v gpg >/dev/null; then + echo "gpg binary not found; required in path for checking download" + return "$EX_UNAVAILABLE" + fi -if ! which -s gpg ; then - echo "gpg binary required in path for checking download. Skipping check." - exit 0 -fi + # The verification key must be present, or it must be possible to download it + # from the keyserver to perform file verification. If it is not present, + # return EX_UNAVAILABLE. + if ! { gpg --list-keys "$PT_key_id" || gpg --keyserver "$PT_key_server" --recv-key "$PT_key_id"; } then + echo "Unable to download verification key ${PT_key_id}" + return "$EX_UNAVAILABLE" + fi -echo "Importing Puppet gpg public key" -gpg --keyserver hkp://keyserver.ubuntu.com:11371 --recv-key 4528B6CD9E61EF26 -if gpg --list-key --fingerprint 4528B6CD9E61EF26 | grep -q -E "D681 +1ED3 +ADEE +B844 +1AF5 +AA8F +4528 +B6CD +9E61 +EF26" ; then - echo "gpg public key imported successfully." -else - echo "Could not import gpg public key - wrong fingerprint." - exit 1 -fi + # Perform the verification and return success or failure. + if gpg --verify "$sig" "$doc"; then + echo "Signature verification succeeded" + return 0 + else + echo "Signature verification failed" + return 1 + fi +} + +download() { + printf '%s\n' "Downloading: ${1}" + curl -s -f -L -o "$2" "$1" +} + +download-size-verify() { + local source="$1" + local path="$2" + + urisize=$(curl -s -L --head "$source" | sed -rn 's/Content-Length: ([0-9]+)/\1/p' | tr -d '\012\015') + filesize=$(stat -c%s "$path" 2>/dev/null || stat -f%z "$path" 2>/dev/null) + + echo "Filesize: ${filesize}" + echo "Content-Length header: ${urisize}" + + # Assume that if the file exists and is the same size, we don't have to + # re-download. + if [[ ! -z "$urisize" && ! -z "$filesize" && "$filesize" -eq "$urisize" ]]; then + echo "File size matches HTTP Content-Length header. Using file as-is." + exit 0 + else + download "$source" "$path" + fi +} + +download-signature-verify() { + local source="$1" + local path="$2" + + if ! download "${source}.asc" "${path}.asc" ; then + echo "Unable to download ${source}.asc. Skipping verification." + download-size-verify "$source" "$path" + return "$?" + fi -sigpath=${PT_path}.asc -sigsource=${PT_source}.asc + echo "Verifying ${path}..." + verify_output=$(verify-file "${path}.asc" "$path"); + verify_exit="$?" + if [[ "$verify_exit" -eq "$EX_UNAVAILABLE" ]]; then + echo "Verification unavailable. ${verify_output}. Skipping verification." + download-size-verify "$source" "$path" + elif [[ "$verify_exit" -eq "1" ]]; then + echo "$verify_output" + download "$source" "$path" + echo "Verifying ${path}..." + verify-file "${path}.asc" "$path" + fi +} -echo "Downloading tarball signature from ${sigsource}..." -curl -f -L -o "${sigpath}" "${sigsource}" -echo "Downloaded tarball signature to ${sigpath}." -echo "Checking tarball signature at ${sigpath}..." -if gpg --verify "${sigpath}" "${PT_path}" ; then - echo "Signature verification succeeded." +if [[ "$PT_verify_download" == "true" ]]; then + download-signature-verify "$PT_source" "$PT_path" else - echo "Signature verification failed, please re-run the installation." - exit 1 + download-size-verify "$PT_source" "$PT_path" fi