Skip to content

Commit c2eed44

Browse files
authored
Add support for reinitializing verity. (#231)
Add support for reading the existing verity settings from the base image and then reapplying them after OS customization.
1 parent c419e78 commit c2eed44

13 files changed

Lines changed: 358 additions & 121 deletions

File tree

docs/imagecustomizer/api/cli.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ But it can also be an Azure Linux image that has been customized.
3131

3232
Supported image file formats: vhd, vhdx, qcow2, and raw.
3333

34+
If verity is enabled in the base image, then:
35+
36+
- If the partitions are recustomized using the
37+
[disks](../api/configuration/storage.md#disks-disk) API, then the existing verity
38+
settings are thrown away.
39+
New verity settings can be configured with the
40+
[verity](../api/configuration/verity.md) API.
41+
42+
- Otherwise, the existing verity settings are reapplied to the image after OS
43+
customization.
44+
45+
This feature is in preview and may be subject to breaking changes.
46+
You may enable this feature by adding `reinitialize-verity` to the
47+
[previewfeatures](./configuration/config.md#previewfeatures-string) API.
48+
3449
Added in v0.3.
3550

3651
## --output-image-file=FILE-PATH

docs/imagecustomizer/api/configuration/config.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ Supported options:
9999

100100
Added in v0.14.
101101

102+
- `reinitialize-verity`: Enables support for customizing an image that has verity
103+
enabled (without needing to recustomize the partitions). The verity settings are read
104+
from the image and reapplied after OS customization.
105+
106+
Added in v0.15.
107+
102108
## output [[output](./output.md)]
103109

104110
Specifies the configuration for the output image and artifacts.

docs/imagecustomizer/api/configuration/inputImage.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,19 @@ But it can also be an Azure Linux image that has been customized.
2626

2727
Supported image file formats: vhd, vhdx, qcow2, and raw.
2828

29+
If verity is enabled in the base image, then:
30+
31+
- If the partitions are recustomized using the
32+
[disks](storage.md#disks-disk) API, then the existing verity
33+
settings are thrown away.
34+
New verity settings can be configured with the
35+
[verity](verity.md) API.
36+
37+
- Otherwise, the existing verity settings are reapplied to the image after OS
38+
customization.
39+
40+
This feature is in preview and may be subject to breaking changes.
41+
You may enable this feature by adding `reinitialize-verity` to the
42+
[previewfeatures](config.md#previewfeatures-string) API.
43+
2944
Added in v0.13.0.

toolkit/tools/imagecustomizerapi/previewfeaturetype.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const (
1616

1717
// PreviewFeatureInjectFiles enables files injection into target partitions.
1818
PreviewFeatureInjectFiles PreviewFeature = "inject-files"
19+
20+
// PreviewFeatureReinitializeVerity will reinitialize verity on verity partitions in the base image.
21+
PreviewFeatureReinitializeVerity = "reinitialize-verity"
1922
)
2023

2124
func (pf PreviewFeature) IsValid() error {

toolkit/tools/pkg/imagecustomizerlib/customizepartitionsfilecopy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
func customizePartitionsUsingFileCopy(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config,
1717
buildImageFile string, newBuildImageFile string,
1818
) (map[string]string, error) {
19-
existingImageConnection, _, err := connectToExistingImage(buildImageFile, buildDir, "imageroot", false)
19+
existingImageConnection, _, _, err := connectToExistingImage(buildImageFile, buildDir, "imageroot", false)
2020
if err != nil {
2121
return nil, err
2222
}

toolkit/tools/pkg/imagecustomizerlib/customizepartitionsuuids.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ func resetFileSystemUuid(partition diskutils.PartitionInfo) (string, error) {
124124
newUuid = strings.ToUpper(newUuid)
125125
newUuid = newUuid[:4] + "-" + newUuid[4:]
126126

127+
case "DM_verity_hash":
128+
// Resetting partition IDs on a disk with verity would require updating the kernel command-line args.
129+
// This is probably doable, just not implemented yet.
130+
return "", fmt.Errorf("resetting partition IDs on a verity-enabled image is not implemented")
131+
127132
default:
128133
return "", fmt.Errorf("unsupported filesystem type (%s)", partition.FileSystemType)
129134
}
@@ -174,7 +179,7 @@ func fixPartitionUuidsInFstabFile(partitions []diskutils.PartitionInfo, newUuids
174179
// Find the partition.
175180
// Note: The 'partitions' list was collected before all the changes were made. So, the fstab entries will still
176181
// match the values in the `partitions` list.
177-
mountIdType, _, partitionIndex, err := findSourcePartition(fstabEntry.Source, partitions, buildDir)
182+
mountIdType, _, partitionIndex, _, err := findSourcePartition(fstabEntry.Source, partitions, buildDir)
178183
if err != nil {
179184
return err
180185
}

toolkit/tools/pkg/imagecustomizerlib/customizeverity.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"path/filepath"
99
"slices"
10+
"strings"
1011

1112
"github.com/microsoft/azurelinux/toolkit/tools/imagecustomizerapi"
1213
"github.com/microsoft/azurelinux/toolkit/tools/imagegen/diskutils"
@@ -297,6 +298,32 @@ func SystemdFormatCorruptionOption(corruptionOption imagecustomizerapi.Corruptio
297298
}
298299
}
299300

301+
func parseSystemdVerityOptions(options string) (imagecustomizerapi.CorruptionOption, error) {
302+
corruptionOption := imagecustomizerapi.CorruptionOptionIoError
303+
304+
optionValues := strings.Split(options, ",")
305+
for _, option := range optionValues {
306+
switch option {
307+
case "":
308+
// Ignore empty string.
309+
310+
case "ignore-corruption":
311+
corruptionOption = imagecustomizerapi.CorruptionOptionIgnore
312+
313+
case "panic-on-corruption":
314+
corruptionOption = imagecustomizerapi.CorruptionOptionPanic
315+
316+
case "restart-on-corruption":
317+
corruptionOption = imagecustomizerapi.CorruptionOptionRestart
318+
319+
default:
320+
return "", fmt.Errorf("unknown verity option (%s)", option)
321+
}
322+
}
323+
324+
return corruptionOption, nil
325+
}
326+
300327
func validateVerityDependencies(imageChroot *safechroot.Chroot) error {
301328
requiredRpms := []string{"lvm2"}
302329

toolkit/tools/pkg/imagecustomizerlib/customizeverity_test.go

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func testCustomizeImageVerityHelper(t *testing.T, testName string, imageType bas
4747
return
4848
}
4949

50-
verityRootVerity(t, imageType, imageVersion, buildDir, outImageFilePath)
50+
verifyRootVerity(t, imageType, imageVersion, buildDir, outImageFilePath)
5151

5252
// Recustomize the image.
5353
err = CustomizeImageWithConfigFile(buildDir, configFile, outImageFilePath, nil, outImageFilePath, "raw",
@@ -56,10 +56,10 @@ func testCustomizeImageVerityHelper(t *testing.T, testName string, imageType bas
5656
return
5757
}
5858

59-
verityRootVerity(t, imageType, imageVersion, buildDir, outImageFilePath)
59+
verifyRootVerity(t, imageType, imageVersion, buildDir, outImageFilePath)
6060
}
6161

62-
func verityRootVerity(t *testing.T, imageType baseImageType, imageVersion baseImageVersion, buildDir string,
62+
func verifyRootVerity(t *testing.T, imageType baseImageType, imageVersion baseImageVersion, buildDir string,
6363
outImageFilePath string,
6464
) {
6565
// Connect to customized image.
@@ -449,3 +449,83 @@ func testCustomizeImageVerityUsr2StageHelper(t *testing.T, testName string, imag
449449

450450
verityUsrVerity(t, imageType, imageVersion, buildDir, stage2FilePath, "panic-on-corruption")
451451
}
452+
453+
func TestCustomizeImageVerityReinitRoot(t *testing.T) {
454+
for _, version := range supportedAzureLinuxVersions {
455+
t.Run(string(version), func(t *testing.T) {
456+
testCustomizeImageVerityReinitRootHelper(t, "TestCustomizeImageVerityReinitRoot"+string(version),
457+
baseImageTypeCoreEfi, version)
458+
})
459+
}
460+
}
461+
462+
func testCustomizeImageVerityReinitRootHelper(t *testing.T, testName string, imageType baseImageType,
463+
imageVersion baseImageVersion,
464+
) {
465+
baseImage := checkSkipForCustomizeImage(t, imageType, imageVersion)
466+
467+
testTempDir := filepath.Join(tmpDir, testName)
468+
buildDir := filepath.Join(testTempDir, "build")
469+
stage1ConfigFile := filepath.Join(testDir, "verity-config.yaml")
470+
stage2ConfigFile := filepath.Join(testDir, "verity-reinit.yaml")
471+
stage1FilePath := filepath.Join(testTempDir, "image.raw")
472+
stage2FilePath := filepath.Join(testTempDir, "image.raw")
473+
474+
// Stage 1: Initialize verity.
475+
err := CustomizeImageWithConfigFile(buildDir, stage1ConfigFile, baseImage, nil, stage1FilePath, "raw",
476+
"" /*outputPXEArtifactsDir*/, true /*useBaseImageRpmRepos*/)
477+
if !assert.NoError(t, err) {
478+
return
479+
}
480+
481+
verifyRootVerity(t, imageType, imageVersion, buildDir, stage2FilePath)
482+
483+
// Stage 2: Reinitialize verity.
484+
err = CustomizeImageWithConfigFile(buildDir, stage2ConfigFile, stage1FilePath, nil, stage2FilePath, "raw",
485+
"" /*outputPXEArtifactsDir*/, true /*useBaseImageRpmRepos*/)
486+
if !assert.NoError(t, err) {
487+
return
488+
}
489+
490+
verifyRootVerity(t, imageType, imageVersion, buildDir, stage2FilePath)
491+
}
492+
493+
func TestCustomizeImageVerityReinitUsr(t *testing.T) {
494+
for _, version := range supportedAzureLinuxVersions {
495+
t.Run(string(version), func(t *testing.T) {
496+
testCustomizeImageVerityReinitUsrHelper(t, "TestCustomizeImageVerityReinitUsr"+string(version),
497+
baseImageTypeCoreEfi, version)
498+
})
499+
}
500+
}
501+
502+
func testCustomizeImageVerityReinitUsrHelper(t *testing.T, testName string, imageType baseImageType,
503+
imageVersion baseImageVersion,
504+
) {
505+
baseImage := checkSkipForCustomizeImage(t, imageType, imageVersion)
506+
507+
testTempDir := filepath.Join(tmpDir, testName)
508+
buildDir := filepath.Join(testTempDir, "build")
509+
stage1ConfigFile := filepath.Join(testDir, "verity-usr-config.yaml")
510+
stage2ConfigFile := filepath.Join(testDir, "verity-reinit.yaml")
511+
stage1FilePath := filepath.Join(testTempDir, "image.raw")
512+
stage2FilePath := filepath.Join(testTempDir, "image.raw")
513+
514+
// Stage 1: Initialize verity.
515+
err := CustomizeImageWithConfigFile(buildDir, stage1ConfigFile, baseImage, nil, stage1FilePath, "raw",
516+
"" /*outputPXEArtifactsDir*/, true /*useBaseImageRpmRepos*/)
517+
if !assert.NoError(t, err) {
518+
return
519+
}
520+
521+
verityUsrVerity(t, imageType, imageVersion, buildDir, stage1FilePath, "")
522+
523+
// Stage 2: Reinitialize verity.
524+
err = CustomizeImageWithConfigFile(buildDir, stage2ConfigFile, stage1FilePath, nil, stage2FilePath, "raw",
525+
"" /*outputPXEArtifactsDir*/, true /*useBaseImageRpmRepos*/)
526+
if !assert.NoError(t, err) {
527+
return
528+
}
529+
530+
verityUsrVerity(t, imageType, imageVersion, buildDir, stage2FilePath, "")
531+
}

0 commit comments

Comments
 (0)