TL;DR
This blog post will explain how GPG signatures are implemented for RPM files and yum repository metadata, as well as how to generate and verify those signatures.
This blog post also explains what the purpose of the pygpgme python library is, how it is used for verifying GPG signatures in RPMs and yum repository metadata, and an unfortunate bug related to pygpgme found in yum as prepared for CentOS 5 / Red Hat Enterprise Linux 5.
This is a companion post to a previous blog post about GPG signing and verifying deb packages and apt repositories.
Why GPG sign?
Signing data with a GPG key enables the recipient of the data to verify that no modifications occurred after the data was signed (assuming the recipient has a copy of the sender’s public GPG key).
RPM package files (.rpm) and yum repository metadata can be signed with GPG.
pygpgme
pygpgme is a python wrapper around a library called GPGME, a library designed to make using GPG much more straightforward for applications.
GPGME provides a convenient set of interfaces for accessing information about GPG keys, encrypting data, decrypting data and more.
Most of yum is written in python, so yum makes extensive use of this library for verifying digital signatures found in RPM packages and yum repository metadata.
We’ll now take a quick look at the different types of GPG signatures used in RPM packages and yum.
RPM GPG signatures
The RPM file format is a binary file format that consists of:
- A data structure called a
lead
, which has mostly been obsoleted and superseded by the header structure. - A signature section which may contain a GPG signature that can be used for verifying that the RPM file has not been modified since it was created.
- A header section containing a set of index entries, which map numbered types to values. This is used for storing internal information about RPM package files.
- A gzip compressed CPIO archive containing the actual files to be written to the filesystem.
A GPG signature for an RPM file is calculated from data found in the header and the compressed CPIO archive and stored in the signature section.
Signing RPMs with rpmsign
(or rpm --addsign
)
Begin first by creating a file called .rpmmacros
in your home directory containing the value %_gpg_name
. The value of this variable will be used to determine which GPG key should be used to sign the RPM.
For example, if your GPG key was for ‘Alicia Gonzalez’ with an email address of agonzalez@packagecloud.io, your .rpmmacros
file would look like this:
You can then sign the RPM by running either:
or
yum repository metadata GPG signatures
You can also sign yum repository metadata. The purpose of signing repository metadata is to ensure that the metadata has not been modified or tampered with since it was generated. This is an often overlooked component of generating secure yum repositories.
Unfortunately, createrepo
contains no option to automatically generate GPG signatures for repository metadata at the time this was written, but luckily, generating GPG signatures for metadata is relatively straight forward.
After you’ve generated your repository metadata using createrepo
, you can generate a detached GPG signature by running:
This command will create a file named repodata/repomd.xml.asc
which contains an ASCII version of the GPG signature of the repository metadata file repomd.xml
.
yum clients will automatically request this file and attempt to verify the signature contained here if the proper options are set in the repository config (see the next section of this blog post for more information).
Enable verification of GPG signatures for RPMs and metadata
In order to properly verify GPG keys, two things must happen on the client system:
- You must install the
pygpgme
package so that yum can verify gpg signatures. If this package is not installed. yum will skip verification of GPG signatures. In order to install this package, you will need to have the EPEL repository installed on your system. - You must also enable the proper options in the yum repository configuration file, as explained next.
yum repositories are configured by writing a file ending with .repo
to a client’s /etc/yum.repos.d/
directory, for example:
/etc/yum.repos.d/my_stuff.repo
There are two separate options which must be enabled in this file depending on which type of GPG verification is desired:
gpgcheck=1
enables GPG verification of RPM files themslevesrepo_gpgcheck=1
enables GPG verification of the yum repository metadata
You can specify the public GPG keys which should be imported by listing their URLs to the gpgkey
option:
You may specify multiple keys if desired, as well:
CentOS 5 / RHEL 5 yum broken dependency
Unfortunately, yum on CentOS 5 and RHEL 5 has a broken dependency and will not automatically install pygpgme when yum is installed.
This means that in order to actually verify GPG signatures of RPMs or yum repositories, you must manually install pygpgme
from the EPEL repository on the client machine if the client machine is running CentOS 5 or RHEL 5.
Conclusion
Generating and verfying GPG signatures is critical to establishing the authenticity of package objects and the repositories storing those objects.
Knowing that a package being installed on your system is the same package that was generated by a vendor, and that you were able to retrieve that package from the repository generated by the vendor is the only way to ensure that you are safely installing software in your infrastructure.
Unfortunately, signing and verifying RPM packages and yum repositories is tricky and there are a few bugs which can prevent clients from properly verifying signatures.
packagecloud.io signs repository metadata it generates using a GPG key so that users of our repositories know, without a doubt, that the repository metadata their package manager downloaded was generated by packagecloud.io.
The Chef cookbook and Puppet module provided by packagecloud can be used to quickly and easily install packagecloud repositories across your infrastructure, or customers’ infrastructure. Both will automatically install pygpgme
or warn the user if pygpgme
is unable to be installed due to the EPEL repository being absent on the system.