11#! /bin/bash
22
33# =================================================================
4- # Restic Backup Script v0.19 - 2025.09.09
4+ # Restic Backup Script v0.20 - 2025.09.09
55# =================================================================
66
77set -euo pipefail
88umask 077
99
1010# --- Script Constants ---
11- SCRIPT_VERSION=" 0.19 "
11+ SCRIPT_VERSION=" 0.20 "
1212SCRIPT_DIR=$( cd -- " $( dirname -- " ${BASH_SOURCE[0]} " ) " & > /dev/null && pwd)
1313CONFIG_FILE=" ${SCRIPT_DIR} /restic-backup.conf"
1414LOCK_FILE=" /tmp/restic-backup.lock"
@@ -66,30 +66,25 @@ import_restic_key() {
6666check_and_install_restic () {
6767 echo -e " ${C_BOLD} --- Checking Restic Version ---${C_RESET} "
6868
69- # Check for dependencies
7069 if ! command -v bzip2 & > /dev/null || ! command -v curl & > /dev/null || ! command -v gpg & > /dev/null; then
7170 echo -e " ${C_RED} ERROR: 'bzip2', 'curl', and 'gpg' are required for secure auto-installation.${C_RESET} " >&2
72- echo -e " ${C_YELLOW} Please install them with: sudo apt-get install bzip2 curl gnupg${C_RESET} " >&2
71+ echo -e " ${C_YELLOW} On Debian based systems install with: sudo apt-get install bzip2 curl gnupg${C_RESET} " >&2
7372 exit 1
7473 fi
75-
7674 local latest_version
7775 latest_version=$( curl -s " https://api.github.com/repos/restic/restic/releases/latest" | grep -o ' "tag_name": "[^"]*"' | sed -E ' s/.*"v?([^"]+)".*/\1/' )
7876 if [ -z " $latest_version " ]; then
7977 echo -e " ${C_YELLOW} Could not fetch latest restic version from GitHub. Skipping check.${C_RESET} "
8078 return 0
8179 fi
82-
8380 local local_version=" "
8481 if command -v restic & > /dev/null; then
8582 local_version=$( restic version | head -n1 | awk ' {print $2}' )
8683 fi
87-
8884 if [[ " $local_version " == " $latest_version " ]]; then
8985 echo -e " ${C_GREEN} ✅ Restic is up to date (version $local_version ).${C_RESET} "
9086 return 0
9187 fi
92-
9388 echo -e " ${C_YELLOW} A new version of Restic is available ($latest_version ). Current version is ${local_version:- not installed} .${C_RESET} "
9489 if [ -t 1 ]; then
9590 read -p " Would you like to download and install it? (y/n): " confirm
@@ -102,12 +97,12 @@ check_and_install_restic() {
10297 echo " Skipping interactive installation in non-interactive mode (cron)."
10398 return 0
10499 fi
105-
106100 if ! import_restic_key; then
107101 return 1
108102 fi
109-
110- # Determine architecture and URLs
103+ local temp_binary temp_checksums temp_signature
104+ temp_binary=$( mktemp) && temp_checksums=$( mktemp) && temp_signature=$( mktemp)
105+ trap ' rm -f "$temp_binary" "$temp_checksums" "$temp_signature"' RETURN
111106 local arch=$( uname -m)
112107 local arch_suffix=" "
113108 case " $arch " in
@@ -118,40 +113,27 @@ check_and_install_restic() {
118113 local latest_version_tag=" v${latest_version} "
119114 local filename=" restic_${latest_version} _linux_${arch_suffix} .bz2"
120115 local base_url=" https://github.com/restic/restic/releases/download/${latest_version_tag} "
121-
122- # Download artifacts
116+ local curl_opts=(-sL --fail --retry 3 --retry-delay 2)
123117 echo " Downloading Restic binary, checksums, and signature..."
124- local temp_binary temp_checksums temp_signature
125- temp_binary=$( mktemp) && temp_checksums=$( mktemp) && temp_signature=$( mktemp)
126- curl -sL -o " $temp_binary " " ${base_url} /${filename} " || { echo " Download failed" ; rm -f " $temp_binary " " $temp_checksums " " $temp_signature " ; return 1; }
127- curl -sL -o " $temp_checksums " " ${base_url} /SHA256SUMS" || { echo " Download failed" ; rm -f " $temp_binary " " $temp_checksums " " $temp_signature " ; return 1; }
128- curl -sL -o " $temp_signature " " ${base_url} /SHA256SUMS.asc" || { echo " Download failed" ; rm -f " $temp_binary " " $temp_checksums " " $temp_signature " ; return 1; }
129-
130- # Verify signature of checksums
118+ if ! curl " ${curl_opts[@]} " -o " $temp_binary " " ${base_url} /${filename} " ; then echo " Download failed" ; return 1; fi
119+ if ! curl " ${curl_opts[@]} " -o " $temp_checksums " " ${base_url} /SHA256SUMS" ; then echo " Download failed" ; return 1; fi
120+ if ! curl " ${curl_opts[@]} " -o " $temp_signature " " ${base_url} /SHA256SUMS.asc" ; then echo " Download failed" ; return 1; fi
131121 echo " Verifying checksum signature..."
132122 if ! gpg --verify " $temp_signature " " $temp_checksums " > /dev/null 2>&1 ; then
133123 echo -e " ${C_RED} FATAL: Invalid signature on SHA256SUMS. Aborting.${C_RESET} " >&2
134- rm -f " $temp_binary " " $temp_checksums " " $temp_signature "
135124 return 1
136125 fi
137126 echo -e " ${C_GREEN} ✅ Checksum file signature is valid.${C_RESET} "
138-
139- # Verify the binary checksum (exact match by filename)
140127 echo " Verifying restic binary checksum..."
141128 local expected_hash
142129 expected_hash=$( awk -v f=" $filename " ' $2==f {print $1}' " $temp_checksums " )
143130 local actual_hash
144- # --- THIS LINE IS THE FIX ---
145131 actual_hash=$( sha256sum " $temp_binary " | awk ' {print $1}' )
146132 if [[ -z " $expected_hash " || " $expected_hash " != " $actual_hash " ]]; then
147133 echo -e " ${C_RED} FATAL: Binary checksum mismatch. Aborting.${C_RESET} " >&2
148- rm -f " $temp_binary " " $temp_checksums " " $temp_signature "
149134 return 1
150135 fi
151136 echo -e " ${C_GREEN} ✅ Restic binary checksum is valid.${C_RESET} "
152- rm -f " $temp_checksums " " $temp_signature "
153-
154- # Decompress and install
155137 echo " Decompressing and installing to /usr/local/bin/restic..."
156138 if bunzip2 -c " $temp_binary " > /usr/local/bin/restic.tmp; then
157139 chmod +x /usr/local/bin/restic.tmp
@@ -160,15 +142,14 @@ check_and_install_restic() {
160142 else
161143 echo -e " ${C_RED} Installation failed.${C_RESET} " >&2
162144 fi
163- rm -f " $temp_binary "
164145}
165146
166147check_for_script_update () {
167148 if ! [ -t 0 ]; then
168149 return 0
169150 fi
170- local SCRIPT_URL=" https://raw.githubusercontent.com/buildplan/restic-backup-script/main/restic-backup.sh"
171151 echo -e " ${C_BOLD} --- Checking for script updates ---${C_RESET} "
152+ local SCRIPT_URL=" https://raw.githubusercontent.com/buildplan/restic-backup-script/main/restic-backup.sh"
172153 local remote_version
173154 remote_version=$( curl -sL " $SCRIPT_URL " | grep ' SCRIPT_VERSION=' | head -n1 | sed -E ' s/.*"([^"]+)".*/\1/' )
174155 if [ -z " $remote_version " ] || [[ " $remote_version " == " $SCRIPT_VERSION " ]]; then
@@ -181,38 +162,36 @@ check_for_script_update() {
181162 echo " Skipping update."
182163 return 0
183164 fi
184- local temp_file
185- temp_file=$( mktemp)
186- if ! curl -sL -o " $temp_file " " $SCRIPT_URL " ; then
187- echo -e " ${C_RED} Download failed. Please try updating manually.${C_RESET} " >&2
188- rm -f " $temp_file "
189- return 1
190- fi
191- echo " Verifying downloaded file integrity..."
165+ local temp_script temp_checksum
166+ temp_script=$( mktemp)
167+ temp_checksum=$( mktemp)
168+ trap ' rm -f "$temp_script" "$temp_checksum"' RETURN
192169 local CHECKSUM_URL=" ${SCRIPT_URL} .sha256"
170+ local curl_opts=(-sL --fail --retry 3 --retry-delay 2)
171+ echo " Downloading script update..."
172+ if ! curl " ${curl_opts[@]} " -o " $temp_script " " $SCRIPT_URL " ; then echo " Download failed" ; return 1; fi
173+ if ! curl " ${curl_opts[@]} " -o " $temp_checksum " " $CHECKSUM_URL " ; then echo " Download failed" ; return 1; fi
174+ echo " Verifying downloaded file integrity..."
193175 local remote_hash
194- remote_hash=$( curl -sL " $CHECKSUM_URL " | awk ' {print $1}' )
176+ remote_hash=$( awk ' {print $1}' " $temp_checksum " )
195177 if [ -z " $remote_hash " ]; then
196- echo -e " ${C_RED} Could not download checksum file. Aborting update.${C_RESET} " >&2
197- rm -f " $temp_file "
178+ echo -e " ${C_RED} Could not read remote checksum. Aborting update.${C_RESET} " >&2
198179 return 1
199180 fi
200181 local local_hash
201- local_hash=$( sha256sum " $temp_file " | awk ' {print $1}' )
182+ local_hash=$( sha256sum " $temp_script " | awk ' {print $1}' )
202183 if [[ " $local_hash " != " $remote_hash " ]]; then
203184 echo -e " ${C_RED} FATAL: Checksum mismatch! File may be corrupt or tampered with.${C_RESET} " >&2
204185 echo -e " ${C_RED} Aborting update for security reasons.${C_RESET} " >&2
205- rm -f " $temp_file "
206186 return 1
207187 fi
208188 echo -e " ${C_GREEN} ✅ Checksum verified successfully.${C_RESET} "
209- if ! grep -q " #!/bin/bash" " $temp_file " ; then
189+ if ! grep -q " #!/bin/bash" " $temp_script " ; then
210190 echo -e " ${C_RED} Downloaded file does not appear to be a valid script. Aborting update.${C_RESET} " >&2
211- rm -f " $temp_file "
212191 return 1
213192 fi
214- chmod +x " $temp_file "
215- mv " $temp_file " " $0 "
193+ chmod +x " $temp_script "
194+ mv " $temp_script " " $0 "
216195 if [ -n " ${SUDO_USER:- } " ] && [[ " $SCRIPT_DIR " != /root* ]]; then
217196 chown " ${SUDO_USER} :${SUDO_GID:- $SUDO_USER } " " $0 "
218197 fi
0 commit comments