stages/dnf: verify repository checksum

Require "checksum" option for each repository, which contains the
checksum of the `repodata/repomd.xml` file. This file (indirectly)
contains checksums for all packages.

Verify that the metadata dnf downloaded to install packages matches that
checksum. This way, this stage will give an error when a reposiory
changed between putting together the pipeline and running it.
This commit is contained in:
Lars Karlitski 2019-09-24 00:57:02 +02:00 committed by Tom Gundersen
parent e23b5a32a2
commit 57c82a00d0
9 changed files with 54 additions and 10 deletions

View file

@ -1,5 +1,6 @@
#!/usr/bin/python3
import hashlib
import json
import subprocess
import sys
@ -27,6 +28,29 @@ def write_repofile(f, repoid, repo):
write_option("gpgkey", f"file://{keyfile}")
def dnf_cachedir(repoid, repo, releasever, basearch):
"""Return the relative cache directory for a repository.
Using the same algorithm as libdnf:
https://github.com/rpm-software-management/libdnf/blob/master/libdnf/repo/Repo.cpp#L1288
"""
if "metalink" in repo:
url = repo["metalink"]
elif "mirrorlist" in repo:
url = repo["mirrorlist"]
elif "baseurl" in repo:
url = repo["baseurl"]
else:
raise RuntimeError(f"one of metalink, mirrorlist, or baseurl must be given for repository '{repoid}'")
url = url.replace("$basearch", basearch).replace("$releasever", releasever)
digest = hashlib.sha256(url.encode()).hexdigest()[:16]
return f"{repoid}-{digest}"
def main(tree, options):
repos = options["repos"]
packages = options["packages"]
@ -65,7 +89,18 @@ def main(tree, options):
] + packages
print(" ".join(cmd), flush=True)
return subprocess.run(cmd).returncode
subprocess.run(cmd, check=True)
# verify metadata checksum
for repoid, repo in repos.items():
algorithm, checksum = repo["checksum"].split(":")
assert algorithm == "sha256"
cachedir = dnf_cachedir(repoid, repo, releasever, basearch)
with open(f"{tree}/var/cache/dnf/{cachedir}/repodata/repomd.xml", "rb") as f:
repomd = f.read()
assert hashlib.sha256(repomd).hexdigest() == checksum
return 0
if __name__ == '__main__':