Bash script to download and verify an OpenWRT release

This is something I wrote to avoid having to manually collect OpenWRT release files from the website with a browser, and then having to verify them afterwards.

It downloads and verifies all relevant files for a requested OpenWRT release for a specific OpenWRT device type to a new directory in the working directory.

download-openwrt
#!/bin/bash

set -o nounset
set -o noclobber
set -o errexit
shopt -qs inherit_errexit

# download-openwrt
# Downloads and verifies an OpenWRT version for a specific OpenWRT device type

# Usage:
#   download-openwrt openWrtVersion
#
# Example:
#   download-openwrt 24.10.4
# This would create a directory "openwrt-24.10.4" in the working directory
# containing the release files for OpenWRT 24.10.4
#
# The device type is hard-coded via the $urlRoot and $urlParts variables; edit
# those as needed

# Parse single argument: The OpenWRT version/release to download, e.g. "24.10.4"
version="$1"; shift

# The deepest common path of all download URLs
urlRoot="https://downloads.openwrt.org/releases/${version}/targets/ipq40xx/generic/"
# The directory options for wget suitable for the deepest common path
wgetDirOptions=( --force-directories --no-host-directories --cut-dirs=5 )

# The files that should be downloaded, relative to the URL root
urlParts=()
urlParts+=( "openwrt-${version}-ipq40xx-generic-avm_fritzbox-7530-initramfs-uImage.itb" )
urlParts+=( "openwrt-${version}-ipq40xx-generic-avm_fritzbox-7530-squashfs-sysupgrade.bin" )
urlParts+=( "u-boot-fritz7520/uboot-fritz7520.bin" )
urlParts+=( "u-boot-fritz7530/uboot-fritz7530.bin" )
urlParts+=( "config.buildinfo" )
urlParts+=( "feeds.buildinfo" )
urlParts+=( "openwrt-${version}-ipq40xx-generic.bom.cdx.json" )
urlParts+=( "openwrt-${version}-ipq40xx-generic.manifest" )
urlParts+=( "profiles.json" )
urlParts+=( "sha256sums" )
urlParts+=( "sha256sums.asc" )
urlParts+=( "sha256sums.sig" )
urlParts+=( "version.buildinfo" )

# The directory the files should be downloaded to
targetDir="openwrt-${version}"

# Create the target directory if required and enter it
mkdir --parents -- "$targetDir"
cd "$targetDir" || exit 1

# Download the files, unless they are already there
echo " INFO Downloading files to \"$targetDir\"" >&2
for urlPart in "${urlParts[@]}"; do
	echo " INFO Downloading: \"$urlPart\"" >&2
	wget --no-clobber "${wgetDirOptions[@]}" --quiet -- "${urlRoot}$urlPart"
done

# PGP-verify sha256sums file
echo " INFO Verifying PGP signature of the \"sha256sums\" file" >&2
gpg --quiet --verify -- sha256sums.asc sha256sums

# Verify the downloaded files' SHA256 checksums
echo " INFO Verifying SHA256 checksums" >&2
sha256sum --ignore-missing --check -- sha256sums
Example command line use
[user@system fritzbox-7520-openwrt]$ ./download-openwrt 24.10.4
 INFO Downloading files to "openwrt-24.10.4"
 INFO Downloading: "openwrt-24.10.4-ipq40xx-generic-avm_fritzbox-7530-initramfs-uImage.itb"
 INFO Downloading: "openwrt-24.10.4-ipq40xx-generic-avm_fritzbox-7530-squashfs-sysupgrade.bin"
 INFO Downloading: "u-boot-fritz7520/uboot-fritz7520.bin"
 INFO Downloading: "u-boot-fritz7530/uboot-fritz7530.bin"
 INFO Downloading: "config.buildinfo"
 INFO Downloading: "feeds.buildinfo"
 INFO Downloading: "openwrt-24.10.4-ipq40xx-generic.bom.cdx.json"
 INFO Downloading: "openwrt-24.10.4-ipq40xx-generic.manifest"
 INFO Downloading: "profiles.json"
 INFO Downloading: "sha256sums"
 INFO Downloading: "sha256sums.asc"
 INFO Downloading: "sha256sums.sig"
 INFO Downloading: "version.buildinfo"
 INFO Verifying PGP signature of the "sha256sums" file
