Skip to content
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
176 changes: 169 additions & 7 deletions toolkit/tools/pkg/imagecustomizerlib/customizeuki_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,50 @@ func TestCustomizeImageVerityUsrUki(t *testing.T) {
verifyUsrVerity(t, buildDir, outImageFilePath2, ukiFilesChecksums)
}

func TestCustomizeImageVerityRootUki(t *testing.T) {
baseImageInfo := testBaseImageAzl3CoreEfi
baseImage := checkSkipForCustomizeImage(t, baseImageInfo)

ukifyExists, err := file.CommandExists("ukify")
assert.NoError(t, err)
if !ukifyExists {
t.Skip("The 'ukify' command is not available")
}

if runtime.GOARCH == "arm64" {
t.Skip("systemd-boot not available on AZL3 ARM64 yet")
}

testTempDir := filepath.Join(tmpDir, "TestCustomizeImageRootVerityUki")
buildDir := filepath.Join(testTempDir, "build")
outImageFilePath := filepath.Join(testTempDir, "image.raw")
configFile := filepath.Join(testDir, "verity-root-uki.yaml")

// Customize image.
err = CustomizeImageWithConfigFile(t.Context(), buildDir, configFile, baseImage, nil, outImageFilePath, "raw",
true /*useBaseImageRpmRepos*/, "" /*packageSnapshotTime*/)
if !assert.NoError(t, err) {
return
}

ukiFilesChecksums, ok := verifyRootVerityUki(t, buildDir, outImageFilePath, nil)
if !ok {
return
}

// Customize again without changing /root verity or the UKI.
outImageFilePath2 := filepath.Join(testTempDir, "image2.raw")
configFile2 := filepath.Join(testDir, "verity-reinit-root-nop.yaml")

err = CustomizeImageWithConfigFile(t.Context(), buildDir, configFile2, outImageFilePath, nil, outImageFilePath2, "raw",
true /*useBaseImageRpmRepos*/, "" /*packageSnapshotTime*/)
if !assert.NoError(t, err) {
return
}

verifyRootVerityUki(t, buildDir, outImageFilePath2, ukiFilesChecksums)
}

