Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

osbuild: add support for qemu-secex #3764

Merged
merged 7 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ COPY ./ /root/containerbuild/
RUN ./build.sh write_archive_info
RUN ./build.sh make_and_makeinstall
RUN ./build.sh configure_user
RUN ./build.sh patch_osbuild

# clean up scripts (it will get cached in layers, but oh well)
WORKDIR /srv/
Expand Down
23 changes: 23 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ if [ $# -gt 1 ]; then
echo " configure_yum_repos"
echo " install_rpms"
echo " make_and_makeinstall"
echo " patch_osbuild"
exit 1
fi

Expand Down Expand Up @@ -160,6 +161,27 @@ write_archive_info() {
prepare_git_artifacts "${srcdir}" /cosa/coreos-assembler-git.json /cosa/coreos-assembler-git.tar.gz
}

patch_osbuild() {
# Add a few patches that either haven't made it into a release or
# that will be obsoleted with other work that will be done soon.

# To make it easier to apply patches we'll move around the osbuild
# code on the system first:
rmdir /usr/lib/osbuild/osbuild
mv /usr/lib/python3.12/site-packages/osbuild /usr/lib/osbuild/
mkdir /usr/lib/osbuild/tools
mv /usr/bin/osbuild-mpp /usr/lib/osbuild/tools/

# Now all the software is under the /usr/lib/osbuild dir and we can patch
patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0001-stages-add-stage-for-creating-dm-verity-partitions.patch

# And then move the files back; supermin appliance creation will need it back
# in the places delivered by the RPM.
mv /usr/lib/osbuild/tools/osbuild-mpp /usr/bin/osbuild-mpp
mv /usr/lib/osbuild/osbuild /usr/lib/python3.12/site-packages/osbuild
mkdir /usr/lib/osbuild/osbuild
}

if [ $# -ne 0 ]; then
# Run the function specified by the calling script
${1}
Expand All @@ -174,4 +196,5 @@ else
install_ocp_tools
trust_redhat_gpg_keys
configure_user
patch_osbuild
fi
208 changes: 208 additions & 0 deletions src/0001-stages-add-stage-for-creating-dm-verity-partitions.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
From 136a73c9f9b64afcc1bc2a4610768be83d406d33 Mon Sep 17 00:00:00 2001
From: Nikita Dubrovskii <nikita@linux.ibm.com>
Date: Wed, 20 Mar 2024 10:03:49 +0100
Subject: [PATCH] stages: add stage for creating dm-verity partitions

Co-authored-by: Michael Vogt <michael.vogt@gmail.com>
---
stages/org.osbuild.dmverity | 31 ++++++++++
stages/org.osbuild.dmverity.meta.json | 58 +++++++++++++++++++
stages/test/test_dmverity.py | 83 +++++++++++++++++++++++++++
3 files changed, 172 insertions(+)
create mode 100755 stages/org.osbuild.dmverity
create mode 100644 stages/org.osbuild.dmverity.meta.json
create mode 100644 stages/test/test_dmverity.py

diff --git a/stages/org.osbuild.dmverity b/stages/org.osbuild.dmverity
new file mode 100755
index 00000000..88a9cae7
--- /dev/null
+++ b/stages/org.osbuild.dmverity
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+import os
+import subprocess
+import sys
+
+import osbuild.api
+
+
+def main(tree, paths, devices, options):
+ data_device = os.path.join(paths["devices"], devices["data_device"]["path"])
+ hash_device = os.path.join(paths["devices"], devices["hash_device"]["path"])
+
+ blocksize = options.get("blocksize", 512)
+ root_hash_file = os.path.join(tree, options["root_hash_file"])
+
+ subprocess.run(["/usr/sbin/veritysetup",
+ "format", data_device, hash_device,
+ "--data-block-size", f"{blocksize}",
+ "--root-hash-file", root_hash_file],
+ check=True)
+
+ subprocess.run(["/usr/sbin/veritysetup",
+ "verify", data_device, hash_device,
+ "--root-hash-file", root_hash_file],
+ check=True)
+
+
+if __name__ == '__main__':
+ args = osbuild.api.arguments()
+ r = main(args["tree"], args["paths"], args["devices"], args["options"])
+ sys.exit(r)
diff --git a/stages/org.osbuild.dmverity.meta.json b/stages/org.osbuild.dmverity.meta.json
new file mode 100644
index 00000000..213b5855
--- /dev/null
+++ b/stages/org.osbuild.dmverity.meta.json
@@ -0,0 +1,58 @@
+{
+ "summary": "Enables dm-verity protection",
+ "description": [
+ "Sets up dm-verity for data_device and stores hash blockes on hash_device.",
+ "Root hash gets written to `root_hash_file`"
+ ],
+ "schema_2": {
+ "options": {
+ "additionalProperties": false,
+ "required": [
+ "root_hash_file"
+ ],
+ "properties": {
+ "blocksize": {
+ "type": "number",
+ "default": 512
+ },
+ "root_hash_file": {
+ "type": "string"
+ }
+ }
+ },
+ "devices": {
+ "type": "object",
+ "additionalProperties": true,
+ "required": [
+ "data_device",
+ "hash_device"
+ ],
+ "properties": {
+ "data_device": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "path"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ }
+ }
+ },
+ "hash_device": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "path"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/stages/test/test_dmverity.py b/stages/test/test_dmverity.py
new file mode 100644
index 00000000..b2d3621b
--- /dev/null
+++ b/stages/test/test_dmverity.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+
+import os
+import subprocess
+
+import pytest # type: ignore
+
+from osbuild import testutil
+from osbuild.testutil import has_executable
+
+STAGE_NAME = "org.osbuild.dmverity"
+
+
+@pytest.mark.parametrize("test_data,expected_err", [
+ # bad
+ ({}, "'root_hash_file' is a required property"),
+ ({"root_hash_file": 123}, "123 is not of type 'string'"),
+ # good
+ ({"root_hash_file": "abc"}, ""),
+])
+def test_schema_validation_dmverity(stage_schema, test_data, expected_err):
+ test_input = {
+ "type": STAGE_NAME,
+ "devices": {
+ "data_device": {
+ "path": "some-path",
+ },
+ "hash_device": {
+ "path": "some-path",
+ },
+ },
+ "options": {
+ }
+ }
+ test_input["options"].update(test_data)
+ res = stage_schema.validate(test_input)
+
+ if expected_err == "":
+ assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"
+ else:
+ assert res.valid is False
+ testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1)
+
+
+@pytest.mark.skipif(not has_executable("mkfs.ext4"), reason="need mkfs.ext4")
+@pytest.mark.skipif(not has_executable("veritysetup"), reason="need veritysetup")
+def test_dmverity_integration(tmp_path, stage_module):
+ fake_dev_path = tmp_path / "dev"
+ fake_dev_path.mkdir()
+
+ fake_data_disk = "xxd1"
+ fake_hash_disk = "xxd2"
+ for fname in [fake_data_disk, fake_hash_disk]:
+ p = fake_dev_path / fname
+ p.write_bytes(b"")
+ os.truncate(p, 10 * 1024 * 1024)
+ # format is not strictly needed as dmvertify is working on the block level but this makes the test more realistic
+ subprocess.run(
+ ["mkfs.ext4", os.fspath(fake_dev_path / fake_data_disk)], check=True)
+
+ paths = {
+ "devices": fake_dev_path,
+ }
+ devices = {
+ "data_device": {
+ "path": fake_data_disk,
+ },
+ "hash_device": {
+ "path": fake_hash_disk,
+ },
+ }
+ options = {
+ "root_hash_file": "hashfile",
+ }
+
+ tree = tmp_path
+ stage_module.main(tree, paths, devices, options)
+ output = subprocess.check_output(
+ ["veritysetup", "dump", os.fspath(fake_dev_path / fake_hash_disk)],
+ universal_newlines=True)
+ assert "UUID:" in output
+ # hash file is created and has the expected size
+ assert (tree / "hashfile").stat().st_size == 64
--
2.45.2

Loading
Loading