Ubuntu: fixing apt NO_PUBKEY errors by converting deprecated keyring to signed-by attribute

If apt update throws warnings about invalid signature verification and NO_PUBKEY, you may need to migrate from using the deprecated system keyring to using a ‘signed-by’ attribute in your apt repo definition file.

Here are examples of errors you might see when doing an ‘apt update’.

W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://linux.dropbox.com/ubuntu disco Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY FC918B335044912E

W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: https://cli.github.com/packages stable InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 23F3D4EA75716059

W: Failed to fetch https://cli.github.com/packages/dists/stable/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 23F3D4EA75716059

If these are valid keys in the deprecated system keyring (they exist and are not expired), you can export them from the system keyring and then reference their path in the ‘signed-by’ attribute of the apt repo definition file to resolve the issue.

Identify all NO_PUBKEY warnings

First, we will parse the warnings from ‘apt update’ to construct a list of repository URL and hexadecimal key id that need to be addressed.

# show list of public keys not found in apt
sudo apt update 2>&1

# isolate keys referenced in apt warnings
keylist=$(sudo apt update 2>&1 | grep -Po '^W.* NO_PUBKEY \K(.*)')
echo -e "keys mentioned in apt warnings:\n$keylist"

# isolate repo url referenced in warnings
repolist=$(sudo apt update 2>&1 | grep -Po "(error:|fetch) (http|https):\/\/\K([^ \/]*)")
echo -e "repos mentioned in apt warnings:\n$repolist"

# make sure keylist and repolist have same count of items
# if these do not match, grep is not parsing correctly (do not continue)
[[ $(echo "$repolist" | wc -l) -eq $(echo "$keylist" | wc -l) ]] && echo "OK same count" || echo "ERROR keylist and repolist do not have same count"

Validate keys found in deprecated system keyring

Then we want to validate that all the hexadecimal key ids identified are actually in the system keyring, and are both valid and not expired.

# package for dumping gpg key details
sudo apt install -y pgpdump

# show whether key can be found in legacy system keyring
COUNTER=1
for key in $keylist; do keyname=$(echo $repolist | cut -d' ' -f $COUNTER); gpg --export $key 1>/dev/null; [[ $? -eq 0 ]] && echo "OK $key for $keyname found in system keyring" || echo "NOTFOUND $key for $keyname not in system keyring"; ((COUNTER+=1)); done

# show whether key in legacy system keyring has expired
COUNTER=1
for key in $keylist; do keyname=$(echo $repolist | cut -d' ' -f $COUNTER); echo -e "\n==Expiration for $key used by $keyname"; gpg --export $key | pgpdump | grep expiration -A1; [[ $? -ne 0 ]] && echo "n/a no expiration set"; ((COUNTER+=1)); done

If the key is NOT found in the deprecated system keyring or is expired, you have one of two choices.

Option#1: Fetch from keyserver

The key may be distributed to a standard keyserver.  You can try fetching and importing it into the deprecated system keyring.

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key

Alternate key servers include: pgpkeys.mit.edu, pool.sks-keyservers.net

Option#2: Manually search for key on internet, download

See my previous article here on searching duckduckgo.com to find the gpg key for a remote repository, under the section “Find newest PGP key”.

The downloaded file can be added to the deprecated system keyring using apt-key.

sudo apt-key add <filePath>

Export from system keyring to independent file

Export from the deprecated system keyring to the recommended implementation, which is an independent file under “/usr/share/keyrings”.

# export gpg key from deprecated trusted keyring to /usr/share/keyrings
COUNTER=1
for key in $keylist; do keyname=$(echo $repolist | cut -d' ' -f $COUNTER); echo "exporting $key as /usr/share/keyrings/$keyname.gpg"; gpg --export $key | sudo tee /usr/share/keyrings/$keyname.gpg 1>/dev/null; ((COUNTER+=1)); done

# MUST set permissions for files
sudo chmod 644 /usr/share/keyrings/*

In the above command, we export to a binary .gpg format.  If we had dearmored the file (‘-a’ flag) into a PGP public key text file, then we would have saved with the ‘.asc’ suffix instead.

If the files in ‘/usr/share/keyrings’ do not have at least mode 644, apt will continue to throw a NO_PUBKEY error.

Modify apt repo definition files

Finally, we need to add a ‘signed-by’ attribute to each apt repository definition file that references these remote URL.

NOTE: This script only shows you what strings to add to your files, you still need to manually add them.  It does NOT modify any files.

# show 'signed-by' value that needs to be added to apt repo url files
for repo in $repolist; do echo -e "\nTODO ADD '[arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/$repo.gpg]' to these files:";grep -srl "$repo" /etc/apt | grep -Ev "save|distUpgrade|~"; done 

The ‘signed-by’ attribute goes inside square brackets right after the initial ‘deb’, with the syntax shown below.

deb [arch=xxx, signed-by=/usr/share/keyrings/xxx.gpg] https://repoURL dist component

Validate resolution

Check apt update again and the warnings should be gone.

sudo apt update

Deleting key from system keyring

As an optional step, you can now delete the key from the deprecated system keyring.

# optional step
for key in $keylist; do sudo apt-key del $key; done

 

REFERENCES

debian.org, current scheme for package signature checks

cirwin.in, online PGP decoder

askubuntu.com, script for finding all legacy keys and converting to new format

askubuntu.com, exporting legacy key from legacy keyring to its own file and referencing in apt repo definition

askubuntu.com, finding the signing key for a ppa launchpad

askubuntu.com, alternate pgp keyserver at pgpkeys.mit.edu (primary=keyserver.ubuntu.com), screenshot showing how to get key id for ppa

linuxuprising.com, script for identifying and exporting all legacy keys from keyring (another alternate keyserver=pool.sks-keyservers.net)

itfoss.com, explanation of legacy keyrings, dearmor, deprecation of ‘apt-key’

omgubuntu.co.uk – fix apt-key deprecation error with export and dearmor

ubuntu man pages, apt-key deprecated in Ubuntu 21

jeffmotoretta.ca – why apt-key is deprecated

NOTES

Check if the developers have loaded newer key into keyserver

curl -s "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${hexid}" | pgpdump | grep -E "User ID -|expiration" -A1