Support signed RPM package feeds
cover Support signed RPM package feeds
This patchset re-enabled support for signed RPM package feeds, a feature that was disabled in dnf transition. It also extends the dnf selftest to cover signed package feeds (in addition to fixing the testcase which was not being run at all because of invalid naming).
The second patch pulls in new dependencies. On core-image-minimal (qemux86) the addition of gnupg also pulls 6 other packages, increasing the rootfs disk footprint ca. 9MB.
The following changes since commit df7f5221a56118da7654476f072c37ae1e75dc50:
libinput: Upgrade 1.7.3 -> 1.8.1 (2017-08-09 09:25:47 +0100)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib marquiz/fixes-11209
http://cgit.openembedded.org/openembedded-core-contrib/log/?h=marquiz/fixes-11209
Markus Lehtonen (5):
lib/oe/package_manager: re-implement rpm feed signing
dnf: rrecommend gnupg
oeqa: fix dnf tests
oeqa: fix temp file handling in dnf package feed test
dnf: expand dnf selftest to test signed package feeds
meta-selftest/files/signing/key.passphrase | 1 +
.../cases/{dnf-runtime.py => dnf_runtime.py} | 21 ++++++++++++++++++---
meta/lib/oe/package_manager.py | 14 +++++++++++++-
meta/lib/oeqa/selftest/cases/runtime_test.py | 15 +++++++++++++--
meta/recipes-devtools/dnf/dnf_2.5.1.bb | 2 ++
5 files changed, 47 insertions(+), 6 deletions(-)
create mode 100644 meta-selftest/files/signing/key.passphrase
rename meta-selftest/lib/oeqa/runtime/cases/{dnf-runtime.py => dnf_runtime.py} (64%)Signature · 1 line
2.12.3
PATCH 1/5 lib/oe/package_manager: re-implement rpm feed signing
[YOCTO #11209]
--- meta/lib/oe/package_manager.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
meta/lib/oe/package_manager.py +13 -1
@@ -104,13 +104,25 @@ class Indexer(object, metaclass=ABCMeta): class RpmIndexer(Indexer): def write_index(self): if self.d.getVar('PACKAGE_FEED_SIGN') == '1': - raise NotImplementedError('Package feed signing not yet implementd for rpm') + signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND')) + else: + signer = None createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c") result = create_index("%s --update -q %s" % (createrepo_c, self.deploy_dir)) if result: bb.fatal(result) + # Sign repomd + if signer: + sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE') + is_ascii_sig = (sig_type.upper() != "BIN") + signer.detach_sign(os.path.join(self.deploy_dir, 'repodata', 'repomd.xml'), + self.d.getVar('PACKAGE_FEED_GPG_NAME'), + self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'), + armor=is_ascii_sig) + + class OpkgIndexer(Indexer): def write_index(self): arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
Signature · 1 line
2.12.3
PATCH 2/5 dnf: rrecommend gnupg
In reply to Markus LehtonenThis makes it possible to enable 'repo_gpgcheck' in dnf.conf. That is, do GPG signature check on repository metadata. Without gnupg dnf fails with "error: Invalid crypto engine."
[YOCTO #11209]
--- meta/recipes-devtools/dnf/dnf_2.5.1.bb | 2 ++ 1 file changed, 2 insertions(+)
meta/recipes-devtools/dnf/dnf_2.5.1.bb +2 -0
@@ -27,6 +27,8 @@ EXTRA_OECMAKE = " -DWITH_MAN=0 -DPYTHON_INSTALL_DIR=${PYTHON_SITEPACKAGES_DIR} - BBCLASSEXTEND = "native nativesdk" RDEPENDS_${PN}_class-target += "python3-core python3-codecs python3-netclient python3-email python3-threading python3-distutils librepo python3-shell python3-subprocess libcomps libdnf python3-sqlite3 python3-compression python3-rpm python3-iniparse python3-json python3-importlib python3-curses python3-argparse python3-misc python3-gpg" +# Recommend gnupg so that GPG signature check on repository metadata is possible +RRECOMMENDS_${PN}_class-target += "gnupg" # Create a symlink called 'dnf' as 'make install' does not do it, but # .spec file in dnf source tree does (and then Fedora and dnf documentation
Signature · 1 line
2.12.3
PATCH 3/5 oeqa: fix dnf tests
In reply to Markus LehtonenRename one dnf runtime test that it will recognized as a python module and thus also found by the oe test loader. Also, fix value of TEST_SUITES in dnf selftest so that all test dependencies are satisfied and the runtime test may be successfully run from there.
---
meta-selftest/lib/oeqa/runtime/cases/{dnf-runtime.py => dnf_runtime.py} | 0
meta/lib/oeqa/selftest/cases/runtime_test.py | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename meta-selftest/lib/oeqa/runtime/cases/{dnf-runtime.py => dnf_runtime.py} (100%)meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py renamed from meta-selftest/lib/oeqa/runtime/cases/dnf-runtime.py +0 -0
meta/lib/oeqa/selftest/cases/runtime_test.py +1 -1
@@ -138,7 +138,7 @@ class TestImage(OESelftestTestCase): self.skipTest('core-image-full-cmdline not buildable for poky-tiny') features = 'INHERIT += "testimage"\n' - features += 'TEST_SUITES = "ping ssh dnf-runtime"\n' + features += 'TEST_SUITES = "ping ssh dnf_runtime dnf.DnfBasicTest.test_dnf_help"\n' # We don't yet know what the server ip and port will be - they will be patched # in at the start of the on-image test features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n'
Signature · 1 line
2.12.3
PATCH 4/5 oeqa: fix temp file handling in dnf package feed test
In reply to Markus LehtonenPrevent stale temp files and a possible (if unlikely) race in tempfile usage.
--- meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py +6 -3
@@ -6,6 +6,8 @@ class DnfSelftest(DnfTest): @classmethod def setUpClass(cls): + import tempfile + cls.temp_dir = tempfile.TemporaryDirectory(prefix="oeqa-remotefeeds-") cls.repo_server = HTTPService(os.path.join(cls.tc.td['WORKDIR'], 'oe-rootfs-repo'), cls.tc.target.server_ip) cls.repo_server.start()
@@ -13,6 +15,7 @@ class DnfSelftest(DnfTest): @classmethod def tearDownClass(cls): cls.repo_server.stop() + cls.temp_dir.cleanup() @OETestDepends(['dnf.DnfBasicTest.test_dnf_help']) def test_verify_package_feeds(self):
@@ -25,11 +28,11 @@ class DnfSelftest(DnfTest): """ # When we created an image, we had to supply fake ip and port # for the feeds. Now we can patch the real ones into the config file. - import tempfile - temp_file = tempfile.TemporaryDirectory(prefix="oeqa-remotefeeds-").name + temp_file = os.path.join(self.temp_dir.name, 'tmp.repo') self.tc.target.copyFrom("/etc/yum.repos.d/oe-remote-repo.repo", temp_file) fixed_config = open(temp_file, "r").read().replace("bogus_ip", self.tc.target.server_ip).replace("bogus_port", str(self.repo_server.port)) - open(temp_file, "w").write(fixed_config) + with open(temp_file, "w") as f: + f.write(fixed_config) self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") import re
Signature · 1 line
2.12.3
PATCH 5/5 dnf: expand dnf selftest to test signed package feeds
In reply to Markus Lehtonen[YOCTO #12099]
--- meta-selftest/files/signing/key.passphrase | 1 + meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py | 12 ++++++++++++ meta/lib/oeqa/selftest/cases/runtime_test.py | 13 ++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 meta-selftest/files/signing/key.passphrase
meta-selftest/files/signing/key.passphrase new file +1 -0
@@ -0,0 +1 @@ +test123
meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py +12 -0
@@ -31,12 +31,24 @@ class DnfSelftest(DnfTest): temp_file = os.path.join(self.temp_dir.name, 'tmp.repo') self.tc.target.copyFrom("/etc/yum.repos.d/oe-remote-repo.repo", temp_file) fixed_config = open(temp_file, "r").read().replace("bogus_ip", self.tc.target.server_ip).replace("bogus_port", str(self.repo_server.port)) + fixed_config += 'repo_gpgcheck=1\n' with open(temp_file, "w") as f: f.write(fixed_config) self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + # First try should fail as the gpg pubkey is not available for dnf import re output_makecache = self.dnf('makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is not None, msg = "dnf makecache should have failed: %s" %(output_makecache)) + + # Add public key to dnf config -> now we should succeed + fixed_config += 'gpgkey=file:///etc/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s\n' % self.tc.td['DISTRO_VERSION'] + with open(temp_file, "w") as f: + f.write(fixed_config) + self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + + output_makecache = self.dnf('-y makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is None, msg = "dnf makecache failed to synchronize repo: %s" %(output_makecache)) self.assertTrue(re.match(r".*Metadata cache created", output_makecache, re.DOTALL) is not None, msg = "dnf makecache failed: %s" %(output_makecache)) output_repoinfo = self.dnf('repoinfo')
meta/lib/oeqa/selftest/cases/runtime_test.py +12 -1
@@ -3,6 +3,7 @@ from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqem from oeqa.core.decorator.oeid import OETestID import os import re +import tempfile class TestExport(OESelftestTestCase):
@@ -143,7 +144,17 @@ class TestImage(OESelftestTestCase): # in at the start of the on-image test features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n' features += 'EXTRA_IMAGE_FEATURES += "package-management"\n' - features += 'PACKAGE_CLASSES = "package_rpm"' + features += 'PACKAGE_CLASSES = "package_rpm"\n' + + # Enable package feed signing + self.gpg_home = tempfile.TemporaryDirectory(prefix="oeqa-feed-sign-") + signing_key_dir = os.path.join(self.testlayer_path, 'files', 'signing') + runCmd('gpg --batch --homedir %s --import %s' % (self.gpg_home.name, os.path.join(signing_key_dir, 'key.secret'))) + features += 'INHERIT += "sign_package_feed"\n' + features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' + features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') + features += 'GPG_PATH = "%s"\n' % self.gpg_home.name + features += 'IMAGE_INSTALL_append = "signing-keys-packagefeed"\n' self.write_config(features) # Build core-image-sato and testimage
Signature · 1 line
2.12.3
Re: [PATCH 5/5] dnf: expand dnf selftest to test signed package feeds
Quoted 17 lines · Markus Lehtonen
meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py +5
-0@@ -31,12 +31,24 @@ class DnfSelftest(DnfTest): temp_file = os.path.join(self.temp_dir.name, 'tmp.repo') self.tc.target.copyFrom("/etc/yum.repos.d/oe-remote-repo.repo", temp_file) fixed_config = open(temp_file, "r").read().replace("bogus_ip", self.tc.target.server_ip).replace("bogus_port", str(self.repo_server.port)) + fixed_config += 'repo_gpgcheck=1\n' with open(temp_file, "w") as f: f.write(fixed_config) self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + # First try should fail as the gpg pubkey is not available for dnf import re output_makecache = self.dnf('makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is not None, msg = "dnf makecache should have failed: %s" %(output_makecache)) + + # Add public key to dnf config -> now we should succeed
meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py class DnfSelftest(DnfTest): · line 54
Quoted 10 lines
meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py +7
-0+ fixed_config += 'gpgkey=file:///etc/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s\n' % self.tc.td['DISTRO_VERSION'] + with open(temp_file, "w") as f: + f.write(fixed_config) + self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + + output_makecache = self.dnf('-y makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is None, msg = "dnf makecache failed to synchronize repo: %s" %(output_makecache)) self.assertTrue(re.match(r".*Metadata cache created", output_makecache, re.DOTALL) is not None, msg = "dnf makecache failed: %s" %(output_makecache)) output_repoinfo = self.dnf('repoinfo')
Wait a moment. If feed signing is enabled, then dnf's "repo_gpgcheck", and "gpgkey" settings should be configured and working by default. You shouldn't fix them after the fact in the test. Please add the necessary code to insert_feeds_uris() in package_manager.py.
Then you can simply test that:
a) repository access ('dnf makecache' and 'dnf repoinfo') works without error.
b) the signatures are indeed present in the feed configuration ('repoinfo' would probably print that)
Alex
Re: [PATCH 5/5] dnf: expand dnf selftest to test signed package feeds
On 11/08/2017, 14.11, "Alexander Kanavin" <[email protected]> wrote:
On 08/11/2017 01:51 PM, Markus Lehtonen wrote:
> --- a/meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py > +++ b/meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py > @@ -31,12 +31,24 @@ class DnfSelftest(DnfTest):
> temp_file = os.path.join(self.temp_dir.name, 'tmp.repo')
> self.tc.target.copyFrom("/etc/yum.repos.d/oe-remote-repo.repo", temp_file)
> fixed_config = open(temp_file, "r").read().replace("bogus_ip", self.tc.target.server_ip).replace("bogus_port", str(self.repo_server.port))
> + fixed_config += 'repo_gpgcheck=1\n'
> with open(temp_file, "w") as f:
> f.write(fixed_config)
> self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo")
>
> + # First try should fail as the gpg pubkey is not available for dnf
> import re
> output_makecache = self.dnf('makecache')
> + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is not None, msg = "dnf makecache should have failed: %s" %(output_makecache))
> +
> + # Add public key to dnf config -> now we should succeed
> + fixed_config += 'gpgkey=file:///etc/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s\n' % self.tc.td['DISTRO_VERSION']
> + with open(temp_file, "w") as f:
> + f.write(fixed_config)
> + self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo")
> +
> + output_makecache = self.dnf('-y makecache')
> + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is None, msg = "dnf makecache failed to synchronize repo: %s" %(output_makecache))
> self.assertTrue(re.match(r".*Metadata cache created", output_makecache, re.DOTALL) is not None, msg = "dnf makecache failed: %s" %(output_makecache))
>
> output_repoinfo = self.dnf('repoinfo')
Wait a moment. If feed signing is enabled, then dnf's "repo_gpgcheck", and "gpgkey" settings should be configured and working by default. You shouldn't fix them after the fact in the test. Please add the necessary code to insert_feeds_uris() in package_manager.py.
Do you think it's a safe assumption that all repos configured via PACKAGE_FEED_URIS are signed and with the same key?
Thanks,
Markus
Re: [PATCH 5/5] dnf: expand dnf selftest to test signed package feeds
Quoted 7 lines · Markus Lehtonen
Wait a moment. If feed signing is enabled, then dnf's "
repo_gpgcheck", and "gpgkey" settings should be configured and working by default. You shouldn't fix them after the fact in the test. Please add the necessary code toinsert_feeds_uris()inpackage_manager.py.Do you think it's a safe assumption that all repos configured via
PACKAGE_FEED_URISare signed and with the same key?
No; you should insert those lines only if PACKAGE_FEED_SIGN is set to "1". Do you mean something else?
Alex
Re: [PATCH 5/5] dnf: expand dnf selftest to test signed package feeds
In reply to Markus LehtonenQuoted 6 lines · Markus Lehtonen
Wait a moment. If feed signing is enabled, then dnf's "
repo_gpgcheck", and "gpgkey" settings should be configured and working by default. You shouldn't fix them after the fact in the test. Please add the necessary code toinsert_feeds_uris()inpackage_manager.py.Do you think it's a safe assumption that all repos configured via
PACKAGE_FEED_URISare signed and with the same key?
We had a discussion on IRC; the problem here is that some of those repos may be from a 3rd party, or created earlier with different signing settings. We don't provide configuration support for such a mix of repositories; if PACKAGE_FEED_SIGN is enabled, then it is assumed that all of the configured repositories are signed with the provided key. If someone needs a more intricate configuration, they can have it via a custom repository indexer recipe, and image creation hooks that configure dnf to match that.
The alternative (not configuring dnf to check the signatures) is worse: the repos are signed, but then dnf does not actually verify anything. So the signing is quietly subverted. This default case should simply work, and not fail quietly.
Alex
Re: [PATCH 5/5] dnf: expand dnf selftest to test signed package feeds
In reply to Markus LehtonenQuoted 1 line · Markus Lehtonen
[YOCTO #12099]
seems that the bugzilla ID does not exit.
Quoted 79 lines
Signed-off-by Markus Lehtonen <[email protected]>--- meta-selftest/files/signing/key.passphrase | 1 + meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py | 12 ++++++++++++ meta/lib/oeqa/selftest/cases/runtime_test.py | 13 ++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 meta-selftest/files/signing/key.passphrasemeta-selftest/files/signing/key.passphrase new file +1
-0@@ -0,0 +1 @@ +test123meta-selftest/lib/oeqa/runtime/cases/dnf_runtime.py +12
-0@@ -31,12 +31,24 @@ class DnfSelftest(DnfTest): temp_file = os.path.join(self.temp_dir.name, 'tmp.repo') self.tc.target.copyFrom("/etc/yum.repos.d/oe-remote-repo.repo", temp_file) fixed_config = open(temp_file, "r").read().replace("bogus_ip", self.tc.target.server_ip).replace("bogus_port", str(self.repo_server.port)) + fixed_config += 'repo_gpgcheck=1\n' with open(temp_file, "w") as f: f.write(fixed_config) self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + # First try should fail as the gpg pubkey is not available for dnf import re output_makecache = self.dnf('makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is not None, msg = "dnf makecache should have failed: %s" %(output_makecache)) + + # Add public key to dnf config -> now we should succeed + fixed_config += 'gpgkey=file:///etc/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s\n' % self.tc.td['DISTRO_VERSION'] + with open(temp_file, "w") as f: + f.write(fixed_config) + self.tc.target.copyTo(temp_file, "/etc/yum.repos.d/oe-remote-repo.repo") + + output_makecache = self.dnf('-y makecache') + self.assertTrue(re.match(r".*Failed to synchronize cache", output_makecache, re.DOTALL) is None, msg = "dnf makecache failed to synchronize repo: %s" %(output_makecache)) self.assertTrue(re.match(r".*Metadata cache created", output_makecache, re.DOTALL) is not None, msg = "dnf makecache failed: %s" %(output_makecache)) output_repoinfo = self.dnf('repoinfo')meta/lib/oeqa/selftest/cases/runtime_test.py +12
-1@@ -3,6 +3,7 @@ from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqem from oeqa.core.decorator.oeid import OETestID import os import re +import tempfile class TestExport(OESelftestTestCase):@@ -143,7 +144,17 @@ class TestImage(OESelftestTestCase): # in at the start of the on-image test features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n' features += 'EXTRA_IMAGE_FEATURES += "package-management"\n' - features += 'PACKAGE_CLASSES = "package_rpm"' + features += 'PACKAGE_CLASSES = "package_rpm"\n' + + # Enable package feed signing + self.gpg_home = tempfile.TemporaryDirectory(prefix="oeqa-feed-sign-") + signing_key_dir = os.path.join(self.testlayer_path, 'files', 'signing') + runCmd('gpg --batch --homedir %s --import %s' % (self.gpg_home.name, os.path.join(signing_key_dir, 'key.secret'))) + features += 'INHERIT += "sign_package_feed"\n' + features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' + features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') + features += 'GPG_PATH = "%s"\n' % self.gpg_home.name + features += 'IMAGE_INSTALL_append = "signing-keys-packagefeed"\n' self.write_config(features) # Build core-image-sato and testimageSignature · 1 line
2.12.3