image-info: changes related to reading SELinux labels unknown to host

When `image-info` inspects ostree images, the `/usr/etc` is bind-mounted
to `/etc`. This results in conflicting SELinux policy specification for
these files and makes the outcome dependent on the `setfiles` build.
All the files in `/etc` have mismatch in the expected vs. actual SELinux
context.

Exclude `/etc` from the check of SELinux ctx mismatches in case the
analysed tree is from an ostree-based image.

Sort the list returned `read_selinux_ctx_mismatch()` based on the item's
`filename` key, to make the result consistent across runs.

`image-info` can not read SELinux labels from the images, which are not
known to the host. This makes the report content depend on the host
environment. As a temporary workaround, relabel the image-info script with
osbuild_exec_t label to allow it to read unknown SELinux labels.

Modify documentation in `test/README.md` to explain the issue with
`image-info` and unknown SELinux labels.

Modify the `generate-all-test-cases` to relabel `image-info` before
generating test cases.

Modify the `image_tests.sh` to relabel `image-info` before running image
test cases.

Add 'tar' image for 'rhel-8' on 's390x' back to the matrix of generated
test cases, as it was removed by mistake. Regenerate the image test
case. Remove 'tar' image from 'rhel-84' on 's390x' from the matrix of
generated test cases, as it is not supported.

Regenerate all affected image test cases.

Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
Tomas Hozza 2021-06-11 12:40:15 +02:00 committed by Alexander Todorov
parent 95cd5b782e
commit bce603586e
26 changed files with 134 additions and 144862 deletions

View file

@ -835,7 +835,7 @@ def read_locale(tree):
return parse_environment_vars(f.read())
def read_selinux_info(tree):
def read_selinux_info(tree, is_ostree):
"""
Read information related to SELinux.
@ -872,7 +872,7 @@ def read_selinux_info(tree):
result["policy"] = policy
with contextlib.suppress(subprocess.CalledProcessError):
ctx_mismatch = read_selinux_ctx_mismatch(tree)
ctx_mismatch = read_selinux_ctx_mismatch(tree, is_ostree)
if ctx_mismatch:
result["context-mismatch"] = ctx_mismatch
@ -897,7 +897,7 @@ def read_selinux_conf(tree):
return parse_environment_vars(f.read())
def read_selinux_ctx_mismatch(tree):
def read_selinux_ctx_mismatch(tree, is_ostree):
"""
Read any mismatch in selinux context of files on the image.
@ -905,6 +905,11 @@ def read_selinux_ctx_mismatch(tree):
are no mismatches between used and expected selinux context,
then an empty list is returned.
If the checked 'tree' is ostree, then the path '/etc' is
excluded from the check. This is beause it is bind-mounted
from /usr/etc and therefore has incorrect selinux context
for its filesystem path.
An example of returned value:
[
{
@ -937,6 +942,12 @@ def read_selinux_ctx_mismatch(tree):
f"{tree}"
]
if is_ostree:
# exclude /etc from being checked when the tree is ostree, because
# it is just bind-mounted from /usr/etc and has incorrect selinux
# context for /etc path
CMD.extend(["-e", f"{tree}/etc"])
output = subprocess.check_output(CMD).decode()
# output are lines such as:
@ -959,6 +970,9 @@ def read_selinux_ctx_mismatch(tree):
}
result.append(parsed_line)
# sort the list to make it consistent across runs
result.sort(key=lambda x: x.get("filename"))
return result
@ -1742,7 +1756,7 @@ def append_filesystem(report, tree, *, is_ostree=False):
if rhsm:
report["rhsm"] = rhsm
selinux = read_selinux_info(tree)
selinux = read_selinux_info(tree, is_ostree)
if selinux:
report["selinux"] = selinux

View file