gpg: Signature made Di 21 Okt 2025 09:13:14 CEST
gpg:                using EDDSA key 92C561DE55AE6552F3C736B82B0151090606D1D9
gpg: Good signature from "OpenWrt Build System (Nitrokey3) <contact@openwrt.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 8A8B C12F 46B8 36C0 F9CD  B36F 1D53 D187 7742 E911
     Subkey fingerprint: 92C5 61DE 55AE 6552 F3C7  36B8 2B01 5109 0606 D1D9
 INFO Verifying SHA256 checksums
config.buildinfo: OK
feeds.buildinfo: OK
openwrt-24.10.4-ipq40xx-generic-avm_fritzbox-7530-initramfs-uImage.itb: OK
openwrt-24.10.4-ipq40xx-generic-avm_fritzbox-7530-squashfs-sysupgrade.bin: OK
openwrt-24.10.4-ipq40xx-generic.bom.cdx.json: OK
openwrt-24.10.4-ipq40xx-generic.manifest: OK
profiles.json: OK
u-boot-fritz7520/uboot-fritz7520.bin: OK
u-boot-fritz7530/uboot-fritz7530.bin: OK
version.buildinfo: OK

Zooming out a bit... what is the goal here in practical terms?

For example, are you just downloading the standard release images to install onto your devices? There is a tool called OpenWrt Upgrade Tool (owut) and it's GUI counterpart called Attended Sysupgrade (ASU) which can do all of this automatically directly from within the router's admin surfaces. Not only does make upgrades easy, it also can pre-build custom images for you with all of your packages pre-installed. Then you don't need to externally "collect" all of the files on your computer, and the owut/asu system checks the checksums before installation, too.

2 Likes

I also made a script that checks for the model and downloads the sysupgrade file long before owut exists.
It works fine but if owut can do this, I’d like to replace my script.
Can you give me a hint how to do this with owut (don't catch it…)?

2 Likes
1 Like

Sorry, but i have already found and read the links you posted but could not figure it out how download the sysupgrade-file.
when I do “owut download --keep -v” owut finds the right directory but the wrong image-filename:

Image-URL https://downloads.openwrt.org/snapshots/targets/mediatek/filogic
Image-file openwrt-mediatek-filogic-glinet_gl-mt6000-squashfs-sysupgrade.bin

but seems to download a modified version (with other packages than default).

Yes, that is the purpose of asu - to combine same packages user has installed.

Point taken, I did not know about owut or ASU.

One of my greatest pain points with OpenWRT is indeed the inability to retain explicitely installed packages upon update, or for that matter the inability of opkg to discern between explicitely installed packages and dependency-pulled packages at all.
Owut/ASU appear to solve this.

That being said, I am sceptical about custom images built on-demand by a build server. It smells like such a lot of complexity, and complexity is the bane of systems security. Wasn't there even an incident some time ago where that build server was tricked into producing signed but malicious images or something the like an issue was found that would have allowed an attacker to trick a build server into producing a signed but malicious image (although no such attack was observed)?

Anyway, a build server is a complex beast and might break or get overloaded, or as was the case, hacked, whereas serving some static pre-built few-megabytes image files from nginx or whatever seems much more resilient.

Edit: Mark and correct untrue statements about the December 2024 build server security incident

Yes, that was the whole point of it.... and it's pretty brilliant!

In general, I would disagree with this insofar as it's really not any different (from a security perspective) than grabbing a standard/default image and installing the packages from the repo (online or offline, it's the same). This is because you're already taking a bit of a leap of faith regarding the build system itself doing the right thing to create the base images and all of the packages you want to install. You could, of course, build it yourself locally, but unless you're reading every line of code in every relevant source file, you're still trusting that everything is properly secure. For the record, I do trust OpenWrt to be well vetted and secure to the best knowledge of the developer team, but CVEs do crop up from time to time and/or there is always the risk of other software supply chain attacks that could be unknown until they are activated or otherwise discovered.

Yes, this was true, and so with all that I said above, there is always a risk that there is some currently unknown vulnerability. The response to this security vulnerability was swift, and it did not come back online until it was deemed secure.

You could actually spin up your own instance of an ASU server if you wanted it to serve only you (thus removing the malicious actor from the equation), and then you'd have the same ease of use with ASU, but you'd know that you're the only person on the server.

Anyway, you can read the details of the incident and decide for yourself if you feel comfortable with the service.

Sure... there are a number of ways, including this, that you can mitigate any potential risks (security, availability, etc.) that you may face with a public ASU server. It comes down to a question of trust and convenience of the tools provided by the project vs effort and maintenance on your part to download, verify, and maintain a local 'repo' of sorts. There isn't a right or wrong answer here, just what makes the most sense for you.

1 Like