From 26995dbe9c869b0940cb60d6b2aed0279dab4e81 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 24 May 2017 13:11:15 +0200 Subject: [PATCH 1/5] Add --signing-key option This commit adds --signing-key option which sets a key file to import into the package manager trusted keys database. This commit adds this flag support only for zypper. Fixes #342 --- doc/source/commands/system_build.rst | 9 +++++++++ doc/source/commands/system_prepare.rst | 9 +++++++++ kiwi/repository/base.py | 10 ++++++++++ kiwi/repository/zypper.py | 9 +++++++++ kiwi/system/prepare.py | 4 +++- kiwi/tasks/system_build.py | 6 +++++- kiwi/tasks/system_prepare.py | 6 +++++- test/unit/cli_test.py | 1 + test/unit/repository_base_test.py | 4 ++++ test/unit/repository_zypper_test.py | 8 ++++++++ test/unit/system_prepare_test.py | 8 +++++++- test/unit/tasks_system_build_test.py | 7 ++++--- test/unit/tasks_system_prepare_test.py | 7 ++++--- 13 files changed, 78 insertions(+), 10 deletions(-) diff --git a/doc/source/commands/system_build.rst b/doc/source/commands/system_build.rst index ec099141..f5d92f9e 100644 --- a/doc/source/commands/system_build.rst +++ b/doc/source/commands/system_build.rst @@ -17,6 +17,7 @@ SYNOPSIS [--obs-repo-internal] [--add-package=...] [--delete-package=...] + [--signing-key=...] kiwi system build help DESCRIPTION @@ -53,6 +54,14 @@ OPTIONS is shared between multiple image builds on that host for performance reasons. +--signing-key= + + set the key file to be trusted and imported into the package + manager database before performing any opertaion. This is useful + if an image build should take and validate repository and package + signatures during build time. This option can be specified multiple + times + --delete-package= specify package to delete. The option can be specified diff --git a/doc/source/commands/system_prepare.rst b/doc/source/commands/system_prepare.rst index 5c7ffe25..92bde9ed 100644 --- a/doc/source/commands/system_prepare.rst +++ b/doc/source/commands/system_prepare.rst @@ -18,6 +18,7 @@ SYNOPSIS [--obs-repo-internal] [--add-package=...] [--delete-package=...] + [--signing-key=...] kiwi system prepare help DESCRIPTION @@ -59,6 +60,14 @@ OPTIONS is shared between multiple image builds on that host for performance reasons. +--signing-key= + + set the key file to be trusted and imported into the package + manager database before performing any opertaion. This is useful + if an image build should take and validate repository and package + signatures during build time. This option can be specified multiple + times + --delete-package= specify package to delete. The option can be specified diff --git a/kiwi/repository/base.py b/kiwi/repository/base.py index 9213187e..725667dc 100644 --- a/kiwi/repository/base.py +++ b/kiwi/repository/base.py @@ -86,6 +86,16 @@ class RepositoryBase(object): """ raise NotImplementedError + def import_trusted_keys(self, signing_keys): + """ + Imports trusted keys into the image + + Implementation in specialized repository class + + :param list signing_keys: list of the key files to import + """ + raise NotImplementedError + def cleanup_unused_repos(self): """ Cleanup/Delete unused repositories diff --git a/kiwi/repository/zypper.py b/kiwi/repository/zypper.py index 96286888..70580b95 100644 --- a/kiwi/repository/zypper.py +++ b/kiwi/repository/zypper.py @@ -243,6 +243,15 @@ class RepositoryZypper(RepositoryBase): ) self._restore_package_cache() + def import_trusted_keys(self, signing_keys): + """ + Imports trusted keys into the image + + :param list signing_keys: list of the key files to import + """ + for key in signing_keys: + Command.run(['rpm', '--root', self.root_dir, '--import', key]) + def delete_repo(self, name): """ Delete zypper repository diff --git a/kiwi/system/prepare.py b/kiwi/system/prepare.py index a547068d..d077437f 100644 --- a/kiwi/system/prepare.py +++ b/kiwi/system/prepare.py @@ -90,7 +90,7 @@ class SystemPrepare(object): # for System operations self.uri_list = [] - def setup_repositories(self, clear_cache=False): + def setup_repositories(self, clear_cache=False, signing_keys=None): """ Set up repositories for software installation and return a package manager for performing software installation tasks @@ -108,6 +108,8 @@ class SystemPrepare(object): repo = Repository( self.root_bind, package_manager, repository_options ) + if signing_keys: + repo.import_trusted_keys(signing_keys) for xml_repo in repository_sections: repo_type = xml_repo.get_type() repo_source = xml_repo.get_source().get_path() diff --git a/kiwi/tasks/system_build.py b/kiwi/tasks/system_build.py index c42da616..db955d18 100644 --- a/kiwi/tasks/system_build.py +++ b/kiwi/tasks/system_build.py @@ -27,6 +27,7 @@ usage: kiwi system build -h | --help [--delete-package=...] [--set-container-derived-from=] [--set-container-tag=] + [--signing-key=...] kiwi system build help commands: @@ -69,6 +70,8 @@ options: repository in the XML description --target-dir= the target directory to store the system image file(s) + --signing-key= + includes the key-file as a trusted key for package manager validations """ import os @@ -181,7 +184,8 @@ class SystemBuildTask(CliTask): self.xml_state, image_root, True ) manager = system.setup_repositories( - self.command_args['--clear-cache'] + self.command_args['--clear-cache'], + self.command_args['--signing-key'] ) system.install_bootstrap(manager) system.install_system( diff --git a/kiwi/tasks/system_prepare.py b/kiwi/tasks/system_prepare.py index cc02bc99..e623f39f 100644 --- a/kiwi/tasks/system_prepare.py +++ b/kiwi/tasks/system_prepare.py @@ -28,6 +28,7 @@ usage: kiwi system prepare -h | --help [--delete-package=...] [--set-container-derived-from=] [--set-container-tag=] + [--signing-key=...] kiwi system prepare help commands: @@ -73,6 +74,8 @@ options: --set-repo= overwrite the repo source, type, alias or priority for the first repository in the XML description + --signing-key= + includes the key-file as a trusted key for package manager validations """ import os @@ -174,7 +177,8 @@ class SystemPrepareTask(CliTask): self.command_args['--allow-existing-root'] ) manager = system.setup_repositories( - self.command_args['--clear-cache'] + self.command_args['--clear-cache'], + self.command_args['--signing-key'] ) system.install_bootstrap(manager) system.install_system( diff --git a/test/unit/cli_test.py b/test/unit/cli_test.py index 74f2ef6a..ed2711eb 100644 --- a/test/unit/cli_test.py +++ b/test/unit/cli_test.py @@ -46,6 +46,7 @@ class TestCli(object): '--delete-package': [], '--set-container-derived-from': None, '--set-container-tag': None, + '--signing-key': [], '-h': False, 'help': False, 'prepare': True, diff --git a/test/unit/repository_base_test.py b/test/unit/repository_base_test.py index 27b22b7d..5a9e4b45 100644 --- a/test/unit/repository_base_test.py +++ b/test/unit/repository_base_test.py @@ -27,6 +27,10 @@ class TestRepositoryBase(object): 'user', 'secret', 'credentials-file' ) + @raises(NotImplementedError) + def test_import_trusted_keys(self): + self.repo.import_trusted_keys(['key-file.asc']) + @raises(NotImplementedError) def test_delete_repo(self): self.repo.delete_repo('name') diff --git a/test/unit/repository_zypper_test.py b/test/unit/repository_zypper_test.py index f131f869..c5a2f127 100644 --- a/test/unit/repository_zypper_test.py +++ b/test/unit/repository_zypper_test.py @@ -126,6 +126,14 @@ class TestRepositoryZypper(object): ]) ] + @patch('kiwi.command.Command.run') + def test_import_trusted_keys(self, mock_run): + self.repo.import_trusted_keys(['key-file-a.asc', 'key-file-b.asc']) + assert mock_run.call_args_list == [ + call(['rpm', '--root', '../data', '--import', 'key-file-a.asc']), + call(['rpm', '--root', '../data', '--import', 'key-file-b.asc']) + ] + @patch('kiwi.command.Command.run') @patch('kiwi.repository.zypper.Path.wipe') @patch('os.path.exists') diff --git a/test/unit/system_prepare_test.py b/test/unit/system_prepare_test.py index 17d449b9..9ca3876d 100644 --- a/test/unit/system_prepare_test.py +++ b/test/unit/system_prepare_test.py @@ -181,7 +181,10 @@ class TestSystemPrepare(object): repo = mock.Mock() mock_repo.return_value = repo - self.system.setup_repositories(clear_cache=True) + self.system.setup_repositories( + clear_cache=True, + signing_keys=['key-file-a.asc', 'key-file-b.asc'] + ) mock_repo.assert_called_once_with( self.system.root_bind, 'package-manager-name', @@ -211,6 +214,9 @@ class TestSystemPrepare(object): call('uri-alias'), call('uri-alias') ] + repo.import_trusted_keys.assert_called_once_with( + ['key-file-a.asc', 'key-file-b.asc'] + ) @patch('kiwi.system.prepare.Repository') @patch('kiwi.system.prepare.Uri') diff --git a/test/unit/tasks_system_build_test.py b/test/unit/tasks_system_build_test.py index bbfe1727..bdab2f5b 100644 --- a/test/unit/tasks_system_build_test.py +++ b/test/unit/tasks_system_build_test.py @@ -81,6 +81,7 @@ class TestSystemBuildTask(object): self.task.command_args['--set-container-derived-from'] = None self.task.command_args['--set-container-tag'] = None self.task.command_args['--clear-cache'] = False + self.task.command_args['--signing-key'] = None @patch('kiwi.logger.Logger.set_logfile') def test_process_system_build(self, mock_log): @@ -91,7 +92,7 @@ class TestSystemBuildTask(object): self.runtime_checker.check_image_include_repos_http_resolvable.assert_called_once_with() self.runtime_checker.check_target_directory_not_in_shared_cache.assert_called_once_with(self.abs_target_dir) self.runtime_checker.check_repositories_configured.assert_called_once_with() - self.system_prepare.setup_repositories.assert_called_once_with(False) + self.system_prepare.setup_repositories.assert_called_once_with(False, None) self.system_prepare.install_bootstrap.assert_called_once_with( self.manager ) @@ -126,7 +127,7 @@ class TestSystemBuildTask(object): self._init_command_args() self.task.command_args['--add-package'] = ['vim'] self.task.process() - self.system_prepare.setup_repositories.assert_called_once_with(False) + self.system_prepare.setup_repositories.assert_called_once_with(False, None) self.system_prepare.install_packages.assert_called_once_with( self.manager, ['vim'] ) @@ -136,7 +137,7 @@ class TestSystemBuildTask(object): self._init_command_args() self.task.command_args['--delete-package'] = ['vim'] self.task.process() - self.system_prepare.setup_repositories.assert_called_once_with(False) + self.system_prepare.setup_repositories.assert_called_once_with(False, None) self.system_prepare.delete_packages.assert_called_once_with( self.manager, ['vim'] ) diff --git a/test/unit/tasks_system_prepare_test.py b/test/unit/tasks_system_prepare_test.py index b9b32d5b..d7cc7a69 100644 --- a/test/unit/tasks_system_prepare_test.py +++ b/test/unit/tasks_system_prepare_test.py @@ -72,6 +72,7 @@ class TestSystemPrepareTask(object): self.task.command_args['--clear-cache'] = False self.task.command_args['--set-container-derived-from'] = None self.task.command_args['--set-container-tag'] = None + self.task.command_args['--signing-key'] = None def test_process_system_prepare(self): self._init_command_args() @@ -84,7 +85,7 @@ class TestSystemPrepareTask(object): self.abs_root_dir ) self.runtime_checker.check_repositories_configured.assert_called_once_with() - self.system_prepare.setup_repositories.assert_called_once_with(True) + self.system_prepare.setup_repositories.assert_called_once_with(True, None) self.system_prepare.install_bootstrap.assert_called_once_with( self.manager ) @@ -113,7 +114,7 @@ class TestSystemPrepareTask(object): self._init_command_args() self.task.command_args['--add-package'] = ['vim'] self.task.process() - self.system_prepare.setup_repositories.assert_called_once_with(False) + self.system_prepare.setup_repositories.assert_called_once_with(False, None) self.system_prepare.install_packages.assert_called_once_with( self.manager, ['vim'] ) @@ -122,7 +123,7 @@ class TestSystemPrepareTask(object): self._init_command_args() self.task.command_args['--delete-package'] = ['vim'] self.task.process() - self.system_prepare.setup_repositories.assert_called_once_with(False) + self.system_prepare.setup_repositories.assert_called_once_with(False, None) self.system_prepare.delete_packages.assert_called_once_with( self.manager, ['vim'] ) From 844640d93a808864459d830d1bc7f8060767710c Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 24 May 2017 13:15:19 +0200 Subject: [PATCH 2/5] Extend --signing-key option to Yum and Dnf This commit extends the --signing-key options support to Yum and Dnf package managers. In addition, signature check for repositories had to be disabled for Yum and Dnf, as kiwi unrelated issues were found while testing. Nevertheless, package signature checks are fully functional. Related to #342 --- kiwi/repository/dnf.py | 16 +++++++++++++--- kiwi/repository/yum.py | 16 +++++++++++++--- test/unit/repository_dnf_test.py | 10 ++++++++-- test/unit/repository_yum_test.py | 10 ++++++++-- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/kiwi/repository/dnf.py b/kiwi/repository/dnf.py index f851094a..b6908c4f 100644 --- a/kiwi/repository/dnf.py +++ b/kiwi/repository/dnf.py @@ -23,6 +23,7 @@ from tempfile import NamedTemporaryFile # project from kiwi.repository.base import RepositoryBase from kiwi.path import Path +from kiwi.command import Command class RepositoryDnf(RepositoryBase): @@ -160,6 +161,15 @@ class RepositoryDnf(RepositoryBase): with open(repo_file, 'w') as repo: repo_config.write(repo) + def import_trusted_keys(self, signing_keys): + """ + Imports trusted keys into the image + + :param list signing_keys: list of the key files to import + """ + for key in signing_keys: + Command.run(['rpm', '--root', self.root_dir, '--import', key]) + def delete_repo(self, name): """ Delete dnf repository @@ -253,9 +263,9 @@ class RepositoryDnf(RepositoryBase): self.runtime_dnf_config.set( 'main', 'gpgcheck', self.gpg_check ) - self.runtime_dnf_config.set( - 'main', 'repo_gpgcheck', self.gpg_check - ) + # We are not setting repo_gpgcheck, which forces repository + # signatures checks, because current Fedora 25 releases do not + # provide signed repositories, only signed packages if self.exclude_docs: self.runtime_dnf_config.set( 'main', 'tsflags', 'nodocs' diff --git a/kiwi/repository/yum.py b/kiwi/repository/yum.py index 8f3194f7..fe30b4b3 100644 --- a/kiwi/repository/yum.py +++ b/kiwi/repository/yum.py @@ -21,6 +21,7 @@ from tempfile import NamedTemporaryFile # project from kiwi.logger import log +from kiwi.command import Command from kiwi.repository.base import RepositoryBase from kiwi.path import Path @@ -159,6 +160,15 @@ class RepositoryYum(RepositoryBase): with open(repo_file, 'w') as repo: repo_config.write(repo) + def import_trusted_keys(self, signing_keys): + """ + Imports trusted keys into the image + + :param list signing_keys: list of the key files to import + """ + for key in signing_keys: + Command.run(['rpm', '--root', self.root_dir, '--import', key]) + def delete_repo(self, name): """ Delete yum repository @@ -248,9 +258,9 @@ class RepositoryYum(RepositoryBase): self.runtime_yum_config.set( 'main', 'gpgcheck', self.gpg_check ) - self.runtime_yum_config.set( - 'main', 'repo_gpgcheck', self.gpg_check - ) + # We are not setting repo_gpgcheck, which forces repository + # signature checks, because I could not manage to make it work. + # Nevertheless, package signature validation is functional. self.runtime_yum_config.set( 'main', 'metadata_expire', '1800' ) diff --git a/test/unit/repository_dnf_test.py b/test/unit/repository_dnf_test.py index b828b103..800d41ad 100644 --- a/test/unit/repository_dnf_test.py +++ b/test/unit/repository_dnf_test.py @@ -41,7 +41,6 @@ class TestRepositoryDnf(object): call('main', 'obsoletes', '1'), call('main', 'plugins', '1'), call('main', 'gpgcheck', '0'), - call('main', 'repo_gpgcheck', '0'), call('main', 'tsflags', 'nodocs'), call('main', 'enabled', '1') ] @@ -81,7 +80,6 @@ class TestRepositoryDnf(object): call('main', 'obsoletes', '1'), call('main', 'plugins', '1'), call('main', 'gpgcheck', '0'), - call('main', 'repo_gpgcheck', '0'), call('main', 'tsflags', 'nodocs'), call('main', 'enabled', '1') ] @@ -112,6 +110,14 @@ class TestRepositoryDnf(object): '/shared-dir/dnf/repos/foo.repo', 'w' ) + @patch('kiwi.command.Command.run') + def test_import_trusted_keys(self, mock_run): + self.repo.import_trusted_keys(['key-file-a.asc', 'key-file-b.asc']) + assert mock_run.call_args_list == [ + call(['rpm', '--root', '../data', '--import', 'key-file-a.asc']), + call(['rpm', '--root', '../data', '--import', 'key-file-b.asc']) + ] + @patch('kiwi.path.Path.wipe') def test_delete_repo(self, mock_wipe): self.repo.delete_repo('foo') diff --git a/test/unit/repository_yum_test.py b/test/unit/repository_yum_test.py index ff7e3819..073f7a42 100644 --- a/test/unit/repository_yum_test.py +++ b/test/unit/repository_yum_test.py @@ -40,7 +40,6 @@ class TestRepositoryYum(object): call('main', 'obsoletes', '1'), call('main', 'plugins', '1'), call('main', 'gpgcheck', '0'), - call('main', 'repo_gpgcheck', '0'), call('main', 'metadata_expire', '1800'), call('main', 'group_command', 'compat'), call('main', 'enabled', '1') @@ -84,7 +83,6 @@ class TestRepositoryYum(object): call('main', 'obsoletes', '1'), call('main', 'plugins', '1'), call('main', 'gpgcheck', '0'), - call('main', 'repo_gpgcheck', '0'), call('main', 'metadata_expire', '1800'), call('main', 'group_command', 'compat'), call('main', 'enabled', '1') @@ -116,6 +114,14 @@ class TestRepositoryYum(object): '/shared-dir/yum/repos/foo.repo', 'w' ) + @patch('kiwi.command.Command.run') + def test_import_trusted_keys(self, mock_run): + self.repo.import_trusted_keys(['key-file-a.asc', 'key-file-b.asc']) + assert mock_run.call_args_list == [ + call(['rpm', '--root', '../data', '--import', 'key-file-a.asc']), + call(['rpm', '--root', '../data', '--import', 'key-file-b.asc']) + ] + @patch('kiwi.path.Path.wipe') def test_delete_repo(self, mock_wipe): self.repo.delete_repo('foo') From 435ab2651a922efe6012036d0202c51db5a627b7 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 24 May 2017 13:18:43 +0200 Subject: [PATCH 3/5] Extend --signing-key to Apt package manager This commit extends support for --siging-key to the Apt package manager. However it has only been included for the chrooted operations, as current implementation of the bootstrap procedure does not provide signature check capabilities. Related to #342 --- kiwi/package_manager/apt.py | 11 +++++++++++ kiwi/repository/apt.py | 11 +++++++++++ test/unit/package_manager_apt_test.py | 9 ++++++++- test/unit/repository_apt_test.py | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/kiwi/package_manager/apt.py b/kiwi/package_manager/apt.py index 17afa1a8..1e87ab58 100644 --- a/kiwi/package_manager/apt.py +++ b/kiwi/package_manager/apt.py @@ -132,6 +132,13 @@ class PackageManagerApt(PackageManagerBase): device='/dev', mountpoint=self.root_dir + '/dev' ) dev_mount.umount() + if self.repository.unauthenticated == 'false': + log.warning( + 'KIWI does not support signature checks for apt-get ' + 'package manager during the bootstrap procedure, any ' + 'provided key will only be used inside the chroot ' + 'environment' + ) Command.run( [ 'debootstrap', '--no-check-gpg', self.distribution, @@ -144,6 +151,10 @@ class PackageManagerApt(PackageManagerBase): data.sync_data( options=['-a', '-H', '-X', '-A'] ) + for key in self.repository.signing_keys: + Command.run([ + 'chroot', self.root_dir, 'apt-key', 'add', key + ], self.command_env) except Exception as e: raise KiwiDebootstrapError( '%s: %s' % (type(e).__name__, format(e)) diff --git a/kiwi/repository/apt.py b/kiwi/repository/apt.py index 9853853d..f840ec3d 100644 --- a/kiwi/repository/apt.py +++ b/kiwi/repository/apt.py @@ -56,6 +56,7 @@ class RepositoryApt(RepositoryBase): """ self.custom_args = custom_args self.exclude_docs = False + self.signing_keys = [] if not custom_args: self.custom_args = [] @@ -167,6 +168,16 @@ class RepositoryApt(RepositoryBase): 'deb %s %s %s\n' % (uri, dist, components) ) + def import_trusted_keys(self, signing_keys): + """ + Keeps trusted keys so that later on they can be immported into + the image by the PackageManager instance. + + :param list signing_keys: list of the key files to import + """ + for key in signing_keys: + self.signing_keys.append(key) + def delete_repo(self, name): """ Delete apt-get repository diff --git a/test/unit/package_manager_apt_test.py b/test/unit/package_manager_apt_test.py index 01119e1e..14e9b5a5 100644 --- a/test/unit/package_manager_apt_test.py +++ b/test/unit/package_manager_apt_test.py @@ -12,6 +12,8 @@ class TestPackageManagerApt(object): def setup(self): repository = mock.Mock() repository.root_dir = 'root-dir' + repository.signing_keys = ['key-file.asc'] + repository.unauthenticated = 'false' root_bind = mock.Mock() root_bind.move_to_root = mock.Mock( @@ -76,12 +78,13 @@ class TestPackageManagerApt(object): mock_exists.return_value = True self.manager.process_install_requests_bootstrap() + @patch('kiwi.logger.log.warning') @patch('kiwi.command.Command.call') @patch('kiwi.command.Command.run') @patch('os.path.exists') @patch('kiwi.package_manager.apt.DataSync') def test_process_install_requests_bootstrap( - self, mock_sync, mock_exists, mock_run, mock_call + self, mock_sync, mock_exists, mock_run, mock_call, mock_warn ): self.manager.request_package('apt-get') self.manager.request_package('vim') @@ -101,6 +104,9 @@ class TestPackageManagerApt(object): 'debootstrap', '--no-check-gpg', 'xenial', 'root-dir.debootstrap', 'xenial_path'], ['env']), + call([ + 'chroot', 'root-dir', 'apt-key', 'add', 'key-file.asc' + ], ['env']), call(['rm', '-r', '-f', 'root-dir.debootstrap']), call([ 'chroot', 'root-dir', 'apt-get', @@ -112,6 +118,7 @@ class TestPackageManagerApt(object): 'root-moved-arguments', 'install', 'vim'], ['env'] ) + assert mock_warn.called @patch('kiwi.command.Command.call') @patch('kiwi.command.Command.run') diff --git a/test/unit/repository_apt_test.py b/test/unit/repository_apt_test.py index 525cee7a..83e9bf11 100644 --- a/test/unit/repository_apt_test.py +++ b/test/unit/repository_apt_test.py @@ -128,6 +128,10 @@ class TestRepositoryApt(object): '/shared-dir/apt-get/sources.list.d/foo.list', 'w' ) + def test_import_trusted_keys(self): + self.repo.import_trusted_keys(['key-file-a.asc', 'key-file-b.asc']) + assert self.repo.signing_keys == ['key-file-a.asc', 'key-file-b.asc'] + @patch('kiwi.path.Path.wipe') def test_delete_repo(self, mock_wipe): self.repo.delete_repo('foo') From 617859b3a4c9df1b64e9c280b36b558c47042ebf Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 31 May 2017 10:04:06 +0200 Subject: [PATCH 4/5] Include signing-key feature for boot images This commit extends the behavior of --signing-key options in order to import the provided key file into the boot image, in addition to the regular image root tree. Related to #342 --- kiwi/boot/image/__init__.py | 8 +++++--- kiwi/boot/image/base.py | 5 ++++- kiwi/boot/image/builtin_kiwi.py | 2 +- kiwi/builder/__init__.py | 8 ++++---- kiwi/builder/disk.py | 16 ++++++++++++---- kiwi/builder/live.py | 8 ++++++-- kiwi/builder/pxe.py | 9 +++++++-- kiwi/tasks/system_build.py | 3 ++- kiwi/tasks/system_create.py | 6 +++++- test/unit/boot_image_builtin_kiwi_test.py | 4 +++- test/unit/boot_image_test.py | 6 +++--- test/unit/builder_disk_test.py | 3 ++- test/unit/builder_live_test.py | 3 ++- test/unit/builder_pxe_test.py | 3 ++- test/unit/builder_test.py | 6 +++--- test/unit/tasks_system_create_test.py | 1 + 16 files changed, 62 insertions(+), 29 deletions(-) diff --git a/kiwi/boot/image/__init__.py b/kiwi/boot/image/__init__.py index a6fae6f1..d98ae6cb 100644 --- a/kiwi/boot/image/__init__.py +++ b/kiwi/boot/image/__init__.py @@ -28,14 +28,16 @@ class BootImage(object): """ BootImge factory """ - def __new__(self, xml_state, target_dir, root_dir=None): + def __new__(self, xml_state, target_dir, root_dir=None, signing_keys=None): initrd_system = xml_state.build_type.get_initrd_system() if not initrd_system: initrd_system = 'kiwi' if initrd_system == 'kiwi': - return BootImageKiwi(xml_state, target_dir) + return BootImageKiwi( + xml_state, target_dir, signing_keys=signing_keys + ) elif initrd_system == 'dracut': - return BootImageDracut(xml_state, target_dir, root_dir) + return BootImageDracut(xml_state, target_dir) else: raise KiwiBootImageSetupError( 'Support for %s initrd system not implemented' % initrd_system diff --git a/kiwi/boot/image/base.py b/kiwi/boot/image/base.py index 824852f9..6384f0e4 100644 --- a/kiwi/boot/image/base.py +++ b/kiwi/boot/image/base.py @@ -56,7 +56,9 @@ class BootImageBase(object): Instance of XMLState of the boot image description """ - def __init__(self, xml_state, target_dir, root_dir=None): + def __init__( + self, xml_state, target_dir, root_dir=None, signing_keys=None + ): self.xml_state = xml_state self.target_dir = target_dir self.initrd_filename = None @@ -64,6 +66,7 @@ class BootImageBase(object): self.setup = None self.temp_directories = [] self.call_destructor = True + self.signing_keys = signing_keys self.boot_root_directory = root_dir if not self.boot_root_directory: diff --git a/kiwi/boot/image/builtin_kiwi.py b/kiwi/boot/image/builtin_kiwi.py index f4ce0a9b..9872f173 100644 --- a/kiwi/boot/image/builtin_kiwi.py +++ b/kiwi/boot/image/builtin_kiwi.py @@ -52,7 +52,7 @@ class BootImageKiwi(BootImageBase): root_dir=self.boot_root_directory, allow_existing=True ) - manager = system.setup_repositories() + manager = system.setup_repositories(signing_keys=self.signing_keys) system.install_bootstrap( manager ) diff --git a/kiwi/builder/__init__.py b/kiwi/builder/__init__.py index 9155893e..cb842e12 100644 --- a/kiwi/builder/__init__.py +++ b/kiwi/builder/__init__.py @@ -33,7 +33,7 @@ class ImageBuilder(object): """ image builder factory """ - def __new__(self, xml_state, target_dir, root_dir): + def __new__(self, xml_state, target_dir, root_dir, custom_args=None): requested_image_type = xml_state.get_build_type_name() if requested_image_type in Defaults.get_filesystem_image_types(): return FileSystemBuilder( @@ -41,15 +41,15 @@ class ImageBuilder(object): ) elif requested_image_type in Defaults.get_disk_image_types(): return DiskBuilder( - xml_state, target_dir, root_dir + xml_state, target_dir, root_dir, custom_args ) elif requested_image_type in Defaults.get_live_image_types(): return LiveImageBuilder( - xml_state, target_dir, root_dir + xml_state, target_dir, root_dir, custom_args ) elif requested_image_type in Defaults.get_network_image_types(): return PxeBuilder( - xml_state, target_dir, root_dir + xml_state, target_dir, root_dir, custom_args ) elif requested_image_type in Defaults.get_archive_image_types(): return ArchiveBuilder( diff --git a/kiwi/builder/disk.py b/kiwi/builder/disk.py index 052b78e7..93b2cd55 100644 --- a/kiwi/builder/disk.py +++ b/kiwi/builder/disk.py @@ -171,7 +171,7 @@ class DiskBuilder(object): * :attr:`result` Instance of Result """ - def __init__(self, xml_state, target_dir, root_dir): + def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': self.arch = 'ix86' @@ -206,8 +206,15 @@ class DiskBuilder(object): self.disk_setup = DiskSetup( xml_state, root_dir ) + self.custom_args = custom_args + + self.boot_signing_keys = None + if custom_args and 'signing_keys' in custom_args: + self.boot_signing_keys = custom_args['signing_keys'] + self.boot_image = BootImage( - xml_state, target_dir, root_dir + xml_state, target_dir, root_dir, + signing_keys=self.boot_signing_keys ) self.firmware = FirmWare( xml_state @@ -256,7 +263,7 @@ class DiskBuilder(object): * image="vmx" """ disk = DiskBuilder( - self.xml_state, self.target_dir, self.root_dir + self.xml_state, self.target_dir, self.root_dir, self.custom_args ) result = disk.create_disk() @@ -480,7 +487,8 @@ class DiskBuilder(object): self.xml_state.build_type.get_initrd_system() self.boot_image = BootImageKiwi( - self.xml_state, self.target_dir + self.xml_state, self.target_dir, + signing_keys=self.boot_signing_keys ) self.boot_image.prepare() diff --git a/kiwi/builder/live.py b/kiwi/builder/live.py index 13bcfe38..f55c3d2f 100644 --- a/kiwi/builder/live.py +++ b/kiwi/builder/live.py @@ -100,7 +100,7 @@ class LiveImageBuilder(object): * :attr:`result` Instance of Result """ - def __init__(self, xml_state, target_dir, root_dir): + def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.media_dir = None self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': @@ -122,8 +122,12 @@ class LiveImageBuilder(object): if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() + boot_signing_keys = None + if custom_args and 'signing_keys' in custom_args: + boot_signing_keys = custom_args['signing_keys'] + self.boot_image_task = BootImage( - xml_state, target_dir + xml_state, target_dir, signing_keys=boot_signing_keys ) self.firmware = FirmWare( xml_state diff --git a/kiwi/builder/pxe.py b/kiwi/builder/pxe.py index e4178e94..dd9d1eb7 100644 --- a/kiwi/builder/pxe.py +++ b/kiwi/builder/pxe.py @@ -73,7 +73,7 @@ class PxeBuilder(object): * :attr:`result` Instance of Result """ - def __init__(self, xml_state, target_dir, root_dir): + def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.target_dir = target_dir self.compressed = xml_state.build_type.get_compressed() self.machine = xml_state.get_build_type_machine_section() @@ -84,8 +84,13 @@ class PxeBuilder(object): self.system_setup = SystemSetup( xml_state=xml_state, root_dir=root_dir ) + + boot_signing_keys = None + if custom_args and 'signing_keys' in custom_args: + boot_signing_keys = custom_args['signing_keys'] + self.boot_image_task = BootImage( - xml_state, target_dir + xml_state, target_dir, signing_keys=boot_signing_keys ) self.image_name = ''.join( [ diff --git a/kiwi/tasks/system_build.py b/kiwi/tasks/system_build.py index db955d18..f1230b6c 100644 --- a/kiwi/tasks/system_build.py +++ b/kiwi/tasks/system_build.py @@ -243,7 +243,8 @@ class SystemBuildTask(CliTask): image_builder = ImageBuilder( self.xml_state, abs_target_dir_path, - image_root + image_root, + {'signing_keys': self.command_args['--signing-key']} ) result = image_builder.create() result.print_results() diff --git a/kiwi/tasks/system_create.py b/kiwi/tasks/system_create.py index 4646b228..9dd24925 100644 --- a/kiwi/tasks/system_create.py +++ b/kiwi/tasks/system_create.py @@ -18,6 +18,7 @@ """ usage: kiwi system create -h | --help kiwi system create --root= --target-dir= + [--signing-key=...] kiwi system create help commands: @@ -34,6 +35,8 @@ options: a former system prepare call --target-dir= the target directory to store the system image file(s) + --signing-key= + includes the key-file as a trusted key for package manager validations """ import os @@ -93,7 +96,8 @@ class SystemCreateTask(CliTask): image_builder = ImageBuilder( self.xml_state, abs_target_dir_path, - abs_root_path + abs_root_path, + custom_args={'signing_keys': self.command_args['--signing-key']} ) result = image_builder.create() result.print_results() diff --git a/test/unit/boot_image_builtin_kiwi_test.py b/test/unit/boot_image_builtin_kiwi_test.py index aa9e18c1..167b468e 100644 --- a/test/unit/boot_image_builtin_kiwi_test.py +++ b/test/unit/boot_image_builtin_kiwi_test.py @@ -50,7 +50,9 @@ class TestBootImageKiwi(object): def test_prepare(self, mock_boot_path): mock_boot_path.return_value = '../data' self.boot_image.prepare() - self.system_prepare.setup_repositories.assert_called_once_with() + self.system_prepare.setup_repositories.assert_called_once_with( + signing_keys=None + ) self.system_prepare.install_bootstrap.assert_called_once_with( self.manager ) diff --git a/test/unit/boot_image_test.py b/test/unit/boot_image_test.py index 8d87a152..5c4051e7 100644 --- a/test/unit/boot_image_test.py +++ b/test/unit/boot_image_test.py @@ -23,7 +23,7 @@ class TestBootImage(object): self.xml_state.build_type.get_initrd_system.return_value = None BootImage(self.xml_state, 'target_dir') mock_kiwi.assert_called_once_with( - self.xml_state, 'target_dir' + self.xml_state, 'target_dir', signing_keys=None ) @patch('kiwi.boot.image.BootImageKiwi') @@ -31,7 +31,7 @@ class TestBootImage(object): self.xml_state.build_type.get_initrd_system.return_value = 'kiwi' BootImage(self.xml_state, 'target_dir') mock_kiwi.assert_called_once_with( - self.xml_state, 'target_dir' + self.xml_state, 'target_dir', signing_keys=None ) @patch('kiwi.boot.image.BootImageDracut') @@ -39,5 +39,5 @@ class TestBootImage(object): self.xml_state.build_type.get_initrd_system.return_value = 'dracut' BootImage(self.xml_state, 'target_dir') mock_dracut.assert_called_once_with( - self.xml_state, 'target_dir', None + self.xml_state, 'target_dir' ) diff --git a/test/unit/builder_disk_test.py b/test/unit/builder_disk_test.py index de6ec578..277f87d2 100644 --- a/test/unit/builder_disk_test.py +++ b/test/unit/builder_disk_test.py @@ -163,7 +163,8 @@ class TestDiskBuilder(object): return_value=self.luks_root ) self.disk_builder = DiskBuilder( - XMLState(description.load()), 'target_dir', 'root_dir' + XMLState(description.load()), 'target_dir', 'root_dir', + custom_args={'signing_keys': ['key_file_a', 'key_file_b']} ) self.disk_builder.root_filesystem_is_overlay = False self.disk_builder.build_type_name = 'oem' diff --git a/test/unit/builder_live_test.py b/test/unit/builder_live_test.py index d399c907..42b0075b 100644 --- a/test/unit/builder_live_test.py +++ b/test/unit/builder_live_test.py @@ -67,7 +67,8 @@ class TestLiveImageBuilder(object): return_value='custom_cmdline' ) self.live_image = LiveImageBuilder( - self.xml_state, 'target_dir', 'root_dir' + self.xml_state, 'target_dir', 'root_dir', + custom_args={'signing_keys': ['key_file_a', 'key_file_b']} ) self.live_image.machine = mock.Mock() self.live_image.machine.get_domain = mock.Mock( diff --git a/test/unit/builder_pxe_test.py b/test/unit/builder_pxe_test.py index b210952c..aca05d9c 100644 --- a/test/unit/builder_pxe_test.py +++ b/test/unit/builder_pxe_test.py @@ -50,7 +50,8 @@ class TestPxeBuilder(object): return_value=self.kernel ) self.pxe = PxeBuilder( - self.xml_state, 'target_dir', 'root_dir' + self.xml_state, 'target_dir', 'root_dir', + custom_args={'signing_keys': ['key_file_a', 'key_file_b']} ) self.machine = mock.Mock() self.machine.get_domain = mock.Mock( diff --git a/test/unit/builder_test.py b/test/unit/builder_test.py index 6e947e80..e1f482b0 100644 --- a/test/unit/builder_test.py +++ b/test/unit/builder_test.py @@ -29,7 +29,7 @@ class TestImageBuilder(object): ) ImageBuilder(xml_state, 'target_dir', 'root_dir') mock_builder.assert_called_once_with( - xml_state, 'target_dir', 'root_dir' + xml_state, 'target_dir', 'root_dir', None ) @patch('kiwi.builder.LiveImageBuilder') @@ -40,7 +40,7 @@ class TestImageBuilder(object): ) ImageBuilder(xml_state, 'target_dir', 'root_dir') mock_builder.assert_called_once_with( - xml_state, 'target_dir', 'root_dir' + xml_state, 'target_dir', 'root_dir', None ) @patch('kiwi.builder.PxeBuilder') @@ -51,7 +51,7 @@ class TestImageBuilder(object): ) ImageBuilder(xml_state, 'target_dir', 'root_dir') mock_builder.assert_called_once_with( - xml_state, 'target_dir', 'root_dir' + xml_state, 'target_dir', 'root_dir', None ) @patch('kiwi.builder.ArchiveBuilder') diff --git a/test/unit/tasks_system_create_test.py b/test/unit/tasks_system_create_test.py index 105a4172..f084c104 100644 --- a/test/unit/tasks_system_create_test.py +++ b/test/unit/tasks_system_create_test.py @@ -57,6 +57,7 @@ class TestSystemCreateTask(object): self.task.command_args['create'] = False self.task.command_args['--root'] = '../data/root-dir' self.task.command_args['--target-dir'] = 'some-target' + self.task.command_args['--signing-key'] = ['some-key-file'] def test_process_system_create(self): self._init_command_args() From 376e9daa58ad7b2749186f82dd80d615fb9af2b5 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 31 May 2017 14:54:40 +0200 Subject: [PATCH 5/5] Some fine tune updates * Updated the docs for system_create command * Reverted dracut image initialization * Updated yum comment about repo_gpgcheck option * Updated variable name in disk builder * Typo correction --- doc/source/commands/system_create.rst | 10 ++++++++++ kiwi/boot/image/__init__.py | 2 +- kiwi/builder/disk.py | 8 ++++---- kiwi/repository/apt.py | 2 +- kiwi/repository/yum.py | 4 ++-- test/unit/boot_image_test.py | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/source/commands/system_create.rst b/doc/source/commands/system_create.rst index 6013e4d2..2a06e114 100644 --- a/doc/source/commands/system_create.rst +++ b/doc/source/commands/system_create.rst @@ -10,6 +10,7 @@ SYNOPSIS kiwi system create -h | --help kiwi system create --root= --target-dir= + [--signing-key=...] kiwi system create help DESCRIPTION @@ -33,3 +34,12 @@ OPTIONS --target-dir= Path to store the build results. + +--signing-key= + + set the key file to be trusted and imported into the package + manager database before performing any opertaion. This is useful + if an image build should take and validate repository and package + signatures during build time. In create step this option only + affects the boot image. This option can be specified multiple + times diff --git a/kiwi/boot/image/__init__.py b/kiwi/boot/image/__init__.py index d98ae6cb..22f257b6 100644 --- a/kiwi/boot/image/__init__.py +++ b/kiwi/boot/image/__init__.py @@ -37,7 +37,7 @@ class BootImage(object): xml_state, target_dir, signing_keys=signing_keys ) elif initrd_system == 'dracut': - return BootImageDracut(xml_state, target_dir) + return BootImageDracut(xml_state, target_dir, root_dir) else: raise KiwiBootImageSetupError( 'Support for %s initrd system not implemented' % initrd_system diff --git a/kiwi/builder/disk.py b/kiwi/builder/disk.py index 93b2cd55..8d2192e8 100644 --- a/kiwi/builder/disk.py +++ b/kiwi/builder/disk.py @@ -208,13 +208,13 @@ class DiskBuilder(object): ) self.custom_args = custom_args - self.boot_signing_keys = None + self.signing_keys = None if custom_args and 'signing_keys' in custom_args: - self.boot_signing_keys = custom_args['signing_keys'] + self.signing_keys = custom_args['signing_keys'] self.boot_image = BootImage( xml_state, target_dir, root_dir, - signing_keys=self.boot_signing_keys + signing_keys=self.signing_keys ) self.firmware = FirmWare( xml_state @@ -488,7 +488,7 @@ class DiskBuilder(object): self.boot_image = BootImageKiwi( self.xml_state, self.target_dir, - signing_keys=self.boot_signing_keys + signing_keys=self.signing_keys ) self.boot_image.prepare() diff --git a/kiwi/repository/apt.py b/kiwi/repository/apt.py index f840ec3d..4ec12d0e 100644 --- a/kiwi/repository/apt.py +++ b/kiwi/repository/apt.py @@ -170,7 +170,7 @@ class RepositoryApt(RepositoryBase): def import_trusted_keys(self, signing_keys): """ - Keeps trusted keys so that later on they can be immported into + Keeps trusted keys so that later on they can be imported into the image by the PackageManager instance. :param list signing_keys: list of the key files to import diff --git a/kiwi/repository/yum.py b/kiwi/repository/yum.py index fe30b4b3..fe435433 100644 --- a/kiwi/repository/yum.py +++ b/kiwi/repository/yum.py @@ -259,8 +259,8 @@ class RepositoryYum(RepositoryBase): 'main', 'gpgcheck', self.gpg_check ) # We are not setting repo_gpgcheck, which forces repository - # signature checks, because I could not manage to make it work. - # Nevertheless, package signature validation is functional. + # signature checks. Because, at the time of writing this comment, + # yum v3.4.3 was not capable to perform a repository key validation self.runtime_yum_config.set( 'main', 'metadata_expire', '1800' ) diff --git a/test/unit/boot_image_test.py b/test/unit/boot_image_test.py index 5c4051e7..55703b2b 100644 --- a/test/unit/boot_image_test.py +++ b/test/unit/boot_image_test.py @@ -37,7 +37,7 @@ class TestBootImage(object): @patch('kiwi.boot.image.BootImageDracut') def test_boot_image_task_dracut(self, mock_dracut): self.xml_state.build_type.get_initrd_system.return_value = 'dracut' - BootImage(self.xml_state, 'target_dir') + BootImage(self.xml_state, 'target_dir', 'root_dir') mock_dracut.assert_called_once_with( - self.xml_state, 'target_dir' + self.xml_state, 'target_dir', 'root_dir' )