Skip to content

Commit 3db21e8

Browse files
committed
sbom: T9098: syft should run un squashfs instead of unpacked chroot
lb (live-build) binary runs binary_rootfs first, then binary_grub-efi. binary_grub-efi temporarily installs EFI tooling and then runs: "apt remove --auto-remove --purge --allow-remove-essential" That cleanup is safe for the already-generated squashfs, but it mutates build/chroot and can remove vyos-1x, breaking subsequent work that expects chroot to remain intact for SBOM generation. But why unpacking the squashfs? In an ideal world we could call syft on the compressed squashfs file which is supported. In our world, we do use a BCJ pre-filter chained with LZMA2 for compressing the squashfs, which will increase the compression ratio without a decompression penalty. Difference: ~14.3 MB, ~2.7% smaller This is unsupported by fyft which means we do need to unpack the squashfs first before checking the files and generating the SBOM file.
1 parent 5e2e9a3 commit 3db21e8

1 file changed

Lines changed: 32 additions & 19 deletions

File tree

scripts/image-build/build-vyos-image

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import datetime
3131
import functools
3232
import string
3333
import subprocess
34+
import tempfile
3435

3536
class ImageBuildError(Exception):
3637
pass
@@ -156,6 +157,7 @@ def build():
156157
'qemu-utils',
157158
'gdisk',
158159
'kpartx',
160+
'squashfs-tools',
159161
'dosfstools'
160162
],
161163
'binaries': []
@@ -727,26 +729,37 @@ Pin-Priority: 600
727729
manifest['artifacts'].append(iso_file)
728730

729731
# Now create SBOM
730-
syft_target_dir = 'chroot'
731-
syft_base_path = os.getcwd() + f'/{syft_target_dir}'
732732
base_filename = iso_file.rstrip('.iso')
733-
syft_cmd = [['syft', syft_target_dir,
734-
'--source-name', 'VyOS', '--source-version', version,
735-
'-o', f'cyclonedx-json={base_filename}.cdx.json',
736-
'-o', f'spdx-json={base_filename}.spdx.json']]
737-
738-
# syft bug for CycloneDX https://github.com/anchore/syft/issues/4592#issuecomment-4567247328
739-
syft_cmd.append(['sed', '-i', '-e', f's@{syft_base_path}@@g', f'{base_filename}.cdx.json'])
740-
syft_cmd.append(['sed', '-i', '-e', f's@{syft_base_path}@//@g', f'{base_filename}.spdx.json'])
741-
742-
for c in syft_cmd:
743-
with subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
744-
text=True, bufsize=1) as p:
745-
for line in p.stdout:
746-
sys.stdout.write(line)
747-
sys.stdout.flush()
748-
p.wait()
749-
print("I: Finished SBOM generation")
733+
syft_target_dir = tempfile.mkdtemp(prefix='unsquashfs_rootfs-', dir=os.getcwd())
734+
syft_base_path = syft_target_dir
735+
try:
736+
# lb config builds the amd64 squashfs as xz with -Xbcj x86 (a BCJ pre-filter
737+
# chained with LZMA2. Real unsquashfs/mksquashfs fully support multi-filter
738+
# xz streams; syft's own Go-based squashfs/xz decoder apparently only handles
739+
# plain single-filter. Extract squashfs first
740+
print("I: Unpack squashfs for SBOM generation")
741+
syft_cmd = [['unsquashfs', '-quiet', '-no-progress', '-force', '-dest', syft_target_dir, 'binary/live/filesystem.squashfs']]
742+
# run syft on extracted content
743+
syft_cmd.append(['syft', syft_target_dir,
744+
'--source-name', 'VyOS', '--source-version', version,
745+
'-o', f'cyclonedx-json={base_filename}.cdx.json',
746+
'-o', f'spdx-json={base_filename}.spdx.json'])
747+
748+
# syft bug for CycloneDX https://github.com/anchore/syft/issues/4592#issuecomment-4567247328
749+
syft_cmd.append(['sed', '-i', '-e', f's@{syft_base_path}@@g', f'{base_filename}.cdx.json'])
750+
syft_cmd.append(['sed', '-i', '-e', f's@{syft_base_path}@//@g', f'{base_filename}.spdx.json'])
751+
752+
for c in syft_cmd:
753+
with subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
754+
text=True, bufsize=1) as p:
755+
for line in p.stdout:
756+
sys.stdout.write(line)
757+
sys.stdout.flush()
758+
p.wait()
759+
print("I: Finished SBOM generation")
760+
finally:
761+
# remove temporary unpacked squashfs, even on failure/interruption
762+
shutil.rmtree(syft_target_dir, ignore_errors=True)
750763

751764

752765
# If the flavor has `image_format = "iso"`, then the work is done.

0 commit comments

Comments
 (0)