Yum GPG keys for Jenkins
I use/mirror a number of third-party Yum repositories. The typical pattern for these is to import the vendor’s public signing key using rpm --import
and then setup a new entry /etc/yum.repos.d
entry. Some vendors have -release
packages that handle both steps. The trouble is, this creates a bit of a chicken/egg problem and doesn’t lend itself so well to automation.
Instead I tend to favour having Puppet manage the keys and repositories. Since the GPG keys don’t change very often we can safely import them into Puppet. Puppet pushes out the key as a file resource before setting up a Yum repository resource with the attribute gpgkey = file:///
.
Here is what the resulting class for Jenkins looks like.
class yum::repos::jenkins {
$yum_jenkins_url = extlookup('yum_jenkins_url', 'http://pkg.jenkins-ci.org/redhat-stable/')
$yum_jenkins_gpg = '/etc/pki/rpm-gpg/RPM-GPG-KEY-jenkins'
file { $yum_jenkins_gpg:
source => "puppet:///modules/yum${yum_jenkins_gpg}",
}
yumrepo { 'jenkins':
descr => 'Jenkins',
baseurl => $yum_jenkins_url,
gpgcheck => 1,
gpgkey => "file://${yum_jenkins_gpg}",
require => File[$yum_jenkins_gpg],
}
}
The problem
This pattern works fine for most vendors. It works fine for Jenkins on EL6. But it throws the following error on EL5.
notice: /File[/etc/pki/rpm-gpg/RPM-GPG-KEY-jenkins]/ensure: defined content as '{md5}9fa06089848262c5a6383ec27fdd2575'
notice: /Stage[main]/Yum::Repos::Jenkins/Yumrepo[jenkins]/descr: descr changed '' to 'Jenkins'
notice: /Stage[main]/Yum::Repos::Jenkins/Yumrepo[jenkins]/baseurl: baseurl changed '' to 'http://pkg.jenkins-ci.org/redhat-stable/'
notice: /Stage[main]/Yum::Repos::Jenkins/Yumrepo[jenkins]/gpgcheck: gpgcheck changed '' to '1'
notice: /Stage[main]/Yum::Repos::Jenkins/Yumrepo[jenkins]/gpgkey: gpgkey changed '' to 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-jenkins'
err: /Stage[main]/Jenkins::Package/Package[jenkins]/ensure: change from absent to present failed: Execution of '/usr/bin/yum -d 0 -e 0 -y install jenkins' returned 1: warning: rpmts_HdrFromFdno: Header V4 DSA signature: NOKEY, key ID d50582e6
Traceback (most recent call last):
File "/usr/bin/yum", line 29, in ?
yummain.user_main(sys.argv[1:], exit_code=True)
File "/usr/share/yum-cli/yummain.py", line 309, in user_main
errcode = main(args)
File "/usr/share/yum-cli/yummain.py", line 261, in main
return_code = base.doTransaction()
File "/usr/share/yum-cli/cli.py", line 410, in doTransaction
if self.gpgsigcheck(downloadpkgs) != 0:
File "/usr/share/yum-cli/cli.py", line 510, in gpgsigcheck
self.getKeyForPackage(po, lambda x, y, z: self.userconfirm())
File "/usr/lib/python2.4/site-packages/yum/__init__.py", line 3544, in getKeyForPackage
keys = self._retrievePublicKey(keyurl, repo)
File "/usr/lib/python2.4/site-packages/yum/__init__.py", line 3509, in _retrievePublicKey
keys_info = misc.getgpgkeyinfo(rawkey, multiple=True)
File "/usr/lib/python2.4/site-packages/yum/misc.py", line 375, in getgpgkeyinfo
raise ValueError(str(e))
ValueError: unknown pgp packet type 17 at 706
Running yum install jenkins
by hand results in exactly the same error. So we know it’s not Puppet getting in the way. Following the jenkins-ci.org instructions of running rpm --import
and setting up the repository without gpgkey
also works. So what’s the difference?
Some digging
The best place to start would be the code in the traceback. Looking at __init__.py:getKeyForPackage()
we can see that if Yum is given a gpgkey
attribute it will make sure that the key is imported. It determines the presence and some other information about the key by calling misc.py:getpgpkeyinfo()
which in turn uses pgpmsg.py
to do the actual parsing. At the top of that file is a list of numeric packet types as constants.
For Yum 3.2.22, which is used in EL5, these numerics only go as high as 14. Which would explain why it’s complaining that 17 is unknown. By running git blame
and git describe
against Yum’s git repository we can see that the additional packet types were first present in Yum 3.2.25. Which explains why we don’t see the same problem in EL6 which uses Yum 3.2.29.
So what is packet type 17? The IANA assignments for PGP Packet Types/Tags tell us that it is a “User Attribute Packet”. The assignments for PGP User Attribute Types also tell us that there’s only one registered type of user attribute, suggesting that it must be an image.
The solution
It seems that an image shouldn’t be essential to our use of verifying package integrity. The good news is that we can remove it from the public key.
Start by obtaining a copy of the original public key.
$ curl http://pkg.jenkins-ci.org/redhat-stable/jenkins-ci.org.key > RPM-GPG-KEY-jenkins
Import it into GPG. The version of GPG that you use shouldn’t matter too much. I’m using a more recent version on my Mint desktop machine.
$ gpg --import RPM-GPG-KEY-jenkins
gpg: /home/dan/.gnupg/trustdb.gpg: trustdb created
gpg: key D50582E6: public key "Kohsuke Kawaguchi <kk@kohsuke.org>" imported
gpg: Total number processed: 1
gpg: imported: 1
List the available keys to determine it’s identity. We’re after that hex value D50582E6
.
$ gpg --list-keys
/home/dan/.gnupg/pubring.gpg
----------------------------
pub 1024D/D50582E6 2009-02-01
uid Kohsuke Kawaguchi <kk@kohsuke.org>
uid Kohsuke Kawaguchi <kohsuke.kawaguchi@sun.com>
uid [jpeg image of size 3704]
sub 2048g/10AF40FE 2009-02-01
Begin editing that key. We can see that there are three IDs within the key, the last one being the image that we want to remove. Mark it, delete it, and save the modifications.
$ gpg --edit-key D50582E6
gpg (GnuPG) 1.4.11; Copyright (C) 2010 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pub 1024D/D50582E6 created: 2009-02-01 expires: never usage: SC
trust: unknown validity: unknown
sub 2048g/10AF40FE created: 2009-02-01 expires: never usage: E
[ unknown] (1). Kohsuke Kawaguchi <kk@kohsuke.org>
[ unknown] (2) Kohsuke Kawaguchi <kohsuke.kawaguchi@sun.com>
[ unknown] (3) [jpeg image of size 3704]
gpg> uid 3
pub 1024D/D50582E6 created: 2009-02-01 expires: never usage: SC
trust: unknown validity: unknown
sub 2048g/10AF40FE created: 2009-02-01 expires: never usage: E
[ unknown] (1). Kohsuke Kawaguchi <kk@kohsuke.org>
[ unknown] (2) Kohsuke Kawaguchi <kohsuke.kawaguchi@sun.com>
[ unknown] (3)* [jpeg image of size 3704]
gpg> deluid
Really remove this user ID? (y/N) y
pub 1024D/D50582E6 created: 2009-02-01 expires: never usage: SC
trust: unknown validity: unknown
sub 2048g/10AF40FE created: 2009-02-01 expires: never usage: E
[ unknown] (1). Kohsuke Kawaguchi <kk@kohsuke.org>
[ unknown] (2) Kohsuke Kawaguchi <kohsuke.kawaguchi@sun.com>
gpg> save
Export the modifications to a new ASCII key file. We’ll give it a different filename to the original.
$ gpg --export -a D50582E6 > RPM-GPG-KEY-jenkins.el5
Now we can send out the modified key to any EL5 nodes and use the original for everyone else. Voila, the Puppet run completes and the package is installed successfully.
$yum_jenkins_gpg = $::operatingsystemmajrelease ? {
'5' => '/etc/pki/rpm-gpg/RPM-GPG-KEY-jenkins.el5',
default => '/etc/pki/rpm-gpg/RPM-GPG-KEY-jenkins',
}
If you’re wondering, operatingsystemmajrelease
is a custom fact. It works much like lsbmajdistrelease
without the gazillion dependencies.