[elbe-devel] [PATCH v3 3/8] debinstaller: implement download of vmlinuz and initrd.gz

Manuel Traut manut at linutronix.de
Fri Oct 19 13:21:20 CEST 2018


On Mon, Oct 15, 2018 at 03:18:01PM +0200, Torben Hohn wrote:
> the debinstaller module shall replace elbe-bootstrap.
> it downloads debian installer linux kernel and initrd.gz from
> a debian mirror. It does that in a secure manner, iE validating
> Release.gpg and SHA256SUMS on the way.
> 
> filenames on the mirror:
> /debian/dists/jessie/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
> /debian/dists/jessie/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
> /debian/dists/jessie/main/installer-amd64/current/images/cdrom/initrd.gz
> 
> Although this functionality is also provided by apt,
> we implement it here in pure python, because outside
> of the initvm, we can not rely on apt being available.
> 
> The initrd and vmlinuz are stored in the initvm in /var/cache/elbe/installer.
> Also put them on bin-cdrom.iso, and reuse them, when an elbe build
> is run from cdrom.
> 
> since elbe uses elbepack.debinstaller outside of the initvm now,
> the non-virtap fallback code is not needed anymore.
> Remove it and make pkgutils.py pylint clean.
> Also rename the, now moved to debinstaller, NoKinitrdException
> to NoPackageException.
> 
> Also remove code dealing with elbe bootstrap in packagelists and
> xmldefaults.
> 
> Signed-off-by: Torben Hohn <torben.hohn at linutronix.de>
>
> ---

[..]

> diff --git a/elbepack/debinstaller.py b/elbepack/debinstaller.py
> new file mode 100644
> index 00000000..d251f202
> --- /dev/null
> +++ b/elbepack/debinstaller.py

[..]

> +def download(url, local_fname):
> +    try:
> +        rf = urlopen(url, None, 10)
> +        with open(local_fname, "w") as wf:
> +            copyfileobj(rf, wf)
> +    finally:
> +        rf.close()

error handling is not correct, if an invalid URL was specified,
i get a backtrace:

--8<--
Traceback (most recent call last):
  File "/home/manut/Projects/elbe/elbe/elbe", line 55, in <module>
    cmdmod.run_command(sys.argv[2:])
  File "/home/manut/Projects/elbe/elbe/elbepack/commands/init.py", line 168, in run_command
    copy_kinitrd(xml.node("/initvm"), out_path)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 231, in copy_kinitrd
    download_kinitrd(tmp, suite, mirror)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 169, in download_kinitrd
    download_release(tmp, base_url)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 135, in download_release
    download(base_url + "Release", tmp.fname('Release'))
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 123, in download
    rf.close()
UnboundLocalError: local variable 'rf' referenced before assignment
--8<--

Also the user should get some output during download. URL, filename is mandatory
for analyzing problems. Sth. like a progress bar would be nice to have if easy
to implement.

Why not just use os.system(wget..) ?

> +def download_release(tmp, base_url):
> +
> +    # setup gpg context, for verifying
> +    # the Release.gpg signature.
> +    os.environ['GNUPGHOME'] = tmp.fname('/')
> +    ctx = Context()
> +
> +    # download the Relase file to a tmp file,
> +    # because we need it 2 times
> +    download(base_url + "Release", tmp.fname('Release'))
> +
> +    # validate signature.
> +    # open downloaded plaintext file, and
> +    # use the urlopen object of the Release.gpg
> +    # directtly.
> +    try:
> +        sig = urlopen(base_url + 'Release.gpg', None, 10)
> +        with tmp.open("Release", "r") as signed:
> +
> +            overall_status = OverallStatus()
> +
> +            # verify detached signature
> +            sigs = ctx.verify(sig, signed, None)
> +
> +            for s in sigs:
> +                status = check_signature(ctx, s)
> +                overall_status.add(status)
> +
> +            if overall_status.to_exitcode():
> +                raise InvalidSignature('Failed to verify Release file')
> +    finally:
> +        sig.close()

incorrect error handling

If Release.gpg is not on the mirror, i get:

  File "/home/manut/Projects/elbe/elbe/elbe", line 55, in <module>
    cmdmod.run_command(sys.argv[2:])
  File "/home/manut/Projects/elbe/elbe/elbepack/commands/init.py", line 168, in run_command
    copy_kinitrd(xml.node("/initvm"), out_path)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 231, in copy_kinitrd
    download_kinitrd(tmp, suite, mirror)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 169, in download_kinitrd
    download_release(tmp, base_url)
  File "/home/manut/Projects/elbe/elbe/elbepack/debinstaller.py", line 157, in download_release
    sig.close()
UnboundLocalError: local variable 'sig' referenced before assignment

I just found the same issue in hashes.py..

> +def download_kinitrd(tmp, suite, mirror):
> +    base_url = "%s/dists/%s/" % (
> +        mirror.replace("LOCALMACHINE", "localhost"), suite)
> +    installer_path = "main/installer-amd64/current/images/"
> +
> +    setup_apt_keyring(tmp.fname('/'), 'pubring.gpg')
> +
> +    # download release file and check
> +    # signature
> +    download_release(tmp, base_url)
> +
> +    # parse Release file, and remember hashvalues
> +    # we are interested in
> +    interesting = [installer_path + 'SHA256SUMS']
> +    release_file = ReleaseFile(base_url, tmp.fname('Release'), interesting)
> +
> +    # now download and validate SHA256SUMS
> +    release_file.download_and_validate_file(
> +            installer_path + 'SHA256SUMS',
> +            tmp.fname('SHA256SUMS'))
> +
> +    # now we have a valid SHA256SUMS file
> +    # parse it
> +    interesting = ['./cdrom/initrd.gz',
> +                   './cdrom/vmlinuz',
> +                   './netboot/debian-installer/amd64/initrd.gz',
> +                   './netboot/debian-installer/amd64/linux']
> +    sha256_sums = SHA256SUMSFile(
> +            base_url + installer_path,
> +            tmp.fname('SHA256SUMS'),
> +            interesting)
> +
> +    # and then download the files we actually want
> +    for p, ln in zip(interesting, ['initrd-cdrom.gz',
> +                                   'linux-cdrom',
> +                                   'initrd.gz',
> +                                   'vmlinuz']):
> +        sha256_sums.download_and_validate_file(
> +                p,
> +                tmp.fname(ln))
> +
> +
> +def get_primary_mirror(prj):
> +    if prj.has("mirror/primary_host"):
> +        m = prj.node("mirror")
> +
> +        mirror = m.text("primary_proto") + "://"
> +        mirror += m.text("primary_host") + "/"
> +        mirror += m.text("primary_path")
> +    else:
> +        raise NoKinitrdException("Broken xml file: "
> +                                 "no cdrom and no primary host")

no cdrom?? this is not checked.

Why not use elbexml.py / get_primary_mirror ?

> +    return mirror
> +
> +
> +def copy_kinitrd(prj, target_dir):
> +
> +    suite = prj.text("suite")
> +
> +    try:
> +        tmp = TmpdirFilesystem()
> +        if prj.has("mirror/cdrom"):
> +            system('7z x -o%s "%s" initrd-cdrom.gz vmlinuz' %
> +                   (tmp.fname('/'), prj.text("mirror/cdrom")))

we should use check_call instead for new code




More information about the elbe-devel mailing list