Skip to content

Commit ea5d1d8

Browse files
authored
Merge pull request #207 from puppetlabs/iterate-on-download-verify
Improve download task gpg verification
2 parents 7bd528d + f31b6ae commit ea5d1d8

File tree

2 files changed

+91
-36
lines changed

2 files changed

+91
-36
lines changed

tasks/download.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,20 @@
99
"type": "String",
1010
"description": "Where to save the downloaded file"
1111
},
12-
"check_download": {
12+
"verify_download": {
1313
"type": "Boolean",
1414
"description": "Whether to check the integrity of the downloaded file",
1515
"default": true
16+
},
17+
"key_id": {
18+
"type": "String",
19+
"description": "The GPG key ID to use when verifying the download",
20+
"default": "4528B6CD9E61EF26"
21+
},
22+
"key_server": {
23+
"type": "String",
24+
"description": "The GPG keyserver to retrieve the GPG key from",
25+
"default": "hkp://keyserver.ubuntu.com:11371"
1626
}
1727
},
1828
"input_method": "environment",

tasks/download.sh

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,90 @@
11
#!/bin/bash
22

3-
urisize=$(curl -s -L --head "$PT_source" | sed -n 's/Content-Length: \([0-9]\+\)/\1/p' | tr -d '\012\015')
4-
filesize=$(stat -c%s "$PT_path" 2>/dev/null)
3+
# Exit code indicating "a service is unavailable", from
4+
# /usr/include/sysexits.h. The verify-file function will return this code if
5+
# prerequisites for verification are unavailable.
6+
EX_UNAVAILABLE=69
57

6-
# Assume that if the file exists and is the same size, we don't have to
7-
# re-download.
8-
if [[ ! -z "$urisize" && ! -z "$filesize" && "$filesize" -eq "$urisize" ]]; then
9-
exit 0
10-
else
11-
printf '%s\n' "Downloading: ${PT_source}" >&2
12-
curl -f -L -o "$PT_path" "$PT_source"
13-
fi
8+
verify-file() {
9+
local sig="$1"
10+
local doc="$2"
1411

15-
if [[ "$PT_check_download" == "false" ]]; then
16-
exit 0
17-
fi
12+
# The GPG binary is required to be present in order to perform file download
13+
# verification. If it is not present, return EX_UNAVAILABLE.
14+
if ! command -v gpg >/dev/null; then
15+
echo "gpg binary not found; required in path for checking download"
16+
return "$EX_UNAVAILABLE"
17+
fi
1818

19-
if ! which -s gpg ; then
20-
echo "gpg binary required in path for checking download. Skipping check."
21-
exit 0
22-
fi
19+
# The verification key must be present, or it must be possible to download it
20+
# from the keyserver to perform file verification. If it is not present,
21+
# return EX_UNAVAILABLE.
22+
if ! { gpg --list-keys "$PT_key_id" || gpg --keyserver "$PT_key_server" --recv-key "$PT_key_id"; } then
23+
echo "Unable to download verification key ${PT_key_id}"
24+
return "$EX_UNAVAILABLE"
25+
fi
2326

24-
echo "Importing Puppet gpg public key"
25-
gpg --keyserver hkp://keyserver.ubuntu.com:11371 --recv-key 4528B6CD9E61EF26
26-
if gpg --list-key --fingerprint 4528B6CD9E61EF26 | grep -q -E "D681 +1ED3 +ADEE +B844 +1AF5 +AA8F +4528 +B6CD +9E61 +EF26" ; then
27-
echo "gpg public key imported successfully."
28-
else
29-
echo "Could not import gpg public key - wrong fingerprint."
30-
exit 1
31-
fi
27+
# Perform the verification and return success or failure.
28+
if gpg --verify "$sig" "$doc"; then
29+
echo "Signature verification succeeded"
30+
return 0
31+
else
32+
echo "Signature verification failed"
33+
return 1
34+
fi
35+
}
36+
37+
download() {
38+
printf '%s\n' "Downloading: ${1}"
39+
curl -s -f -L -o "$2" "$1"
40+
}
41+
42+
download-size-verify() {
43+
local source="$1"
44+
local path="$2"
45+
46+
urisize=$(curl -s -L --head "$source" | sed -rn 's/Content-Length: ([0-9]+)/\1/p' | tr -d '\012\015')
47+
filesize=$(stat -c%s "$path" 2>/dev/null || stat -f%z "$path" 2>/dev/null)
48+
49+
echo "Filesize: ${filesize}"
50+
echo "Content-Length header: ${urisize}"
51+
52+
# Assume that if the file exists and is the same size, we don't have to
53+
# re-download.
54+
if [[ ! -z "$urisize" && ! -z "$filesize" && "$filesize" -eq "$urisize" ]]; then
55+
echo "File size matches HTTP Content-Length header. Using file as-is."
56+
exit 0
57+
else
58+
download "$source" "$path"
59+
fi
60+
}
61+
62+
download-signature-verify() {
63+
local source="$1"
64+
local path="$2"
65+
66+
if ! download "${source}.asc" "${path}.asc" ; then
67+
echo "Unable to download ${source}.asc. Skipping verification."
68+
download-size-verify "$source" "$path"
69+
return "$?"
70+
fi
3271

33-
sigpath=${PT_path}.asc
34-
sigsource=${PT_source}.asc
72+
echo "Verifying ${path}..."
73+
verify_output=$(verify-file "${path}.asc" "$path");
74+
verify_exit="$?"
75+
if [[ "$verify_exit" -eq "$EX_UNAVAILABLE" ]]; then
76+
echo "Verification unavailable. ${verify_output}. Skipping verification."
77+
download-size-verify "$source" "$path"
78+
elif [[ "$verify_exit" -eq "1" ]]; then
79+
echo "$verify_output"
80+
download "$source" "$path"
81+
echo "Verifying ${path}..."
82+
verify-file "${path}.asc" "$path"
83+
fi
84+
}
3585

36-
echo "Downloading tarball signature from ${sigsource}..."
37-
curl -f -L -o "${sigpath}" "${sigsource}"
38-
echo "Downloaded tarball signature to ${sigpath}."
39-
echo "Checking tarball signature at ${sigpath}..."
40-
if gpg --verify "${sigpath}" "${PT_path}" ; then
41-
echo "Signature verification succeeded."
86+
if [[ "$PT_verify_download" == "true" ]]; then
87+
download-signature-verify "$PT_source" "$PT_path"
4288
else
43-
echo "Signature verification failed, please re-run the installation."
44-
exit 1
89+
download-size-verify "$PT_source" "$PT_path"
4590
fi

0 commit comments

Comments
 (0)