Skip to content

Commit 180be1b

Browse files
nikita-dubrovskiijlebon
authored andcommitted
Add org.osbuild.dmverity stage patch
1 parent 1057608 commit 180be1b

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ COPY ./ /root/containerbuild/
2323
RUN ./build.sh write_archive_info
2424
RUN ./build.sh make_and_makeinstall
2525
RUN ./build.sh configure_user
26+
RUN ./build.sh patch_osbuild
2627

2728
# clean up scripts (it will get cached in layers, but oh well)
2829
WORKDIR /srv/

build.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ if [ $# -gt 1 ]; then
2222
echo " configure_yum_repos"
2323
echo " install_rpms"
2424
echo " make_and_makeinstall"
25+
echo " patch_osbuild"
2526
exit 1
2627
fi
2728

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

164+
patch_osbuild() {
165+
# Add a few patches that either haven't made it into a release or
166+
# that will be obsoleted with other work that will be done soon.
167+
168+
# To make it easier to apply patches we'll move around the osbuild
169+
# code on the system first:
170+
rmdir /usr/lib/osbuild/osbuild
171+
mv /usr/lib/python3.12/site-packages/osbuild /usr/lib/osbuild/
172+
mkdir /usr/lib/osbuild/tools
173+
mv /usr/bin/osbuild-mpp /usr/lib/osbuild/tools/
174+
175+
# Now all the software is under the /usr/lib/osbuild dir and we can patch
176+
patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/0001-stages-add-stage-for-creating-dm-verity-partitions.patch
177+
178+
# And then move the files back; supermin appliance creation will need it back
179+
# in the places delivered by the RPM.
180+
mv /usr/lib/osbuild/tools/osbuild-mpp /usr/bin/osbuild-mpp
181+
mv /usr/lib/osbuild/osbuild /usr/lib/python3.12/site-packages/osbuild
182+
mkdir /usr/lib/osbuild/osbuild
183+
}
184+
163185
if [ $# -ne 0 ]; then
164186
# Run the function specified by the calling script
165187
${1}
@@ -174,4 +196,5 @@ else
174196
install_ocp_tools
175197
trust_redhat_gpg_keys
176198
configure_user
199+
patch_osbuild
177200
fi
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
From 136a73c9f9b64afcc1bc2a4610768be83d406d33 Mon Sep 17 00:00:00 2001
2+
From: Nikita Dubrovskii <[email protected]>
3+
Date: Wed, 20 Mar 2024 10:03:49 +0100
4+
Subject: [PATCH] stages: add stage for creating dm-verity partitions
5+
6+
Co-authored-by: Michael Vogt <[email protected]>
7+
---
8+
stages/org.osbuild.dmverity | 31 ++++++++++
9+
stages/org.osbuild.dmverity.meta.json | 58 +++++++++++++++++++
10+
stages/test/test_dmverity.py | 83 +++++++++++++++++++++++++++
11+
3 files changed, 172 insertions(+)
12+
create mode 100755 stages/org.osbuild.dmverity
13+
create mode 100644 stages/org.osbuild.dmverity.meta.json
14+
create mode 100644 stages/test/test_dmverity.py
15+
16+
diff --git a/stages/org.osbuild.dmverity b/stages/org.osbuild.dmverity
17+
new file mode 100755
18+
index 00000000..88a9cae7
19+
--- /dev/null
20+
+++ b/stages/org.osbuild.dmverity
21+
@@ -0,0 +1,31 @@
22+
+#!/usr/bin/python3
23+
+import os
24+
+import subprocess
25+
+import sys
26+
+
27+
+import osbuild.api
28+
+
29+
+
30+
+def main(tree, paths, devices, options):
31+
+ data_device = os.path.join(paths["devices"], devices["data_device"]["path"])
32+
+ hash_device = os.path.join(paths["devices"], devices["hash_device"]["path"])
33+
+
34+
+ blocksize = options.get("blocksize", 512)
35+
+ root_hash_file = os.path.join(tree, options["root_hash_file"])
36+
+
37+
+ subprocess.run(["/usr/sbin/veritysetup",
38+
+ "format", data_device, hash_device,
39+
+ "--data-block-size", f"{blocksize}",
40+
+ "--root-hash-file", root_hash_file],
41+
+ check=True)
42+
+
43+
+ subprocess.run(["/usr/sbin/veritysetup",
44+
+ "verify", data_device, hash_device,
45+
+ "--root-hash-file", root_hash_file],
46+
+ check=True)
47+
+
48+
+
49+
+if __name__ == '__main__':
50+
+ args = osbuild.api.arguments()
51+
+ r = main(args["tree"], args["paths"], args["devices"], args["options"])
52+
+ sys.exit(r)
53+
diff --git a/stages/org.osbuild.dmverity.meta.json b/stages/org.osbuild.dmverity.meta.json
54+
new file mode 100644
55+
index 00000000..213b5855
56+
--- /dev/null
57+
+++ b/stages/org.osbuild.dmverity.meta.json
58+
@@ -0,0 +1,58 @@
59+
+{
60+
+ "summary": "Enables dm-verity protection",
61+
+ "description": [
62+
+ "Sets up dm-verity for data_device and stores hash blockes on hash_device.",
63+
+ "Root hash gets written to `root_hash_file`"
64+
+ ],
65+
+ "schema_2": {
66+
+ "options": {
67+
+ "additionalProperties": false,
68+
+ "required": [
69+
+ "root_hash_file"
70+
+ ],
71+
+ "properties": {
72+
+ "blocksize": {
73+
+ "type": "number",
74+
+ "default": 512
75+
+ },
76+
+ "root_hash_file": {
77+
+ "type": "string"
78+
+ }
79+
+ }
80+
+ },
81+
+ "devices": {
82+
+ "type": "object",
83+
+ "additionalProperties": true,
84+
+ "required": [
85+
+ "data_device",
86+
+ "hash_device"
87+
+ ],
88+
+ "properties": {
89+
+ "data_device": {
90+
+ "type": "object",
91+
+ "additionalProperties": false,
92+
+ "required": [
93+
+ "path"
94+
+ ],
95+
+ "properties": {
96+
+ "path": {
97+
+ "type": "string"
98+
+ }
99+
+ }
100+
+ },
101+
+ "hash_device": {
102+
+ "type": "object",
103+
+ "additionalProperties": false,
104+
+ "required": [
105+
+ "path"
106+
+ ],
107+
+ "properties": {
108+
+ "path": {
109+
+ "type": "string"
110+
+ }
111+
+ }
112+
+ }
113+
+ }
114+
+ }
115+
+ }
116+
+}
117+
diff --git a/stages/test/test_dmverity.py b/stages/test/test_dmverity.py
118+
new file mode 100644
119+
index 00000000..b2d3621b
120+
--- /dev/null
121+
+++ b/stages/test/test_dmverity.py
122+
@@ -0,0 +1,83 @@
123+
+#!/usr/bin/python3
124+
+
125+
+import os
126+
+import subprocess
127+
+
128+
+import pytest # type: ignore
129+
+
130+
+from osbuild import testutil
131+
+from osbuild.testutil import has_executable
132+
+
133+
+STAGE_NAME = "org.osbuild.dmverity"
134+
+
135+
+
136+
+@pytest.mark.parametrize("test_data,expected_err", [
137+
+ # bad
138+
+ ({}, "'root_hash_file' is a required property"),
139+
+ ({"root_hash_file": 123}, "123 is not of type 'string'"),
140+
+ # good
141+
+ ({"root_hash_file": "abc"}, ""),
142+
+])
143+
+def test_schema_validation_dmverity(stage_schema, test_data, expected_err):
144+
+ test_input = {
145+
+ "type": STAGE_NAME,
146+
+ "devices": {
147+
+ "data_device": {
148+
+ "path": "some-path",
149+
+ },
150+
+ "hash_device": {
151+
+ "path": "some-path",
152+
+ },
153+
+ },
154+
+ "options": {
155+
+ }
156+
+ }
157+
+ test_input["options"].update(test_data)
158+
+ res = stage_schema.validate(test_input)
159+
+
160+
+ if expected_err == "":
161+
+ assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"
162+
+ else:
163+
+ assert res.valid is False
164+
+ testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1)
165+
+
166+
+
167+
+@pytest.mark.skipif(not has_executable("mkfs.ext4"), reason="need mkfs.ext4")
168+
+@pytest.mark.skipif(not has_executable("veritysetup"), reason="need veritysetup")
169+
+def test_dmverity_integration(tmp_path, stage_module):
170+
+ fake_dev_path = tmp_path / "dev"
171+
+ fake_dev_path.mkdir()
172+
+
173+
+ fake_data_disk = "xxd1"
174+
+ fake_hash_disk = "xxd2"
175+
+ for fname in [fake_data_disk, fake_hash_disk]:
176+
+ p = fake_dev_path / fname
177+
+ p.write_bytes(b"")
178+
+ os.truncate(p, 10 * 1024 * 1024)
179+
+ # format is not strictly needed as dmvertify is working on the block level but this makes the test more realistic
180+
+ subprocess.run(
181+
+ ["mkfs.ext4", os.fspath(fake_dev_path / fake_data_disk)], check=True)
182+
+
183+
+ paths = {
184+
+ "devices": fake_dev_path,
185+
+ }
186+
+ devices = {
187+
+ "data_device": {
188+
+ "path": fake_data_disk,
189+
+ },
190+
+ "hash_device": {
191+
+ "path": fake_hash_disk,
192+
+ },
193+
+ }
194+
+ options = {
195+
+ "root_hash_file": "hashfile",
196+
+ }
197+
+
198+
+ tree = tmp_path
199+
+ stage_module.main(tree, paths, devices, options)
200+
+ output = subprocess.check_output(
201+
+ ["veritysetup", "dump", os.fspath(fake_dev_path / fake_hash_disk)],
202+
+ universal_newlines=True)
203+
+ assert "UUID:" in output
204+
+ # hash file is created and has the expected size
205+
+ assert (tree / "hashfile").stat().st_size == 64
206+
--
207+
2.45.2
208+

src/cmdlib.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ runvm() {
729729

730730
# include COSA in the image
731731
find /usr/lib/coreos-assembler/ -type f > "${vmpreparedir}/hostfiles"
732+
echo /usr/lib/osbuild/stages/org.osbuild.dmverity >> "${vmpreparedir}/hostfiles"
732733

733734
# and include all GPG keys
734735
find /etc/pki/rpm-gpg/ -type f >> "${vmpreparedir}/hostfiles"

0 commit comments

Comments
 (0)