diff --git a/elbepack/commands/init.py b/elbepack/commands/init.py index b7b6e092a..94aa4ab3b 100644 --- a/elbepack/commands/init.py +++ b/elbepack/commands/init.py @@ -211,8 +211,10 @@ def run_command(argv): shutil.copyfile(args[0], os.path.join(out_path, "source.xml")) - keys = [] + if xml.has('initvm/mirror/primary_key'): + key = xml.node('initvm/mirror/primary_key') + keys.append(key.et.text) for key in xml.all(".//initvm/mirror/url-list/url/raw-key"): keys.append(key.et.text) diff --git a/elbepack/debinstaller.py b/elbepack/debinstaller.py index 1544bed9d..7ed5b6b75 100644 --- a/elbepack/debinstaller.py +++ b/elbepack/debinstaller.py @@ -6,6 +6,7 @@ import sys import os import re +import tempfile from shutil import copyfile from urllib.request import urlopen @@ -14,7 +15,7 @@ from gpg.constants import PROTOCOL_OpenPGP from elbepack.filesystem import TmpdirFilesystem -from elbepack.egpg import OverallStatus, check_signature +from elbepack.egpg import OverallStatus, check_signature, unarmor_openpgp_keyring from elbepack.shellhelper import CommandError, system from elbepack.hashes import HashValidator, HashValidationFailed @@ -74,7 +75,7 @@ def __init__(self, base_url, fname, fname_list): m.group(1)) -def setup_apt_keyring(gpg_home, keyring_fname): +def setup_apt_keyring(gpg_home, keyring_fname, primary_key): ring_path = os.path.join(gpg_home, keyring_fname) if not os.path.isdir("/etc/apt/trusted.gpg.d"): print("/etc/apt/trusted.gpg.d doesn't exist") @@ -100,6 +101,15 @@ def setup_apt_keyring(gpg_home, keyring_fname): except CommandError: print(f'adding keyring "{key}" to keyring "{ring_path}" failed') + if primary_key: + with tempfile.NamedTemporaryFile(buffering = 0) as fp: + print(f"Import primary key: ") + print(primary_key) + fp.write(unarmor_openpgp_keyring(primary_key)) + try: + system(f'gpg {gpg_options} --import "{fp.name}"') + except: + print(f'adding primary key to keyring "{ring_path}" failed') def download(url, local_fname): try: @@ -144,11 +154,11 @@ def verify_release(tmp, base_url): sig.close() -def download_kinitrd(tmp, suite, mirror, skip_signature=False): +def download_kinitrd(tmp, suite, mirror, primary_key, skip_signature=False): base_url = f"{mirror.replace('LOCALMACHINE', 'localhost')}/dists/{suite}/" installer_path = "main/installer-amd64/current/images/" - setup_apt_keyring(tmp.fname('/'), 'pubring.gpg') + setup_apt_keyring(tmp.fname('/'), 'pubring.gpg', primary_key) # download release file download(base_url + "Release", tmp.fname('Release')) @@ -200,6 +210,16 @@ def get_primary_mirror(prj): return mirror +def get_primary_key(prj): + primary_key = '' + + if prj.has("mirror/primary_key"): + m = prj.node("mirror") + + key = m.text("primary_key") + primary_key = "\n".join(line.strip(" \t") for line in key.splitlines()[1:-1]) + + return primary_key def copy_kinitrd(prj, target_dir): @@ -217,7 +237,8 @@ def copy_kinitrd(prj, target_dir): os.path.join(target_dir, "initrd.gz")) else: mirror = get_primary_mirror(prj) - download_kinitrd(tmp, suite, mirror, prj.has("noauth")) + primary_key = get_primary_key(prj) + download_kinitrd(tmp, suite, mirror, primary_key, prj.has("noauth")) copyfile(tmp.fname("initrd.gz"), os.path.join(target_dir, "initrd.gz")) diff --git a/elbepack/elbeproject.py b/elbepack/elbeproject.py index e913b8e85..9aa23e87e 100644 --- a/elbepack/elbeproject.py +++ b/elbepack/elbeproject.py @@ -38,7 +38,8 @@ from elbepack.pbuilder import (pbuilder_write_config, pbuilder_write_repo_hook, pbuilder_write_cross_config, - pbuilder_write_apt_conf) + pbuilder_write_apt_conf, + pbuilder_get_debootstrap_key_path) from elbepack.repomanager import ProjectRepo from elbepack.config import cfg @@ -865,19 +866,24 @@ def create_pbuilder(self, cross, noccache, ccachesize): # Run pbuilder --create no_check_gpg = "" + keyring = "" + debootstrap_key_path = pbuilder_get_debootstrap_key_path(self.chrootpath, self.xml) if self.xml.prj.has('noauth'): no_check_gpg = "--debootstrapopts --no-check-gpg" + elif debootstrap_key_path: + keyring = f'--debootstrapopts --keyring="{debootstrap_key_path}"' + if cross: do('pbuilder --create ' f'--buildplace "{os.path.join(self.builddir, "pbuilder_cross")}" ' f'--configfile "{os.path.join(self.builddir, "cross_pbuilderrc")}" ' f'--aptconfdir "{os.path.join(self.builddir, "aptconfdir")}" ' - f'--debootstrapopts --include="git,gnupg" {no_check_gpg};') + f'--debootstrapopts --include="git,gnupg" {no_check_gpg} {keyring};') else: do('pbuilder --create ' f'--configfile "{os.path.join(self.builddir, "pbuilderrc")}" ' f'--aptconfdir "{os.path.join(self.builddir, "aptconfdir")}" ' - f'--debootstrapopts --include="git,gnupg" {no_check_gpg}') + f'--debootstrapopts --include="git,gnupg" {no_check_gpg} {keyring}') def sync_xml_to_disk(self): try: diff --git a/elbepack/elbexml.py b/elbepack/elbexml.py index bc86ff3ce..ffc009c1f 100644 --- a/elbepack/elbexml.py +++ b/elbepack/elbexml.py @@ -142,6 +142,21 @@ def get_primary_mirror(self, cdrompath, initvm=True, hostsysroot=False): return replace_localmachine(mirror, initvm) + def get_primary_key(self, cdrompath, hostsysroot=False): + key = '' + + if self.prj.has("mirror/cdrom") and cdrompath: + pass + elif self.prj.has("mirror/primary_host"): + m = self.prj.node("mirror") + + if hostsysroot and self.prj.has("mirror/host") and self.prj.has("mirror/host_key"): + key = m.text("host_key") + elif self.prj.has("mirror/primary_key"): + key = m.text("primary_key") + + return key + # XXX: maybe add cdrom path param ? def create_apt_sources_list(self, build_sources=False, initvm=True, hostsysroot=False): diff --git a/elbepack/pbuilder.py b/elbepack/pbuilder.py index 51ba26b5b..c48e5a50d 100644 --- a/elbepack/pbuilder.py +++ b/elbepack/pbuilder.py @@ -151,6 +151,38 @@ def pbuilder_write_repo_hook(builddir, xml, cross): f.write("apt-get update\n") +def get_debootstrap_key(xml): + key = '' + + if xml.prj.has("mirror/primary_host") and xml.prj.has("mirror/primary_key"): + m = xml.prj.node("mirror") + + if m.has("options"): + options = "[%s]" % ' '.join([opt.et.text.strip(' \t\n') + for opt + in m.all("options/option")]) + else: + options = "" + + if not "trusted=yes" in options: + key = "\n".join(line.strip(" \t") + for line + in m.text('primary_key').splitlines()[1:-1]) + + + return key + +def pbuilder_get_debootstrap_key_path(chrootpath, xml): + path = '' + + # If we have a primary key for use with debootstrap, BuildEnv.debootstrap + # will have added the key. We use the same key for the pbuilder + # debootstrap options. + if get_debootstrap_key(xml): + path = os.path.join(chrootpath, 'etc', 'apt', 'trusted.gpg.d', 'elbe-xml-primary-key.gpg') + + return path + def get_apt_keys(builddir, xml): if xml.prj is None: @@ -161,6 +193,10 @@ def get_apt_keys(builddir, xml): keys = [Filesystem(builddir).read_file("repo/repo.pub")] + debootstrap_key = get_debootstrap_key(xml) + if debootstrap_key: + keys.append(debootstrap_key) + if xml.prj.has("mirror/primary_host") and xml.prj.has("mirror/url-list"): for url in xml.prj.node("mirror/url-list"): diff --git a/elbepack/rfs.py b/elbepack/rfs.py index 1f5c8fb0a..544ebef06 100644 --- a/elbepack/rfs.py +++ b/elbepack/rfs.py @@ -144,6 +144,16 @@ def __exit__(self, typ, value, traceback): do(f"rm {self.path}/etc/apt/sources.list.d/local.list") do(f"rm {self.path}/etc/apt/trusted.gpg.d/elbe-localrepo.gpg") + def import_debootstrap_key(self, key): + path = '' + + if key: + self.rfs.mkdir_p('etc/apt/trusted.gpg.d') + k = "\n".join(line.strip(" \t") for line in key.splitlines()[1:-1]) + path = self.add_key(unarmor_openpgp_keyring(k), "elbe-xml-primary-key.gpg") + + return path + def debootstrap(self, arch="default"): # pylint: disable=too-many-statements @@ -154,6 +164,10 @@ def debootstrap(self, arch="default"): primary_mirror = self.xml.get_primary_mirror( self.rfs.fname('/cdrom/targetrepo'), hostsysroot=self.hostsysroot) + primary_key = self.xml.get_primary_key( + self.rfs.fname('/cdrom/targetrepo'), hostsysroot=self.hostsysroot) + + debootstrap_key_path = self.import_debootstrap_key(primary_key) if self.xml.prj.has("mirror/primary_proxy"): os.environ["no_proxy"] = "10.0.2.2,localhost,127.0.0.1" @@ -202,13 +216,15 @@ def debootstrap(self, arch="default"): else: if self.xml.has("project/mirror/cdrom"): keyring = f' --keyring="{self.rfs.fname("/elbe.keyring")}"' + elif debootstrap_key_path: + keyring = f' --keyring="{debootstrap_key_path}"' cmd = (f'{strapcmd} --arch={arch} ' f'{keyring} "{suite}" "{self.rfs.path}" "{primary_mirror}"') try: self.cdrom_mount() - if keyring: + if keyring and self.xml.has("project/mirror/cdrom"): self.convert_asc_to_gpg("/cdrom/targetrepo/repo.pub", "/elbe.keyring") do(cmd) except CommandError: @@ -227,13 +243,15 @@ def debootstrap(self, arch="default"): else: if self.xml.has("project/mirror/cdrom"): keyring = f' --keyring="{self.rfs.fname("/elbe.keyring")}"' + elif debootstrap_key_path: + keyring = f' --keyring="{debootstrap_key_path}"' cmd = (f'{strapcmd} --foreign --arch={arch} ' f'{keyring} "{suite}" "{self.rfs.path}" "{primary_mirror}"') try: self.cdrom_mount() - if keyring: + if keyring and self.xml.has("project/mirror/cdrom"): self.convert_asc_to_gpg("/cdrom/targetrepo/repo.pub", "/elbe.keyring") do(cmd) @@ -274,9 +292,12 @@ def add_key(self, key, keyname): Adds the binary OpenPGP keyring 'key' as a trusted apt keyring with file name 'keyname'. """ - with open(self.rfs.fname(f"/etc/apt/trusted.gpg.d/{keyname}"), "wb") as outfile: + keyfile = self.rfs.fname(f"/etc/apt/trusted.gpg.d/{keyname}") + with open(keyfile, "wb") as outfile: outfile.write(key) + return keyfile + def import_keys(self): if self.xml.has('project/mirror/url-list'): # Should we use self.xml.prj.has("noauth")??? diff --git a/elbepack/virtapt.py b/elbepack/virtapt.py index 970349318..3cf4b532a 100644 --- a/elbepack/virtapt.py +++ b/elbepack/virtapt.py @@ -150,6 +150,11 @@ def add_key(self, key, keyname): outfile.write(key) def import_keys(self): + if self.xml.has('project/mirror/primary_host') and self.xml.has('project/mirror/primary_key'): + m = self.xml.node('project/mirror') + key = "\n".join(line.strip(" \t") for line in m.text('primary_key').splitlines()[1:-1]) + self.add_key(unarmor_openpgp_keyring(key), "elbe-virtapt-primary-key.gpg") + if self.xml.has('project/mirror/url-list'): # Should we use self.xml.prj.has("noauth")??? # diff --git a/schema/dbsfed.xsd b/schema/dbsfed.xsd index 72bca5a3d..41ba3c523 100644 --- a/schema/dbsfed.xsd +++ b/schema/dbsfed.xsd @@ -283,6 +283,13 @@ SPDX-FileCopyrightText: Linutronix GmbH + + + + Raw public key used to sign the primary mirror + + + @@ -305,6 +312,13 @@ SPDX-FileCopyrightText: Linutronix GmbH + + + + Raw public key used to sign the host mirror + + +