#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : "NO_PUBKEY" or "repository is not signed" -- package updates refuse to run # Fix : Install the key the modern way # Source: https://jbtecwiz.com/support/lnx-sec-repo-key # # Run as : Root shell # Expect : 20 minutes # Risk : medium # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # apt reports NO_PUBKEY or an unsigned repository. # # HOW TO UNDO IT # Remove the keyring file and the sources list entry. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 # - THIS SCRIPT WILL NOT RUN UNTIL YOU EDIT IT - cat >&2 <<'EOF' This script needs editing before it can run. Replace each of these with a real value: example.com (a placeholder domain) Then delete this block near the top of the script. EOF exit 2 if [ "$(id -u)" -ne 0 ]; then echo " This fix is documented as needing root. Re-run with sudo." >&2 exit 3 fi rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 5" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " "NO_PUBKEY" or "repository is not signed" -- package updates refuse to run" echo " Install the key the modern way" echo echo " Risk: medium Reversible 20 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi if step 1 'Identify which repository is failing and what key it wants.' '' cmd 'sudo apt update 2>&1 | grep -E '\''NO_PUBKEY|not signed|GPG error'\'''; then sudo apt update 2>&1 | grep -E 'NO_PUBKEY|not signed|GPG error' if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi if step 2 'Get the key from the vendor'\''s own HTTPS site -- not from a keyserver, and not from a random search result.' 'This key decides which packages your machine will trust to install as root. Fetching it over HTTPS from the vendor is the only step that makes the whole signature mechanism worth anything -- a key from an unverified source verifies nothing.' cmd 'curl -fsSL https://download.example.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/example-archive-keyring.gpg'; then curl -fsSL https://download.example.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/example-archive-keyring.gpg if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Point the source at that specific keyring rather than trusting it globally.' 'signed-by scopes the key to one repository. A key in the global trusted set can sign packages for every repository on the machine, which is exactly what the deprecation of apt-key was about.' cmd 'echo '\''deb [signed-by=/usr/share/keyrings/example-archive-keyring.gpg] https://download.example.com/apt stable main'\'' | sudo tee /etc/apt/sources.list.d/example.list'; then echo 'deb [signed-by=/usr/share/keyrings/example-archive-keyring.gpg] https://download.example.com/apt stable main' | sudo tee /etc/apt/sources.list.d/example.list if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi step 4 'Never use [trusted=yes] to silence this -- it disables verification for that repository entirely.' '' manual || true if step 5 'Update and confirm.' '' cmd 'sudo apt update'; then sudo apt update if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 5 failed. The rest of the fix may depend on it." >&2 fi fi rule " Confirm it worked" prose 'apt update completes with no GPG errors.' if [ "$DRYRUN" = "0" ]; then sudo apt update 2>&1 | tail -5 fi rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-sec-repo-key" else echo " Finished." fi echo prose 'To undo: Remove the keyring file and the sources list entry.' rule