func verifyUsrVerity(t *testing.T, buildDir string, imagePath string, expectedUkiFilesChecksums map[string]string,
) (map[string]string, bool) {
// Connect to customized image.
Expand Down Expand Up @@ -167,14 +211,117 @@ func verifyUsrVerity(t *testing.T, buildDir string, imagePath string, expectedUk
return nil, false
}

ukiFilesChecksums := make(map[string]string)
for _, ukiFile := range ukiFiles {
checksum, err := file.GenerateSHA256(ukiFile)
if !assert.NoError(t, err) {
return nil, false
}
ukiFilesChecksums := calculateUkiFileChecksums(t, ukiFiles)
if ukiFilesChecksums == nil {
return nil, false
}

ukiFilesChecksums[ukiFile] = checksum
if expectedUkiFilesChecksums != nil {
// Verify that the UKI files haven't changed.
// Note: This indirectly also checks that the verity partitions haven't changed since the UKIs contain the
// verity root hash in the kernel command-line args.
assert.Equal(t, expectedUkiFilesChecksums, ukiFilesChecksums)
}

return ukiFilesChecksums, true
}

func verifyRootVerityUki(t *testing.T, buildDir string, imagePath string, expectedUkiFilesChecksums map[string]string,
) (map[string]string, bool) {
// Connect to customized image.
mountPoints := []testutils.MountPoint{
{
PartitionNum: 3,
Path: "/",
FileSystemType: "ext4",
Flags: unix.MS_RDONLY,
},
{
PartitionNum: 2,
Path: "/boot",
FileSystemType: "ext4",
},
{
PartitionNum: 1,
Path: "/boot/efi",
FileSystemType: "vfat",
},
{
PartitionNum: 5,
Path: "/var",
FileSystemType: "ext4",
},
}

imageConnection, err := testutils.ConnectToImage(buildDir, imagePath, false /*includeDefaultMounts*/, mountPoints)
if !assert.NoError(t, err) {
return nil, false
}
defer imageConnection.Close()

partitions, err := getDiskPartitionsMap(imageConnection.Loopback().DevicePath())
assert.NoError(t, err, "get disk partitions")

// Verify that verity is configured correctly.
espPath := filepath.Join(imageConnection.Chroot().RootDir(), "/boot/efi")
rootDevice := testutils.PartitionDevPath(imageConnection, 3)
rootHashDevice := testutils.PartitionDevPath(imageConnection, 4)
verifyVerityUki(t, espPath, rootDevice, rootHashDevice, "PARTUUID="+partitions[3].PartUuid,
"PARTUUID="+partitions[4].PartUuid, "root", buildDir, "rd.info", "panic-on-corruption")

expectedFstabEntries := []diskutils.FstabEntry{
{
Source: "/dev/mapper/root",
Target: "/",
FsType: "ext4",
Options: "ro",
VfsOptions: 0x1,
FsOptions: "",
Freq: 0,
PassNo: 1,
},
{
Source: "PARTUUID=" + partitions[2].PartUuid,
Target: "/boot",
FsType: "ext4",
Options: "defaults",
VfsOptions: 0x0,
FsOptions: "",
Freq: 0,
PassNo: 2,
},
{
Source: "PARTUUID=" + partitions[1].PartUuid,
Target: "/boot/efi",
FsType: "vfat",
Options: "umask=0077",
VfsOptions: 0x0,
FsOptions: "umask=0077",
Freq: 0,
PassNo: 2,
},
{
Source: "PARTUUID=" + partitions[5].PartUuid,
Target: "/var",
FsType: "ext4",
Options: "defaults",
VfsOptions: 0x0,
FsOptions: "",
Freq: 0,
PassNo: 2,
},
}
filteredFstabEntries := getFilteredFstabEntries(t, imageConnection)
assert.Equal(t, expectedFstabEntries, filteredFstabEntries)

ukiFiles, err := getUkiFiles(espPath)
if !assert.NoError(t, err) {
return nil, false
}

ukiFilesChecksums := calculateUkiFileChecksums(t, ukiFiles)
if ukiFilesChecksums == nil {
return nil, false
}

if expectedUkiFilesChecksums != nil {
Expand All @@ -186,3 +333,18 @@ func verifyUsrVerity(t *testing.T, buildDir string, imagePath string, expectedUk

return ukiFilesChecksums, true
}

// calculateUkiFileChecksums generates SHA256 checksums for a list of UKI files.
// Returns nil if any error occurs during checksum calculation.
func calculateUkiFileChecksums(t *testing.T, ukiFiles []string) map[string]string {
ukiFilesChecksums := make(map[string]string)
for _, ukiFile := range ukiFiles {
checksum, err := file.GenerateSHA256(ukiFile)
if !assert.NoError(t, err) {
return nil
}
ukiFilesChecksums[ukiFile] = checksum
}

return ukiFilesChecksums
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
previewFeatures:
- reinitialize-verity

storage:
reinitializeVerity: none

os:
additionalFiles:
- content: |
cat, dog, elephant, squirrel
destination: /var/animals.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
previewFeatures:
- uki

storage:
bootType: efi
disks:
- partitionTableType: gpt
partitions:
- id: esp
type: esp
size: 250M

- id: boot
size: 250M

- id: root
size: 1G

- id: roothash
size: 100M

- id: var
size: 1G

verity:
- id: verityroot
name: root
dataDeviceId: root
hashDeviceId: roothash
corruptionOption: panic

filesystems:
- deviceId: esp
type: fat32
mountPoint:
path: /boot/efi
options: umask=0077

- deviceId: boot
type: ext4
mountPoint: /boot

- deviceId: verityroot
type: ext4
mountPoint:
path: /
options: ro

- deviceId: var
type: ext4
mountPoint: /var

os:
bootloader:
resetType: hard-reset

kernelCommandLine:
extraCommandLine:
- rd.info

uki:
kernels: auto

packages:
remove:
- grub2-efi-binary

install:
- openssh-server
- veritysetup
- systemd-boot
- device-mapper

scripts:
postCustomization:
- content: |
set -eux

# Move SSH host keys off of the read-only / partition to /var
mkdir -p /var/ssh
ln -sr /var/ssh /etc/ssh
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ os:
- systemd-boot
- device-mapper

users:
- name: root
password:
type: plain-text
value: hello

scripts:
postCustomization:
- content: |
Expand Down
Loading