@ -50,7 +50,8 @@
"tar"
],
"s390x": [
"qcow2"
"qcow2",
"tar"
]
},
"rhel-84": {
@ -79,8 +80,7 @@
"tar"
],
"s390x": [
"qcow2",
"tar"
"qcow2"
]
},
"rhel-90": {

View file

@ -102,9 +102,10 @@ class RunnerMountPoint:
Data structure to represent basic data used by Runners to attach host
directory as virtfs to the guest and then to mount it.
"""
def __init__(self, src_host, dst_guest, mount_tag, readonly):
def __init__(self, src_host, dst_guest, mount_tag, security_model, readonly):
self.src_host = src_host
self.dst_guest = dst_guest
self.security_model = security_model
self.readonly = readonly
self.mount_tag = mount_tag
@ -115,9 +116,13 @@ class RunnerMountPoint:
image test cases.
"""
sources_dir = os.getcwd() if sources_dir is None else sources_dir
# Use 'passthrough' security policy for /mnt/sources. The reason is that
# we need it to be exported to the VM without attributes, like symlink
# target, being mapped in xattrs. Otherwise copying the directory
# elsewhere produces errors.
mount_points = [
RunnerMountPoint(sources_dir, "/mnt/sources", "sources", True),
RunnerMountPoint(output_dir, "/mnt/output", "output", False)
RunnerMountPoint(sources_dir, "/mnt/sources", "sources", "passthrough", True),
RunnerMountPoint(output_dir, "/mnt/output", "output", "mapped-xattr", False)
]
return mount_points
@ -216,8 +221,9 @@ class BaseRunner(contextlib.AbstractContextManager):
for mount_point in self.mount_points:
src_host = mount_point.src_host
tag = mount_point.mount_tag
security_model = mount_point.security_model
readonly = ",readonly" if mount_point.readonly else ""
qemu_cmd.extend(["-virtfs", f"local,path={src_host},mount_tag={tag},security_model=mapped-xattr{readonly}"])
qemu_cmd.extend(["-virtfs", f"local,path={src_host},mount_tag={tag},security_model={security_model}{readonly}"])
# handle boot image
qemu_cmd.extend(self._get_qemu_boot_image_option())
@ -673,13 +679,30 @@ class TestCaseMatrixGenerator(contextlib.AbstractContextManager):
# don't use /var/tmp for osbuild's store directory to prevent systemd from possibly
# removing some of the downloaded RPMs due to "ageing"
guest_osbuild_store_dir = "/root/osbuild-store"
guest_osbuild_store_dir = "/home/admin/osbuild-store"
runner.run_command_check_call(f"sudo mkdir {guest_osbuild_store_dir}")
# Log installed versions of important RPMs
rpm_versions, _, _ = runner.run_command("rpm -q osbuild osbuild-composer")
log.info("Installed packages: %s", " ".join(rpm_versions.split("\n")))
# Workaround the problem that 'image-info' can not read SELinux labels unknown to the host.
# It is not possible to relabel the 'image-info' in the mounted path, because it is read-only.
# Also bind-mounting copy of image-info with proper SELinux labels to /mnt/sources didn't do the trick.
# For the reason above, make a full copy of sources in /home/admin and operate on it instead.
osbuild_label, stderr, retcode = runner.run_command("matchpathcon -n /usr/bin/osbuild")
if retcode:
raise RuntimeError(f"Running 'matchpathcon' on the guest failed. retcode: {retcode}\n\nstderr: {stderr}")
osbuild_label = osbuild_label.strip()
sources_path = "/home/admin/sources"
image_info_guest_path = f"{sources_path}/tools/image-info"
log.info(f"Making copy of sources in '{sources_path}'.")
# exclude test/data/manifests, because it is mounted from the host into /mnt/output and
# has UID and GID set, which does not allow us to access it. And it is not needed
# to generate a test case!
runner.run_command_check_call(f"rsync -a --exclude=test/data/manifests /mnt/sources/ {sources_path}")
runner.run_command_check_call(f"chcon {osbuild_label} {image_info_guest_path}")
for distro, img_type_list in generation_matrix.items():
for image_type in img_type_list:
log.info("Generating test case for '%s' '%s' image on '%s'", distro, image_type, arch)
@ -691,7 +714,7 @@ class TestCaseMatrixGenerator(contextlib.AbstractContextManager):
else:
with_customizations = False
gen_test_cases_cmd = "cd /mnt/sources; sudo tools/test-case-generators/generate-test-cases" + \
gen_test_cases_cmd = f"cd {sources_path}; sudo tools/test-case-generators/generate-test-cases" + \
f" --distro {distro} --arch {arch} --image-types {image_type}" + \
f" --store {guest_osbuild_store_dir} --output /mnt/output/"
if with_customizations: