This commit is contained in:
meek2100 2026-01-13 13:14:25 -08:00 committed by GitHub
commit c1f6a5bef1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 281 additions and 40 deletions

144
Why a Spare Router.md Normal file
View file

@ -0,0 +1,144 @@
# Why a "Spare Router" Configuration?
Many people who use OpenWrt wind up with unused routers when
they retire one for a newer device.
These are perfectly functioning devices that are perhaps older
or missing a certain function.
They could be easily re-used and passed along to friends, family or neighbors.
BUT... you have to solve a couple problems:
1. The router retains all your personal info:
passwords, certificates, idiosyncratic packages. etc.
2. You can't remember how it was configured, so you
can't even connect to it.
**The remedy:** A "spare router" configuration script that
you can use when you take a router out of service.
It leaves the router with current OpenWrt firmware
configured for Wifi access, and
a known useful set of package to make it easy to reuse.
The script also prints a label that you can attach to
the router so that you can get started quickly the next
time you get it out.
## Usage
When you retire a router from service, run this script.
It is available at
[config-spare-router.sh](https://github.com/richb-hanover/OpenWrtScripts/blob/master/config-spare-router.sh)
To use it:
* Connect your laptop via Ethernet to a LAN port
* Connect the router's WAN Ethernet to the Internet
(perhaps the LAN port of the new router).
* Use the LuCI GUI to reset settings to
factory default (**System -> Backup/Flash firmware**)
* Use the [Firmware Selector](https://firmware-selector.openwrt.org/)
to find and flash the latest firmware
* SSH into the router using the Ethernet connection
* Run the attached script (see the instructions within it)
* The script prints a label: cut it out and tape it to the router
* _Pro tip:_ Printing the label in 12-point type produces a
"business card" size label.
* _Pro tip:_ Snip the model number from the paper
and tape it directly to the power brick.
(Use the text from the "Power Brick Label" section.)
* _Pro tip:_ Place the router and its power brick in a Ziploc bag
to keep them together.
The `config-spare-router.sh` script may be run multiple times without bad effect.
When the script completes, it displays configuration similar to this,
suitable for printing and taping to the router.
```text
# ======= Printed with: print-router-label.sh =======
# Device: Linksys E8450 (UBI)
# OpenWrt: OpenWrt 23.05.5 r24106-10cc5fcd00
# Connect to: http://SpareRouter.local
# or: ssh root@SpareRouter.local
# LAN: 172.30.42.1
# User: root
# Login PW: SpareRouter
# Wifi SSID: SpareRouter
# Wifi PW: <no password>
# Configured: 2024-11-28
# === See github.com/richb-hanover/OpenWrtScripts ===
#
# Label for Power Brick: Linksys E8450 (UBI)
```
## When you (re)deploy the router
The default settings are (intentionally) insecure.
Remember to change the following:
* Root password (**System -> Administration**)
* Wifi credentials (**Network -> Wireless**)
* Enable other Wifi radios (**Network -> Wireless**)
* Change the LAN interface IP address and other settings as needed (**Network -> Interfaces**)
* (Optional) Configure SQM (**Network -> SQM QoS**)
* (Optional) Change the hostname (**System -> System**)
* (Optional) Install other packages as needed
* (Optional) Travelmate (**Services -> Travelmate**)
Click the **Interface Wizard** button one time
* (Optional) Re-run the `print-router-label.sh` to create
a new label and tape it to the router, so you don't
have to fuss the next time you work on it.
## Rationale for the configuration choices
This script was designed for ease of use.
It presumes that it is being installed on a modern (post-2021)
router that has plenty of RAM and Flash storage, so that size
was not a consideration.
* **Root password:** To make it easy to re-use the router,
the `root` password is set to `SpareRouter`.
There is no need for strong security here, as you will be changing
the password when you set it up in its new location.
* **LAN Address:** The LAN IP address is set to `172.30.42.1`.
This is a
[valid private IP address range](https://en.wikipedia.org/wiki/Private_network)
(like `10...` and `192.168...` subnets) but it is less commonly used.
This means that you can bring the router into virtually any
network environment without concern for IP address conflicts,
then use the LuCI GUI to configure the LAN.
* **Hostname:** is set to "SpareRouter".
Because `umdns` is installed, you can connect using
`http://SpareRouter.local` or `ssh root@SpareRouter.local`
no matter what the LAN IP address is.
* **Wifi settings:** The SSID is of the _first_ radio is set
to `SpareRouter` without encryption.
No other radios are enabled.
As with the root password, there is no need for a strong password,
because you will be changing it immediately.
* **Time Zone:** As a convenience, the time zone is set to `Americas/New York`.
You can use the LuCI GUI to re-configure as needed.
* **Software packages:** The script installs a minimal set of useful
packages that are required to bootstrap a new router.
* **luci** Released versions of OpenWrt already install `luci`.
Re-installing does no harm.
* **umdns** Allows the router to advertise its name as "SpareRouter"
(e.g., connect using `ssh root@sparerouter.local`)
* **luci-app-sqm** All OpenWrt routers ought to have the SQM package
installed to minimize bufferbloat. Just do it.
* **travelmate** _and_
* **luci-app-travelmate** This packages allow a router to
act as a Wifi repeater by making a wireless "uplink"
to an existing network
Even if there's no Ethernet connection for the spare routers's WAN port,
you can use the wireless uplink to download additional packages.
## Modifications
This script provides a stable platform for re-deploying old routers.
Feel free to make suggestions (create an Issue) for _minimal_ tweaks that
would improve the script. Enjoy!
## Old information
The script also has a large number of lines that are commented out.
These were steps for other packages that are not essential for a "Spare Router".
Feel free to experiment with these sections in your own copy of the script.

View file

@ -11,21 +11,27 @@
# Thanks, too, to hnyman for important comments on this script
#
# Version history
# 0.2.3 - added support for selective IPK caching with version retention control for offline recovery installs
# 0.2.2 - editorial tweaks to help text -richb-hanvover
# 0.2.1 - fixed typo in awk script for dependency detection
# 0.2.0 - command interface
# 0.1.0 - Initial release
PCKGLIST=/etc/config/opkg.installed # default package list
SCRIPTNAME=$(basename $0) # name of this script
COMMAND="" # command to execute
PCKGLIST=/etc/config/opkg.installed # Default package list
IPK_CACHE_LIST="/etc/config/opkg.ipk_cache_list" # List of packages to cache IPKs for
IPK_CACHE_DIR="/etc/opkg/ipk_cache" # Directory to store cached IPK files
IPK_CACHE_ALL_VERSIONS=false # Cache only the latest IPK per package (default). Set to true to keep all versions.
SCRIPTNAME=$(basename $0) # Name of this script
COMMAND="" # Command to execute
INSTLIST=$(mktemp) # list of packages to install
PREQLIST=$(mktemp) # list of prerequisite packages
INSTLIST=$(mktemp) # List of packages to install
PREQLIST=$(mktemp) # List of prerequisite packages
UPDATE=false # Update the package database
OPKGOPT="" # Options for opkg calls
VERBOSE=false # Be verbose
DEBUG_TRACE=false # Set to true to enable shell debug tracing (set -x)
UPDATE=false # update the package database
OPKGOPT="" # options for opkg calls
VERBOSE=false # be verbose
cleanup () {
rm -f $INSTLIST $PREQLIST
@ -40,11 +46,18 @@ Available commands:
write write a list of currently installed packages
install install packages on list not currently installed
script output a script to install missing packages
cache-ipks download .ipk files for specific packages to a local cache
Options:
-u update the package database
-t test only, execute opkg commands with --noaction
-v be verbose
-x enable shell debug tracing (set -x)
Configuration:
IPK_CACHE_ALL_VERSIONS in script controls whether to cache all .ipk versions or just the latest.
Default is to cache only the latest. Set IPK_CACHE_ALL_VERSIONS=true to retain all versions.
Warning: caching all versions may consume significant storage and must be cleaned manually.
$SCRIPTNAME can be used to re-install those packages that were installed
before a firmware upgrade but are not part of the new firmware image.
@ -70,35 +83,33 @@ Alternatively, you can execute
to output a shell script that will contain calls to opkg to install those
missing packages. This might be useful if you want to check which packages
would be installed of if you want to edit that list.
would be installed or if you want to edit that list.
In order for this script to work after a firmware upgrade or reboot, the
opkg database must have been updated. You can use the option -u to do this.
You can specify the option -t to test what $SCRIPTNAME would do. All calls
to opkg will be made with the option --noaction. This does not influence
the call to opkg to write the list of installed packages, though.
the call to opkg to write the list of installed packages, though.
"
}
trap cleanup SIGHUP SIGINT SIGTERM EXIT
# parse command line options
while getopts "htuvw" OPTS; do
while getopts "htuvx" OPTS; do
case $OPTS in
t )
OPKGOPT="$OPKGOPT --noaction";;
u )
UPDATE=true;;
v )
VERBOSE=true;;
[h\?*] )
echo_usage
exit 0;;
t ) OPKGOPT="$OPKGOPT --noaction";;
u ) UPDATE=true;;
v ) VERBOSE=true;;
x ) DEBUG_TRACE=true; VERBOSE=true;;
[h\?*] ) echo_usage; exit 0;;
esac
done
shift $(($OPTIND - 1))
[ "$DEBUG_TRACE" = true ] && set -x
# Set the command
COMMAND=$1
@ -110,7 +121,6 @@ fi
#
# Help
#
if [ "x$COMMAND" == "x" ]; then
echo "No command specified."
echo ""
@ -125,20 +135,113 @@ fi
#
# Write
#
if [ $COMMAND = "write" ] ; then
if $VERBOSE; then
echo "Saving package list to $PCKGLIST"
fi
if [ $COMMAND = "write" ]; then
$VERBOSE && echo "Saving package list to $PCKGLIST"
# NOTE: option --noaction not valid for list-installed
opkg list-installed > "$PCKGLIST"
exit 0
fi
#
# Cache IPKs
#
if [ $COMMAND = "cache-ipks" ]; then
if [ ! -f "$IPK_CACHE_LIST" ]; then
echo "Error: IPK cache list '$IPK_CACHE_LIST' not found."
echo "Please create this file and list the packages you want to cache (one per line)."
exit 1
fi
if [ ! -d "$IPK_CACHE_DIR" ]; then
$VERBOSE && echo "Creating IPK cache directory: $IPK_CACHE_DIR"
mkdir -p "$IPK_CACHE_DIR" || {
echo "Error: Could not create IPK cache directory '$IPK_CACHE_DIR'."
exit 1
}
fi
$VERBOSE && echo "Caching IPK files from list: $IPK_CACHE_LIST to $IPK_CACHE_DIR"
$VERBOSE && echo "Checking freshness of opkg cache..."
# Only update if lists are older than 5 minutes
CACHE_AGE_LIMIT=300 # seconds
needs_update=false
for list in /var/opkg-lists/*; do
if [ ! -f "$list" ] || [ "$(($(date +%s) - $(date -r "$list" +%s)))" -gt "$CACHE_AGE_LIMIT" ]; then
needs_update=true
break
fi
done
if $needs_update; then
$VERBOSE && echo "Running opkg update..."
opkg update
$VERBOSE && echo "Finished opkg update"
else
echo "Skipping opkg update: cache is fresh"
fi
TOTAL=0
DOWNLOADED=0
SKIPPED=0
REMOVED=0
while read PACKAGE; do
PACKAGE=$(echo "$PACKAGE" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -z "$PACKAGE" ] && continue
TOTAL=$((TOTAL + 1))
IPK_LATEST_FILENAME=$(opkg info "$PACKAGE" | grep "^Filename:" | awk '{print $2}')
[ -z "$IPK_LATEST_FILENAME" ] && {
echo "Warning: Could not find filename for package '$PACKAGE'. Skipping."
continue
}
BASE_PACKAGE_NAME=$(opkg info "$PACKAGE" | grep "^Package:" | awk '{print $2}')
IPK_LATEST_PATH="$IPK_CACHE_DIR/$IPK_LATEST_FILENAME"
if ! $IPK_CACHE_ALL_VERSIONS && [ -n "$BASE_PACKAGE_NAME" ]; then
CACHED_FILES=$(find "$IPK_CACHE_DIR" -maxdepth 1 -type f -name "${BASE_PACKAGE_NAME}_*.ipk")
for CACHED_FILE in $CACHED_FILES; do
CACHED_FILENAME=$(basename "$CACHED_FILE")
if [ "$CACHED_FILENAME" != "$IPK_LATEST_FILENAME" ]; then
$VERBOSE && echo "Removing old version of $BASE_PACKAGE_NAME: $CACHED_FILENAME"
rm -f "$CACHED_FILE" || echo "Warning: Failed to remove $CACHED_FILE"
REMOVED=$((REMOVED + 1))
fi
done
fi
if [ -f "$IPK_LATEST_PATH" ]; then
echo "$PACKAGE is up to date ($IPK_LATEST_FILENAME)"
SKIPPED=$((SKIPPED + 1))
else
$VERBOSE && echo "Downloading $PACKAGE$IPK_LATEST_FILENAME"
opkg $OPKGOPT download "$PACKAGE"
if [ $? -eq 0 ] && [ -f "./$IPK_LATEST_FILENAME" ]; then
mv "./$IPK_LATEST_FILENAME" "$IPK_LATEST_PATH" || echo "Error: Failed to move downloaded IPK"
$VERBOSE && echo "Moved to $IPK_CACHE_DIR"
DOWNLOADED=$((DOWNLOADED + 1))
else
echo "Warning: Download failed or file missing for '$PACKAGE'"
fi
fi
done < "$IPK_CACHE_LIST"
echo ""
echo "IPK Cache Summary:"
printf " %-25s %d\n" "Packages scanned:" $TOTAL
printf " %-25s %d\n" "New downloads:" $DOWNLOADED
printf " %-25s %d\n" "Older versions removed:" $REMOVED
printf " %-25s %d\n" "Already up to date:" $SKIPPED
echo ""
exit 0
fi
#
# Update
#
if $UPDATE; then
opkg $OPKGOPT update
fi
@ -146,12 +249,9 @@ fi
#
# Check
#
if [ $COMMAND == "install" ] || [ $COMMAND == "script" ]; then
# detect uninstalled packages
if $VERBOSE && [ $COMMAND != "script" ]; then
echo "Checking packages... "
fi
$VERBOSE && [ $COMMAND != "script" ] && echo "Checking packages... "
cat "$PCKGLIST" | while read PACKAGE SEP VERSION; do
# opkg status is much faster than opkg info
# it only returns status of installed packages
@ -162,10 +262,10 @@ if [ $COMMAND == "install" ] || [ $COMMAND == "script" ]; then
# collect prerequisites
opkg info "$PACKAGE" |
awk "/^Depends: / {
sub(\"Depends: \", \"\"); \
gsub(\", \", \"\\n\"); \
print >> \"$PREQLIST\"; \
}"
sub(\"Depends: \", \"\");
gsub(\", \", \"\\n\");
print >> \"$PREQLIST\";
}"
fi
done
fi
@ -173,15 +273,12 @@ fi
#
# Install or script
#
if [ $COMMAND == "install" ]; then
# install packages
cat "$INSTLIST" | while read PACKAGE; do
if grep -q "^$PACKAGE\$" "$PREQLIST"; then
# prerequisite package, will be installed automatically
if $VERBOSE; then
echo "$PACKAGE installed automatically"
fi
$VERBOSE && echo "$PACKAGE installed automatically"
else
# install package
opkg $OPKGOPT install $PACKAGE