Skip to content

Commit

Permalink
add an bootc-image-builder test and a related podman library
Browse files Browse the repository at this point in the history
Signed-off-by: Jiri Jaburek <[email protected]>
  • Loading branch information
comps committed Nov 6, 2024
1 parent d6b828f commit f999c70
Show file tree
Hide file tree
Showing 6 changed files with 508 additions and 0 deletions.
14 changes: 14 additions & 0 deletions conf/remediation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,18 @@ def excludes():
'enable_fips_mode',
]

# RHEL Image Mode
# TODO: revisit these, see which ones we really need for use with Contest
if test_name.startswith('/hardening/container'):
rules += [
'no_direct_root_logins',
'firewalld_sshd_disabled',
'service_sshd_disabled',
'sshd_disable_root_login',
'mount_option_nodev_nonroot_local_partitions',
'enable_fips_mode',
'configure_crypto_policy',
'accounts_tmout',
]

return rules
105 changes: 105 additions & 0 deletions hardening/container/anaconda-ostree/main.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
summary: Creates an OS using Anaconda's ostreecontainer and scans it
test: python3 -m lib.runtest ./test.py
result: custom
environment+:
PYTHONPATH: ../../..
duration: 1h
require+:
# virt library dependencies
- libvirt-daemon
- libvirt-daemon-driver-qemu
- libvirt-daemon-driver-storage-core
- libvirt-daemon-driver-network
- firewalld
- qemu-kvm
- libvirt-client
- virt-install
- rpm-build
- createrepo
# podman library dependencies
- podman
extra-hardware: |
keyvalue = HVM=1
hostrequire = memory>=3720
adjust:
- when: arch != x86_64
enabled: false
because: we want to run virtualization on x86_64 only
- when: distro ~< rhel-8.10 or distro ~< rhel-9.5
enabled: false
because: TODO - what is ostreecontainer supported on?

/anssi_bp28_high:

/anssi_bp28_enhanced:
tag+:
- subset-profile

/anssi_bp28_intermediary:
tag+:
- subset-profile

/anssi_bp28_minimal:
tag+:
- subset-profile

/cis:

/cis_server_l1:
tag+:
- subset-profile

/cis_workstation_l2:

/cis_workstation_l1:
tag+:
- subset-profile

/cui:
adjust+:
- when: distro >= rhel-10
enabled: false
because: there is no CUI profile on RHEL-10+

/e8:

/hipaa:

/ism_o:

/ospp:
adjust+:
- when: distro >= rhel-10
enabled: false
because: there is no OSPP profile on RHEL-10+

/pci-dss:

/stig:

/stig_gui:
adjust+:
- enabled: false
because: not supported without GUI, use stig instead

/ccn_advanced:
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10

/ccn_intermediate:
tag+:
- subset-profile
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10

/ccn_basic:
tag+:
- subset-profile
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10
68 changes: 68 additions & 0 deletions hardening/container/anaconda-ostree/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/python3

from lib import results, oscap, versions, virt, podman, util
from conf import remediation


virt.Host.setup()

_, variant, profile = util.get_test_name().rsplit('/', 2)

oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())

# CentOS Stream image only, for now
src_image = f'quay.io/centos-bootc/centos-bootc:stream{versions.rhel.major}'

guest = virt.Guest()
guest.generate_ssh_keypair()

# prepare a Container file for making a hardened image
cfile = podman.Containerfile()
cfile += util.dedent(fr'''
FROM {src_image}
RUN dnf -y install dnf-plugins-core
RUN dnf -y copr enable packit/OpenSCAP-openscap-2170 centos-stream-9-x86_64
RUN dnf -y install openscap-utils
COPY remediation-ds.xml /root/.
RUN oscap-bootc --profile '{profile}' /root/remediation-ds.xml
''')
cfile.add_ssh_pubkey(guest.ssh_pubkey)
cfile.write_to('Containerfile')

podman.podman('image', 'build', '--tag', 'contest-hardened', '.')

ks = virt.Kickstart()

# install the VM, using a locally-hosted podman registry serving
# the hardened image for Anaconda's ostreecontainer
with podman.Registry(host_addr=virt.NETWORK_HOST) as registry:
image_url = registry.push('contest-hardened')
ks.append(f'ostreecontainer --no-signature-verification --url {image_url}')
# TODO: temporary hack because Anaconda doesn't expose --insecure-skip-tls-verification
raddr, rport = registry.get_listen_addr()
ks.add_pre(
fr'''echo -e '[[registry]]\nlocation = "{raddr}:{rport}"\n'''
r'''insecure = true\n' >> /etc/containers/registries.conf'''
)
# TODO: temporary hack end
guest.install_basic(kickstart=ks)

# boot up and scan the VM
with guest.booted():
# copy the original DS to the guest
guest.copy_to(util.get_datastream(), 'scan-ds.xml')
# scan the remediated system
proc, lines = guest.ssh_stream(
f'oscap xccdf eval --profile {profile} --progress --report report.html'
f' --results-arf results-arf.xml scan-ds.xml'
)
oscap.report_from_verbose(lines)
if proc.returncode not in [0,2]:
raise RuntimeError("post-reboot oscap failed unexpectedly")

guest.copy_from('report.html')
guest.copy_from('results-arf.xml')

util.subprocess_run(['gzip', '-9', 'results-arf.xml'], check=True)

