qvm-template: use key specified in the repo definition if possible
This makes the package verified against _only_ the key specified in the repo config, not all the trusted keys. If repo does not specify a key, use the default one (change this to a single file, instead of the whole directory). Existing 'gpgkey' entry pointing at non-existing file will result in an error.
This commit is contained in:
parent
4f9757ca88
commit
b7446afe3b
@ -152,7 +152,7 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
])
|
])
|
||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
@mock.patch('qubesadmin.tools.qvm_template.get_keys')
|
@mock.patch('qubesadmin.tools.qvm_template.get_keys_for_repos')
|
||||||
def test_090_install_lock(self, mock_get_keys):
|
def test_090_install_lock(self, mock_get_keys):
|
||||||
class SuccessError(Exception):
|
class SuccessError(Exception):
|
||||||
pass
|
pass
|
||||||
@ -251,6 +251,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=False,
|
allow_pv=False,
|
||||||
pool=None
|
pool=None
|
||||||
@ -357,6 +359,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=True,
|
allow_pv=True,
|
||||||
pool='my-pool'
|
pool='my-pool'
|
||||||
@ -432,6 +436,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=False,
|
allow_pv=False,
|
||||||
pool=None
|
pool=None
|
||||||
@ -496,6 +502,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=False,
|
allow_pv=False,
|
||||||
pool=None
|
pool=None
|
||||||
@ -563,6 +571,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=False,
|
allow_pv=False,
|
||||||
pool=None
|
pool=None
|
||||||
@ -611,6 +621,8 @@ class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
|
|||||||
keyring='/tmp',
|
keyring='/tmp',
|
||||||
nogpgcheck=False,
|
nogpgcheck=False,
|
||||||
cachedir='/var/cache/qvm-template',
|
cachedir='/var/cache/qvm-template',
|
||||||
|
repo_files=[],
|
||||||
|
releasever='4.1',
|
||||||
yes=False,
|
yes=False,
|
||||||
allow_pv=False,
|
allow_pv=False,
|
||||||
pool=None
|
pool=None
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
|
import configparser
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
import fcntl
|
import fcntl
|
||||||
@ -104,8 +105,10 @@ def get_parser() -> argparse.ArgumentParser:
|
|||||||
help=('Specify files containing DNF repository configuration.'
|
help=('Specify files containing DNF repository configuration.'
|
||||||
' Can be used more than once.'))
|
' Can be used more than once.'))
|
||||||
parser_main.add_argument('--keyring',
|
parser_main.add_argument('--keyring',
|
||||||
default='/etc/qubes/repo-templates/keys',
|
default='/etc/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-4.1-primary',
|
||||||
help='Specify directory containing RPM public keys.')
|
help='Specify a file containing default RPM public key. '
|
||||||
|
'Individual repositories may point at repo-specific key '
|
||||||
|
'using \'gpgkey\' option')
|
||||||
parser_main.add_argument('--updatevm', default=UPDATEVM,
|
parser_main.add_argument('--updatevm', default=UPDATEVM,
|
||||||
help=('Specify VM to download updates from.'
|
help=('Specify VM to download updates from.'
|
||||||
' (Set to empty string to specify the current VM.)'))
|
' (Set to empty string to specify the current VM.)'))
|
||||||
@ -573,18 +576,32 @@ def qrexec_download(
|
|||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
"qrexec call 'qubes.TemplateDownload' failed.")
|
"qrexec call 'qubes.TemplateDownload' failed.")
|
||||||
|
|
||||||
def get_keys(key_dir: str) -> typing.List[str]:
|
|
||||||
"""List gpg keys"""
|
def get_keys_for_repos(repo_files: typing.List[str],
|
||||||
keys = []
|
releasever: str) -> typing.Dict[str, str]:
|
||||||
for name in os.listdir(key_dir):
|
"""List gpg keys
|
||||||
path = os.path.join(key_dir, name)
|
|
||||||
if os.path.isfile(path):
|
Returns a dict reponame -> key path
|
||||||
keys.append(path)
|
"""
|
||||||
|
keys = {}
|
||||||
|
for repo_file in repo_files:
|
||||||
|
repo_config = configparser.ConfigParser()
|
||||||
|
repo_config.read(repo_file)
|
||||||
|
for repo in repo_config.sections():
|
||||||
|
try:
|
||||||
|
gpgkey_url = repo_config.get(repo, 'gpgkey')
|
||||||
|
except configparser.NoOptionError:
|
||||||
|
continue
|
||||||
|
gpgkey_url = gpgkey_url.replace('$releasever', releasever)
|
||||||
|
# support only file:// urls
|
||||||
|
if gpgkey_url.startswith('file://'):
|
||||||
|
keys[repo] = gpgkey_url[len('file://'):]
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
|
|
||||||
def verify_rpm(
|
def verify_rpm(
|
||||||
path: str,
|
path: str,
|
||||||
keys: typing.List[str],
|
key: str,
|
||||||
nogpgcheck: bool = False
|
nogpgcheck: bool = False
|
||||||
) -> rpm.hdr:
|
) -> rpm.hdr:
|
||||||
"""Verify the digest and signature of a RPM package and return the package
|
"""Verify the digest and signature of a RPM package and return the package
|
||||||
@ -602,7 +619,6 @@ def verify_rpm(
|
|||||||
"""
|
"""
|
||||||
if not nogpgcheck:
|
if not nogpgcheck:
|
||||||
with tempfile.TemporaryDirectory() as rpmdb_dir:
|
with tempfile.TemporaryDirectory() as rpmdb_dir:
|
||||||
for key in keys:
|
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
['rpmkeys', '--dbpath=' + rpmdb_dir, '--import', key])
|
['rpmkeys', '--dbpath=' + rpmdb_dir, '--import', key])
|
||||||
try:
|
try:
|
||||||
@ -834,7 +850,7 @@ def install(
|
|||||||
:param override_existing: Whether to override existing packages. Used for
|
:param override_existing: Whether to override existing packages. Used for
|
||||||
reinstall, upgrade, and downgrade operations
|
reinstall, upgrade, and downgrade operations
|
||||||
"""
|
"""
|
||||||
keys = get_keys(args.keyring)
|
keys = get_keys_for_repos(args.repo_files, args.releasever)
|
||||||
|
|
||||||
unverified_rpm_list = [] # rpmfile, reponame
|
unverified_rpm_list = [] # rpmfile, reponame
|
||||||
verified_rpm_list = []
|
verified_rpm_list = []
|
||||||
@ -847,7 +863,10 @@ def install(
|
|||||||
else:
|
else:
|
||||||
path = rpmfile
|
path = rpmfile
|
||||||
|
|
||||||
package_hdr = verify_rpm(path, keys, args.nogpgcheck)
|
repo_key = keys.get(reponame)
|
||||||
|
if repo_key is None:
|
||||||
|
repo_key = args.keyring
|
||||||
|
package_hdr = verify_rpm(path, repo_key, args.nogpgcheck)
|
||||||
if not package_hdr:
|
if not package_hdr:
|
||||||
parser.error('Package \'%s\' verification failed.' % rpmfile)
|
parser.error('Package \'%s\' verification failed.' % rpmfile)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user