With pacman development progressing smoothly towards an upcoming 4.0.0 release, I thought it would be nice to write about everybody’s favourite (and a not at all controversial) topic… package signing. I will separate the discussion into several parts over the coming weeks, writing about a new area when I personally consider the interface in that area to being relatively finalised. That is not to say what is written about here will not change before the final release, just that I find it unlikely. Note also that I will focusing more on the technical details of the package signing implementation in pacman and its tools. So there will be limited discussion on issues a distribution may face using these features and I will not be specifically covering how Arch Linux will make use of these features.
The first thing that you are going to need to sign packages and repo databases is a PGP key. All the details of creating one using GnuPG can be found elsewhere. The only real consideration is the choice of key type. Currently a 2048-bit RSA key seems to be the gold standard. Going to 4096-bit is probably excessive and being a larger key has the side effect of slowing down the verification process (to an extent that is noticeable on older CPUs).
Once you have that sorted, it is time to sign some packages using makepkg. The implementation is quite simple. When a package signature is needed, makepkg simply calls gpg --detach-sign on the package(s) it creates. If you have the GnuPG-Agent is running, you will not even be asked for your passphrase (depending on your set-up). Deciding whether to sign packages or not is primarily controlled through the “sign” BUILDENV option in makepkg.conf, but can be overridden on the command line using --sign or --nosign. By default, the package will be signed with your primary PGP key. If you wish to use another key, you can set the GPGKEY variable (either in makepkg.conf or the environment), or use the --key option with makepkg.
The additions to repo-add are similarly simple. When adding a package to a repo database, repo-add checks for a detached signature and if present adds it to the package description entry, ready for libalpm to process. Finally, signing packages is not enough. We also need the ability to sign the package database (e.g. to prevent the holding back of an update to an individual package containing a security vulnerability). This is done using similar options to makepkg, with -s/--sign to tell repo-add to sign the database and --key (or the environmental variable GPGKEY) to select a non-default GPG key to sign with. In addition, repo-add has a -v/--verify flag that checks the current signature is valid before proceeding (very important as repo-add adjusts the current database rather than regenerating it from scratch).
As an aside, a couple of other useful security features have made their way into makepkg and repo-add during this development cycle. The ability to automatically check PGP signatures for source files has been added to makepkg (thanks to first time contributer Wieland Hoffmann). This is done by detecting files in the source ending in the standard extensions .sig and .asc. A source file and signature can be quickly be specified using bash expansion like:
sources=($pkgname-$pkgver.tar.gz{,.sig})
which makes it quite clear which source files have signatures. If wanted, this check can be skipped using the --skippgpcheck or the --skipinteg options (the latter of which also ships checksum checks). Also, repo-add includes a SHA256 checksum in the repo database in addition the the current MD5 checksum, although currently libalpm (and thus pacman) does nothing with this entry. (Despite some prior assertion, adding that properly took more than a one line change… but I will leave that there.)
Finally, a quick note on the challenges faced by distributions using these tools for package and database signing. The facilities provided by makepkg and repo-add work well for repositories where the packages get built locally, added to the repo database and then mirrored to their server (such as the repo I provide), but may not be ideal to use for a larger distribution repository maintained by multiple people. For example, if building a package on an remote build server, then the packager should not want to put their private PGP key onto that server to sign the package. It currently appears that there is no easy way around this, so the package building and signing steps need to be separated, with the built package downloaded locally and then signed (although this may change in future GnuPG releases as I see patches have been recently submitted to their mailing list providing a proof-of-concept implementation to improve remote signing functionality). Similarly, how is it best to sign a repository database that is added to by multiple packagers? Having some sort of master key sign it requires some sort of reduction in security of the passphrase (with either all people pushing to the repo knowing it or having it somehow accessible to the script adding the packages to the repo database). If set-up with care, this may be acceptably low risk for a distribution to use (and, from what I understand, this is what is done by several distributions), but personally I do not see it as an ideal solution. And that brings us back to the issue of how to best sign a remote file. So, implementing the tools may actually be the simple part in all of this…