results.report_and_exit(logs=['report.html', 'results-arf.xml.gz'])
105 changes: 105 additions & 0 deletions hardening/container/bootc-image-builder/main.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
summary: Runs bootc-image-builder remediation and scan inside VMs
test: python3 -m lib.runtest ./test.py
result: custom
environment+:
PYTHONPATH: ../../..
duration: 1h
require+:
# virt library dependencies
- libvirt-daemon
- libvirt-daemon-driver-qemu
- libvirt-daemon-driver-storage-core
- libvirt-daemon-driver-network
- firewalld
- qemu-kvm
- libvirt-client
- virt-install
- rpm-build
- createrepo
# podman library dependencies
- podman
extra-hardware: |
keyvalue = HVM=1
hostrequire = memory>=3720
adjust:
- when: arch != x86_64
enabled: false
because: we want to run virtualization on x86_64 only
- when: distro ~< rhel-8.10 or distro ~< rhel-9.5
enabled: false
because: TODO - what is bootc supported on?

/anssi_bp28_high:

/anssi_bp28_enhanced:
tag+:
- subset-profile

/anssi_bp28_intermediary:
tag+:
- subset-profile

/anssi_bp28_minimal:
tag+:
- subset-profile

/cis:

/cis_server_l1:
tag+:
- subset-profile

/cis_workstation_l2:

/cis_workstation_l1:
tag+:
- subset-profile

/cui:
adjust+:
- when: distro >= rhel-10
enabled: false
because: there is no CUI profile on RHEL-10+

/e8:

/hipaa:

/ism_o:

/ospp:
adjust+:
- when: distro >= rhel-10
enabled: false
because: there is no OSPP profile on RHEL-10+

/pci-dss:

/stig:

/stig_gui:
adjust+:
- enabled: false
because: not supported without GUI, use stig instead

/ccn_advanced:
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10

/ccn_intermediate:
tag+:
- subset-profile
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10

/ccn_basic:
tag+:
- subset-profile
adjust+:
- when: distro == rhel-8 or distro == rhel-10
enabled: false
because: CCN profiles are not present on RHEL-8 and on RHEL-10
86 changes: 86 additions & 0 deletions hardening/container/bootc-image-builder/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/python3

import shutil
from pathlib import Path

from lib import results, oscap, versions, virt, podman, util
from conf import remediation


virt.Host.setup()

_, variant, profile = util.get_test_name().rsplit('/', 2)

oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())

# CentOS Stream image only, for now
src_image = f'quay.io/centos-bootc/centos-bootc:stream{versions.rhel.major}'

# note that the .wipe() is necessary here, as we are not calling any .install()
# function that would normally perform it
guest = virt.Guest()
guest.wipe()
guest.generate_ssh_keypair()

# prepare a Container file for making a hardened image
cfile = podman.Containerfile()
cfile += util.dedent(fr'''
FROM {src_image}
RUN dnf -y install dnf-plugins-core
RUN dnf -y copr enable packit/OpenSCAP-openscap-2170 centos-stream-9-x86_64
RUN dnf -y install openscap-utils
COPY remediation-ds.xml /root/.
RUN oscap-bootc --profile '{profile}' /root/remediation-ds.xml
''')
cfile.add_ssh_pubkey(guest.ssh_pubkey)
cfile.write_to('Containerfile')

podman.podman('image', 'build', '--tag', 'contest-hardened', '.')

# pre-create a directory (inside GUEST_IMG_DIR) for storing the
# hardened image, built by bootc-image-builder
bootc_output_dir = Path(virt.GUEST_IMG_DIR) / 'bootc-image-builder-output'
if bootc_output_dir.exists():
shutil.rmtree(bootc_output_dir)
bootc_output_dir.mkdir(parents=True)

# build the hardened image using a containerized builder,
podman.podman(
'container', 'run',
'--rm',
'--privileged',
'--security-opt', 'label=type:unconfined_t',
'--volume', f'{bootc_output_dir}:/output',
'--volume', '/var/lib/containers/storage:/var/lib/containers/storage',
'quay.io/centos-bootc/bootc-image-builder',
# arguments for the builder itself
'build',
'--type', 'qcow2',
'--local',
# 'localhost/' prefix tells the builder to just use local image storage
'localhost/contest-hardened',
)

# path inside the output dir seems to be hardcoded in bootc-image-builder
qcow2_path = bootc_output_dir / 'qcow2' / 'disk.qcow2'
guest.import_image(qcow2_path, 'qcow2')

# boot up and scan the VM
with guest.booted():
# copy the original DS to the guest
guest.copy_to(util.get_datastream(), 'scan-ds.xml')
# scan the remediated system
proc, lines = guest.ssh_stream(
f'oscap xccdf eval --profile {profile} --progress --report report.html'
f' --results-arf results-arf.xml scan-ds.xml'
)
oscap.report_from_verbose(lines)
if proc.returncode not in [0,2]:
raise RuntimeError("post-reboot oscap failed unexpectedly")

guest.copy_from('report.html')
guest.copy_from('results-arf.xml')

util.subprocess_run(['gzip', '-9', 'results-arf.xml'], check=True)

results.report_and_exit(logs=['report.html', 'results-arf.xml.gz'])
Loading

0 comments on commit f999c70

Please sign in to comment.