From 6fc6f78214a9749e5f2640f1b648ac9a8761109b Mon Sep 17 00:00:00 2001 From: Daniel McIlvaney Date: Tue, 2 Jul 2024 14:16:57 -0700 Subject: [PATCH 1/2] docs --- toolkit/docs/formats/imageconfig.md | 57 +----------- toolkit/docs/security/intro.md | 2 - toolkit/docs/security/read-only-roots.md | 109 +---------------------- 3 files changed, 6 insertions(+), 162 deletions(-) diff --git a/toolkit/docs/formats/imageconfig.md b/toolkit/docs/formats/imageconfig.md index bbec548998f..fa087d9de29 100644 --- a/toolkit/docs/formats/imageconfig.md +++ b/toolkit/docs/formats/imageconfig.md @@ -83,7 +83,7 @@ Sample partitions entry, specifying a boot partition and a root partition: - `grub` indicates this is a grub boot partition - `bios_grub` indicates this is a bios grub boot partition - `boot` indicates this is a boot partition -- `dmroot` indicates this partition will be used for a device mapper root device (i.e. `Encryption` or `ReadOnlyVerityRoot`) +- `dmroot` indicates this partition will be used for a device mapper root device (i.e. `Encryption`) #### TypeUUID @@ -259,7 +259,7 @@ The `OverrideRpmLocales` and `DisableRpmDocs` settings are stored in `/usr/lib/r The tools offer the option of executing arbitrary shell scripts during various points of the image generation process. There are three points that scripts can be executed: `PreInstall`, `PostInstall`, and `ImageFinalize`. ->Installer starts -> `PreInstallScripts` -> Create Partitions -> Install Packages -> `PostInstallScripts` -> Configure Bootloader (if any) -> Calculate dm-verity hashes (if configured) -> `ImageFinalizeScripts` +>Installer starts -> `PreInstallScripts` -> Create Partitions -> Install Packages -> `PostInstallScripts` -> Configure Bootloader (if any) -> `ImageFinalizeScripts` Each of the `PreInstallScripts`, `PostInstallScripts`, and `FinalizeImageScripts` entires are an array of file paths and the corresponding input arguments. The scripts will be executed in sequential order and within the context of the final image. The file paths are relative to the image configuration file. Scripts may be passed without arguments if desired. @@ -416,58 +416,9 @@ A sample KernelOptions specifying a default kernel: ### ReadOnlyVerityRoot -"ReadOnlyVerityRoot" key controls making the root filesystem read-only using dm-verity. -It will create a verity disk from the partition mounted at "/". The verity data is stored as -part of the image's initramfs. More details can be found in [Misc: Read Only Roots](../how_it_works/5_misc.md#dm-verity-read-only-roots) +ReadOnlyVerityRoot has been deprecated in the image configuration. The feature is suppored in the [Azure Linux Image Customizer](../../tools/imagecustomizer/README.md) tool now. -#### Considerations - -Having a read-only root filesystem will change the behavior of the image in some fundamental ways. There are several areas that should be considered before enabling a read-only root: - -##### Writable Data - -Any writable data which needs to be preserved will need to be stored into a separate writable partition. The `TmpfsOverlays` key will create throw-away writable partitions which are reset on every boot. The example configs create an overlay on `/var`, but the more refined the overlays are, the more secure they will be. - -##### GPL Licensing - -If using a read-only root in conjunction with a verified boot flow that uses a signed initramfs, carefully consider the implications on GPLv3 code. The read-only nature of the filesystem means a user cannot replace GPLv3 components without re-signing a new initramfs. - -##### Users - -Since users are controlled by files in `/etc`, these files are read-only when this is set. It is recommended to either use SSH key based login or pre-hash the password to avoid storing passwords in plain text in the config files (See [Users](#users)). - -##### Separate `/boot` Partition - -Since the root partition's hash tree is stored as part of the initramfs, the initramfs cannot be stored on the same root partition (it would invalidate the measurements). To avoid this a separate `/boot` partition is needed to house the hash tree (via the initramfs). - -##### ISO - -The ISO command line installer supports enabling read-only roots if they are configured through the configuration JSON file (see [full.json's](../../imageconfigs/full.json) `"Azure Linux Core Read-Only"` entry). The automatic partition creation mode will create the required `/boot` partition if the read-only root is enabled. - -The GUI installer does not currently support read-only roots. - -- `Enable`: Enable dm-verity on the root filesystem -- `Name`: Custom name for the mounted root (default is `"verity_root_fs"`) -- `ErrorCorrectionEnable`: Enable automatic error correction of modified blocks (default is `true`) -- `ErrorCorrectionEncodingRoots`: Increase overhead to increase resiliency of the forward error correction (default is `2` bytes of code per 255 bytes of data) -- `RootHashSignatureEnable`: Validate the root hash against a key stored in the kernel's system keyring. The signature file should be called `.p7` and must be stored in the initramfs. This signature WILL NOT BE included automatically in the initramfs. It must be included via an out of band build step. -- `ValidateOnBoot`: Run a validation of the full disk at boot time, normally blocks are validated only as needed. This can take several minutes if the disk is corrupted. -- `VerityErrorBehavior`: Indicate additional special system behavior when encountering an unrecoverable verity corruption. One of `"ignore"`, `"restart"`, `"panic"`. Normal behavior is to return an IO error when reading corrupt blocks. -- `TmpfsOverlays`: Mount these paths as writable overlays backed by a tmpfs in memory. -- `TmpfsOverlaySize`: Maximum amount of memory the overlays may use. Maybe be one of three forms: `"1234"`, `"1234[k,m,g]"`, `"20%"` (default is `"20%"`) -- `TmpfsOverlayDebugEnabled`: Make the tmpfs overlay mounts easily accessible for debugging purposes. They can be found in /mnt/verity_overlay_debug_tmpfs. Include the - `verity-read-only-root-debug-tools` package to create the required mount points. - -A sample ReadOnlyVerityRoot specifying a basic read-only root using default error correction. This configuration may be used for both normal images and ISO configurations: - -``` json -"ReadOnlyVerityRoot": { - "Enable": true, - "TmpfsOverlays": [ - "/var" - ], -}, -``` +See [Azure Linux Image Customizer configuration](../../tools/imagecustomizer/docs/configuration.md#verity-type) for more information. ### KernelCommandLine diff --git a/toolkit/docs/security/intro.md b/toolkit/docs/security/intro.md index 8f57d1a6ae5..12d19f97d0b 100644 --- a/toolkit/docs/security/intro.md +++ b/toolkit/docs/security/intro.md @@ -4,5 +4,3 @@ Below topics are dedicated to security-related details of the operating system. ## 1. [Security features](security-features.md) ## 2. [SSL CA certificates management](ca-certificates.md) - -## 3. [Read-Only Roots](read-only-roots.md) diff --git a/toolkit/docs/security/read-only-roots.md b/toolkit/docs/security/read-only-roots.md index baa24030d71..166aa71cb73 100644 --- a/toolkit/docs/security/read-only-roots.md +++ b/toolkit/docs/security/read-only-roots.md @@ -1,111 +1,6 @@ Read-Only Roots === -- [dm-verity Read Only Roots](#dm-verity-read-only-roots) - - [Creating Verity Disks](#Creating-Verity-Disks) - - [Writable Areas](#Writable-Areas) - - [Root of Trust](#Root-of-Trust) - - [Users, Machine ID, Misc. Configuration](#Users-Machine-ID-Misc-Configuration) - - [Forward Error Correction (FEC)](#Forward-Error-Correction-(FEC)) - - [Hash Tree and FEC Overhead](#Hash-Tree-and-FEC-Overhead) - - [Debugging](#Debugging) - - [ISO Installers](#ISO-Installers) +ReadOnlyVerityRoot has been deprecated in the image configuration. The feature is suppored in the [Azure Linux Image Customizer](../../tools/imagecustomizer/README.md) tool now. -## dm-verity Read Only Roots -`dm-verity` is a Device Mapper target which creates read-only partitions with built-in integrity checking. This means a verity partition can detect (and if FEC is enabled, fix) changes to the data in the partition. - -A Merkle hash tree is created from the measurements of each block in the partition (Hash each 4k block, then hash those hashes. Repeat until you have a single hash). The root node of the tree, a single hash, is called the `root hash`. - -When data is read from disk the blocks are hashed and checked against the hash tree to detect any changes. Any change to a block will modify its branch of the tree, all the way down to the root hash. By securing the root hash the entire partition can be secured. - -### Creating Verity Disks -Read-only roots can be enabled using the `"ReadOnlyVerityRoot"` key in the image configuration JSON file (See [imageconfig.md](../formats/imageconfig.md#ReadOnlyVerityRoot) for details) -Since the read-only partition becomes unmodifiable as soon as it's measured, the snapshot must be taken at the very end of the build process. The `imager` tool switches the root to read-only mode, then uses the `veritysetup` tool to measure the root into a hash tree, FEC data, and compute the root hash. These files are then placed into the `initramfs`. - -Any non-standard modifications to the image (adding symlinks, generating custom machine IDs, changing configs, etc.) need to be made prior to this point by using the `"PostInstallScripts"` key in the image config. - -### Writable Areas -The build system currently supports two types of writable areas which are useful for read-only verity disks: - -#### Temporary Overlays -Passing a list of directories using the `"TmpfsOverlays"` key in the `"ReadOnlyVerityRoot"` config will cause the initramfs to add `tmpfs` based writable overlays to each listed directory. These overlays are backed in RAM and will be reset after a reboot. These are useful for logging, or other transient data that is not security critical. Ideally the overlays should be kept as small as possible to minimize the number of files which may be maliciously modified at run time. The example configurations mount `/var` under a single overlay, but a more targeted approach is desirable. By default the overlays will consume at most 20% of the available memory. This can be configured using the `"TmpfsOverlaysSize"` key. - -#### Writable Mount Points -Traditional mount points still function as expected. A writable data partition can be mounted using the normal configuration keys if desired. - -### Root of Trust -The root of trust for a verity partition is the root hash. If this single hash is trusted, the entire hash tree, and subsequently the root partition, can also be trusted. - -The root hash may be protected in three ways: -- The file itself is stored in a safe location (signed `initramfs`) -- The root hash is accompanied by a signature file which is validated against the kernel key ring -- The root hash is passed on the kernel's command line in a secure manner - -The `imager` tool automatically places the root hash and hash tree in the initramfs and sets the kernel command line in the `grub.cfg` file to use the verity files. This means that both the grub configuration files and the initramfs must be part of the verified boot chain to fully tie the verity root filesystem into the chain of trust. - -### Users, Machine ID, Misc. Configuration -Files in `/etc` such as the `passwd` and `machine-id` files are also part of the read-only filesystem, making them unmodifiable. If the machine-id is blank on system start, and the file is read-only, the systemd `systemd-machine-id-setup` command will create a tmpfs backed machine-id which will be different for every boot. If a stable `machine-id` is desired this file will need to be writable. Consider bind mounting the file early using the `x-initrd.mount` mount option and adding it to the `fstab` with `"PostInstallScripts"`. - -Ideally as much of `/etc` as possible should be left read-only to avoid miss-configuration. - -### Forward Error Correction (FEC) -Verity supports error correction which will return the original data even if the underlying blocks have been modified (it does not restore the underlying data stored on disk however). FEC incurs some overhead but the hash tree, which is a sunk cost, makes it much more effective than normal Reed-Solomon codes. For a 2GiB disk, FEC with 2 roots (i.e. 2 bytes of error correction codes per 255 bytes of real data) can correct ~16MiB of errors with ~16MiB of overhead. See [veritydisk.go](../../tools/imagegen/configuration/veritydisk.go) for calculation details. - -### Hash Tree and FEC Overhead -The extra data required for verity needs to be stored outside the measured partition. In the case of Azure Linux it is stored in the initramfs. Assuming the Merkle tree is a full m-ary tree with m=128 (128 branches per node, from `4k/sizeof(sha256)`), the size of the Merkle tree is: -``` -blockSize = 4k -m = blockSize / sizeof(sha256) -leafNodes = diskSize/blockSize -totalNodes = ceil((leafNodes*m - 1) / (m - 1)) -totalTreeSize = totalNodes * sizeof(sha256) -``` - -The FEC data grows linearly with the disk size. By default two bytes of error correction data are used for every 255 bytes of actual data. This gives: -``` -fecBytesPerBlock = 2 -fecBlockSize = 255 -fecDataSize = ceil(diskSize / fecBlockSize) * fecBytesPerBlock -``` - -#### Examples -For a 2GiB root disk we would expect: -``` -leafNodes = 2GiB / 4KiB = 524288 -totalTreeSize = ceil((524288*128 - 1) / (128 - 1)) * 256 = 135274752 bits = 16.13 MiB - -fecDataSize = ceil(2GiB / 255 bits) * 2 bits = 134700000 = 16.06 MiB - -totalOverhead = 32.19 MiB (1.5% overhead) -``` -For a 20GiB root disk we would expect: -``` -totalTreeSize = 161.3 MiB - -fecDataSize = 160.6 MiB - -totalOverhead = 321.9 MiB (1.6% overhead) -``` - -### Debugging -The read-only nature of these images can make debugging quite challenging. -#### Serial Debugging -Adding the following config will enable serial output (useful for both hardware and VM images), and will set `dracut` (The package which runs in the initramfs) to log its output and drop to an interactive shell in the event of a failure. -```json -"KernelCommandLine": { - "ExtraCommandLine": "console=tty0 console=ttyS0=9800 rd.debug rd.shell=1" -} -``` - -#### Overlay Debugging -If the `verity-read-only-root-debug-tools` package is included in the image, and the `"TmpfsOverlayDebugEnabled"` key is set to `true`, a read-only copy of the overlay upper directories (which will show the modified files for each overlay) will be visible under `/mnt/verity_overlay_debug_tmpfs`. - -#### Error Handling Debugging -The `verity-read-only-root-debug-tools` package also includes the `/mnt/create_linear_mount.sh` script which will freeze the verity root and make a new, writable, mount at `/mnt/verity_writable_debug`. Interacting with the system while the root is frozen can be unstable however, so proceed with caution. - -Once the writable mount is created it is possible to write data back to the underlying disk. If the root partition is unfrozen again the changes will begin to be detected. Most changes will not show since the view of the disk is cached, but any changes to uncached files should trigger the error behavior. If FEC is enabled errors will simply be fixed until enough blocks are corrupted. The FEC correction is visible in the `dmesg` log. - -If enough blocks are corrupted the system will eventually be unable to recover them with FEC and the error handling behavior will trigger. The default behavior is to simply return an IO error, but other options are available by setting the `"VerityErrorBehavior"` key in the configuration file. - -## ISO Installers -If a configuration used to create an ISO has a read-only root configured, the ISO installer will honor that configuration. (See `full.json`'s `"Azure Linux Core Read-Only"` entry). +See [Azure Linux Image Customizer configuration](../../tools/imagecustomizer/docs/configuration.md#verity-type) for more information. From ae6b249647a4a43934032daab44a063df75f0412 Mon Sep 17 00:00:00 2001 From: Daniel McIlvaney Date: Tue, 2 Jul 2024 14:20:45 -0700 Subject: [PATCH 2/2] Remove from tools --- .../imageconfigvalidator.go | 26 +- .../attendedinstaller/_manualrun/manualrun.go | 39 --- .../attendedinstaller/uitext/uitext.go | 3 - .../autopartitionwidget.go | 58 ---- .../manualpartitionwidget.go | 9 - .../views/encryptview/encryptview.go | 5 - .../installationview/installationview.go | 1 - .../imagegen/configuration/configuration.go | 10 +- .../configuration/configuration_test.go | 24 +- .../imagegen/configuration/systemconfig.go | 17 +- .../testdata/test_configuration.json | 3 - .../imagegen/configuration/veritydisk.go | 109 ------ .../configuration/verityerrorbehavior.go | 67 ---- .../configuration/verityerrorbehavior_test.go | 78 ----- toolkit/tools/imagegen/diskutils/diskutils.go | 17 +- toolkit/tools/imagegen/diskutils/initramfs.go | 249 -------------- toolkit/tools/imagegen/diskutils/verity.go | 316 ------------------ .../imagegen/installutils/installutils.go | 97 +----- toolkit/tools/imager/imager.go | 73 +--- .../internal/resources/assets/grub2/grub | 6 +- .../internal/resources/assets/grub2/grub.cfg | 4 +- .../internal/safemount/safemount_test.go | 4 +- .../pkg/imagecustomizerlib/imageutils.go | 8 +- 23 files changed, 66 insertions(+), 1157 deletions(-) delete mode 100644 toolkit/tools/imagegen/configuration/veritydisk.go delete mode 100644 toolkit/tools/imagegen/configuration/verityerrorbehavior.go delete mode 100644 toolkit/tools/imagegen/configuration/verityerrorbehavior_test.go delete mode 100644 toolkit/tools/imagegen/diskutils/initramfs.go delete mode 100644 toolkit/tools/imagegen/diskutils/verity.go diff --git a/toolkit/tools/imageconfigvalidator/imageconfigvalidator.go b/toolkit/tools/imageconfigvalidator/imageconfigvalidator.go index 99a72cd1238..4a6a0cbd654 100644 --- a/toolkit/tools/imageconfigvalidator/imageconfigvalidator.go +++ b/toolkit/tools/imageconfigvalidator/imageconfigvalidator.go @@ -113,12 +113,10 @@ func validatePackages(config configuration.Config) (err error) { defer timestamp.StopEvent(nil) const ( - validateError = "failed to validate package lists in config" - verityPkgName = "verity-read-only-root" - verityDebugPkgName = "verity-read-only-root-debug-tools" - dracutFipsPkgName = "dracut-fips" - fipsKernelCmdLine = "fips=1" - userAddPkgName = "shadow-utils" + validateError = "failed to validate package lists in config" + dracutFipsPkgName = "dracut-fips" + fipsKernelCmdLine = "fips=1" + userAddPkgName = "shadow-utils" ) for _, systemConfig := range config.SystemConfigs { @@ -127,8 +125,6 @@ func validatePackages(config configuration.Config) (err error) { return fmt.Errorf("%s: %w", validateError, err) } foundSELinuxPackage := false - foundVerityInitramfsPackage := false - foundVerityInitramfsDebugPackage := false foundDracutFipsPackage := false foundUserAddPackage := false kernelCmdLineString := systemConfig.KernelCommandLine.ExtraCommandLine @@ -141,12 +137,6 @@ func validatePackages(config configuration.Config) (err error) { if pkg == "kernel" { return fmt.Errorf("%s: kernel should not be included in a package list, add via config file's [KernelOptions] entry", validateError) } - if pkg == verityPkgName { - foundVerityInitramfsPackage = true - } - if pkg == verityDebugPkgName { - foundVerityInitramfsDebugPackage = true - } if pkg == dracutFipsPkgName { foundDracutFipsPackage = true } @@ -157,14 +147,6 @@ func validatePackages(config configuration.Config) (err error) { foundUserAddPackage = true } } - if systemConfig.ReadOnlyVerityRoot.Enable { - if !foundVerityInitramfsPackage { - return fmt.Errorf("%s: [ReadOnlyVerityRoot] selected, but '%s' package is not included in the package lists", validateError, verityPkgName) - } - if systemConfig.ReadOnlyVerityRoot.TmpfsOverlayDebugEnabled && !foundVerityInitramfsDebugPackage { - return fmt.Errorf("%s: [ReadOnlyVerityRoot] and [TmpfsOverlayDebugEnabled] selected, but '%s' package is not included in the package lists", validateError, verityDebugPkgName) - } - } if strings.Contains(kernelCmdLineString, fipsKernelCmdLine) || systemConfig.KernelCommandLine.EnableFIPS { if !foundDracutFipsPackage { return fmt.Errorf("%s: 'fips=1' provided on kernel cmdline, but '%s' package is not included in the package lists", validateError, dracutFipsPkgName) diff --git a/toolkit/tools/imagegen/attendedinstaller/_manualrun/manualrun.go b/toolkit/tools/imagegen/attendedinstaller/_manualrun/manualrun.go index 30294be5930..823669beffa 100644 --- a/toolkit/tools/imagegen/attendedinstaller/_manualrun/manualrun.go +++ b/toolkit/tools/imagegen/attendedinstaller/_manualrun/manualrun.go @@ -23,11 +23,6 @@ func main() { logger.InitStderrLog() - // The JSON parser if responsible for filling defaults, since we are - // using a Go struct need to manually use the defaults. - verityConfig := configuration.GetDefaultReadOnlyVerityRoot() - verityConfig.Enable = true - baseCfg := configuration.Config{ SystemConfigs: []configuration.SystemConfig{ configuration.SystemConfig{ @@ -95,40 +90,6 @@ func main() { }, }, }, - configuration.SystemConfig{ - Name: "Read-Only", - PackageLists: []string{ - "packagelists/core-packages-image.json", - "packagelists/hyperv-packages.json", - "packagelists/read-only-root-packages.json", - }, - KernelOptions: map[string]string{ - "default": "kernel", - }, - AdditionalFiles: map[string]string{ - "/etc/resolv.conf": "/etc/resolv.conf", - "/root/.bashrc": "/root/.bashrc", - }, - ReadOnlyVerityRoot: verityConfig, - PostInstallScripts: []configuration.InstallScript{ - configuration.InstallScript{ - Path: "arglessScript.sh", - }, - configuration.InstallScript{ - Path: "thisOneNeedsArguments.sh", - Args: "--input abc --output cba", - }, - }, - FinalizeImageScripts: []configuration.InstallScript{ - configuration.InstallScript{ - Path: "arglessScript.sh", - }, - configuration.InstallScript{ - Path: "thisOneNeedsArguments.sh", - Args: "--input abc --output cba", - }, - }, - }, }, } diff --git a/toolkit/tools/imagegen/attendedinstaller/uitext/uitext.go b/toolkit/tools/imagegen/attendedinstaller/uitext/uitext.go index 5062839a236..0b307cd0251 100644 --- a/toolkit/tools/imagegen/attendedinstaller/uitext/uitext.go +++ b/toolkit/tools/imagegen/attendedinstaller/uitext/uitext.go @@ -69,7 +69,6 @@ const ( // Errors InvalidBootPartitionErrorFmt = "Invalid boot partition: first partition must be of type '%s'" InvalidRootPartitionErrorFmt = "Must specify a partition to have the mount point '%s'" - InvalidRootDeviceMapperError = "Must have a root to use with device mapper roots (Encryption, Read-Only)" InvalidRootPartitionErrorFormatFmt = "Root partition cannot be %s" MountPointAlreadyInUseError = "Mount point is already in use" MountPointStartError = "Mount point must start with `/`" @@ -137,8 +136,6 @@ const ( PasswordMismatchFeedback = "Passwords do not match" EnumNavigationFeedback = "Use left or right arrow keys to change the selection" - EncryptionVerityIncompatible = "Encrypted and read-only roots are currently incompatible with each other" - UserNameEmptyError = "user name cannot be empty" UserNameInvalidRuneError = "user name should only contain alpha-numeric and '-', '.' or '_' characters" UserNameInvalidStartError = "user name should start with an alpha-numeric character" diff --git a/toolkit/tools/imagegen/attendedinstaller/views/diskview/autopartitionwidget/autopartitionwidget.go b/toolkit/tools/imagegen/attendedinstaller/views/diskview/autopartitionwidget/autopartitionwidget.go index df0600c619d..7ac2ca7bff2 100644 --- a/toolkit/tools/imagegen/attendedinstaller/views/diskview/autopartitionwidget/autopartitionwidget.go +++ b/toolkit/tools/imagegen/attendedinstaller/views/diskview/autopartitionwidget/autopartitionwidget.go @@ -5,7 +5,6 @@ package autopartitionwidget import ( "fmt" - "math" "github.com/gdamore/tcell" "github.com/rivo/tview" @@ -138,11 +137,6 @@ func (ap *AutoPartitionWidget) mustUpdateConfiguration(sysConfig *configuration. bootPartitionStartMiB = 1 bootPartitionEndMiB = 9 - bootDirPartitionName = "boot" - bootDirPartitionFsType = "ext4" - bootDirPartitionStartMiB = bootPartitionEndMiB - bootDirMountPoint = "/boot" - rootPartitionName = "rootfs" rootFsType = "ext4" rootMountPoint = "/" @@ -185,58 +179,6 @@ func (ap *AutoPartitionWidget) mustUpdateConfiguration(sysConfig *configuration. }, } - // If we are creating a read-only root filesystem we will need a space to store the hash tree. This is done in the initramfs. - // Since the measurements cannot be stored back onto the read-only disk (they would alter the measurements we just created) a - // separate location is needed. /boot is mounted as a new partition, and needs to be made large enough to contain the hash tree - // and optionally the forward error correction data. - if sysConfig.ReadOnlyVerityRoot.Enable { - const ( - hashBlockSize = 4096 - hashSize = 32 - fecBlockSize = 255 - m = hashBlockSize / hashSize - ) - // Calculate the size of the hash tree. Assume the Merkle tree is a full m-ary tree with m = 128 (4k / sizeof(sha256)) - totalSize := ap.systemDevices[ap.deviceList.GetCurrentItem()].RawDiskSize - l := math.Ceil(float64(totalSize) / hashBlockSize) - estimatedHashTreeNodes := math.Ceil((l*m - 1) / (m - 1)) - estimatedHashTreeSize := estimatedHashTreeNodes * hashSize - - bootSize := estimatedHashTreeSize - if sysConfig.ReadOnlyVerityRoot.ErrorCorrectionEnable { - // FEC overhead is linear, divide by size of FEC blocks (255 bytes), multiply by extra FEC bytes per block (default is 2) - fecBytesPerBlock := uint64(sysConfig.ReadOnlyVerityRoot.ErrorCorrectionEncodingRoots) - estimatedFECTreeSize := (totalSize / fecBlockSize) * fecBytesPerBlock - bootSize += float64(estimatedFECTreeSize) - } - - // Give some extra room to be safe - bootSize *= 1.5 - bootDirEndMiB := bootPartitionEndMiB + (uint64(bootSize) / diskutils.MiB) - - // Create the /boot partition - bootDirPartition := configuration.Partition{ - ID: bootDirPartitionName, - Name: bootDirPartitionName, - Start: bootDirPartitionStartMiB, - End: bootDirEndMiB, - FsType: bootDirPartitionFsType, - } - bootDirPartitionSetting := configuration.PartitionSetting{ - ID: bootDirPartitionName, - MountPoint: bootDirMountPoint, - MountIdentifier: configuration.MountIdentifierDefault, - } - - // Update the default root partition to move it farther down - partitions[1].Start = bootDirEndMiB - partitions[1].Flags = append(partitions[1].Flags, configuration.PartitionFlagDeviceMapperRoot) - - // Stick the new partitions in the lists - partitions = append(partitions, bootDirPartition) - partitionSettings = append(partitionSettings, bootDirPartitionSetting) - } - disk := configuration.Disk{} disk.PartitionTableType = partitionTableType disk.TargetDisk = configuration.TargetDisk{ diff --git a/toolkit/tools/imagegen/attendedinstaller/views/diskview/manualpartitionwidget/manualpartitionwidget.go b/toolkit/tools/imagegen/attendedinstaller/views/diskview/manualpartitionwidget/manualpartitionwidget.go index 4d9977d5409..df2ae33b4b4 100644 --- a/toolkit/tools/imagegen/attendedinstaller/views/diskview/manualpartitionwidget/manualpartitionwidget.go +++ b/toolkit/tools/imagegen/attendedinstaller/views/diskview/manualpartitionwidget/manualpartitionwidget.go @@ -601,15 +601,6 @@ func (mp *ManualPartitionWidget) unmarshalPartitionTable() (err error) { mp.sysConfig.PartitionSettings = partitionSettings mp.cfg.Disks = []configuration.Disk{disk} - // Flag the root for use with device mapper - if mp.sysConfig.ReadOnlyVerityRoot.Enable { - rootDiskPart := mp.cfg.GetDiskPartByID(mp.sysConfig.GetRootPartitionSetting().ID) - if rootDiskPart == nil { - return fmt.Errorf(uitext.InvalidRootDeviceMapperError) - } - rootDiskPart.Flags = append(rootDiskPart.Flags, configuration.PartitionFlagDeviceMapperRoot) - } - return } diff --git a/toolkit/tools/imagegen/attendedinstaller/views/encryptview/encryptview.go b/toolkit/tools/imagegen/attendedinstaller/views/encryptview/encryptview.go index b84655cdda1..36a344908af 100644 --- a/toolkit/tools/imagegen/attendedinstaller/views/encryptview/encryptview.go +++ b/toolkit/tools/imagegen/attendedinstaller/views/encryptview/encryptview.go @@ -153,11 +153,6 @@ func (ev *EncryptView) onNextButton(nextPage func(), cfg *configuration.Config) return } - if ev.sysConfig.ReadOnlyVerityRoot.Enable { - ev.navBar.SetUserFeedback(uitext.EncryptionVerityIncompatible, tview.Styles.TertiaryTextColor) - return - } - err := ev.passwordValidator.Check(enteredPassword) if err != nil { ev.navBar.SetUserFeedback(uiutils.ErrorToUserFeedback(err), tview.Styles.TertiaryTextColor) diff --git a/toolkit/tools/imagegen/attendedinstaller/views/installationview/installationview.go b/toolkit/tools/imagegen/attendedinstaller/views/installationview/installationview.go index fd765e0b83f..43747a7245c 100644 --- a/toolkit/tools/imagegen/attendedinstaller/views/installationview/installationview.go +++ b/toolkit/tools/imagegen/attendedinstaller/views/installationview/installationview.go @@ -148,7 +148,6 @@ func (iv *InstallationView) applyConfiguration(sysConfig *configuration.SystemCo sysConfig.PackageLists = selectedConfig.PackageLists sysConfig.KernelOptions = selectedConfig.KernelOptions sysConfig.KernelCommandLine = selectedConfig.KernelCommandLine - sysConfig.ReadOnlyVerityRoot = selectedConfig.ReadOnlyVerityRoot sysConfig.AdditionalFiles = selectedConfig.AdditionalFiles sysConfig.PostInstallScripts = selectedConfig.PostInstallScripts sysConfig.FinalizeImageScripts = selectedConfig.FinalizeImageScripts diff --git a/toolkit/tools/imagegen/configuration/configuration.go b/toolkit/tools/imagegen/configuration/configuration.go index 969aa098fa3..5ef176ca099 100644 --- a/toolkit/tools/imagegen/configuration/configuration.go +++ b/toolkit/tools/imagegen/configuration/configuration.go @@ -139,22 +139,22 @@ func GetKernelCmdLineValue(option string) (cmdlineValue string, err error) { func checkDeviceMapperFlags(config *Config) (err error) { for _, sysConfig := range config.SystemConfigs { var dmRoot *Partition - if sysConfig.ReadOnlyVerityRoot.Enable || sysConfig.Encryption.Enable { + if sysConfig.Encryption.Enable { if len(config.Disks) == 0 { - logger.Log.Warnf("[ReadOnlyVerityRoot] or [Encryption] is enabled, but no partitions are listed as part of System Config '%s'. This is only valid for ISO installers", sysConfig.Name) + logger.Log.Warnf("[Encryption] is enabled, but no partitions are listed as part of System Config '%s'. This is only valid for ISO installers", sysConfig.Name) continue } rootPartSetting := sysConfig.GetRootPartitionSetting() if rootPartSetting == nil { - return fmt.Errorf("can't find a root ('/') [PartitionSetting] to work with either [ReadOnlyVerityRoot] or [Encryption]") + return fmt.Errorf("can't find a root ('/') [PartitionSetting] to work with [Encryption]") } rootDiskPart := config.GetDiskPartByID(rootPartSetting.ID) if rootDiskPart == nil { return fmt.Errorf("can't find a [Disk] [Partition] to match with [PartitionSetting] '%s'", rootPartSetting.ID) } if !rootDiskPart.HasFlag(PartitionFlagDeviceMapperRoot) { - return fmt.Errorf("[Partition] (%s) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (%s)'s root partition since it uses [ReadOnlyVerityRoot] or [Encryption]", rootDiskPart.ID, sysConfig.Name) + return fmt.Errorf("[Partition] (%s) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (%s)'s root partition since it uses [Encryption]", rootDiskPart.ID, sysConfig.Name) } } // There is currently a limitation in diskutils.CreatePartitions() which requires us to know our device-mapper @@ -250,7 +250,7 @@ func (c *Config) IsValid() (err error) { // Check the flags for the disks err = checkDeviceMapperFlags(c) if err != nil { - return fmt.Errorf("a config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\n%w", err) + return fmt.Errorf("a config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\n%w", err) } err = checkInvalidMountIdentifiers(c) diff --git a/toolkit/tools/imagegen/configuration/configuration_test.go b/toolkit/tools/imagegen/configuration/configuration_test.go index 20d22203679..c2343297149 100644 --- a/toolkit/tools/imagegen/configuration/configuration_test.go +++ b/toolkit/tools/imagegen/configuration/configuration_test.go @@ -88,11 +88,11 @@ func TestShouldFailForUntaggedEncryptionDeviceMapperRoot(t *testing.T) { err := testConfig.IsValid() assert.Error(t, err) - assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\n[Partition] (MyRootfs) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (SmallerDisk)'s root partition since it uses [ReadOnlyVerityRoot] or [Encryption]", err.Error()) + assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\n[Partition] (MyRootfs) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (SmallerDisk)'s root partition since it uses [Encryption]", err.Error()) err = remarshalJSON(testConfig, &checkedConfig) assert.Error(t, err) - assert.Equal(t, "failed to parse [Config]:\na config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\n[Partition] (MyRootfs) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (SmallerDisk)'s root partition since it uses [ReadOnlyVerityRoot] or [Encryption]", err.Error()) + assert.Equal(t, "failed to parse [Config]:\na config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\n[Partition] (MyRootfs) must include 'dmroot' device mapper root flag in [Flags] for [SystemConfig] (SmallerDisk)'s root partition since it uses [Encryption]", err.Error()) } func TestShouldFailDeviceMapperWithNoRootPartitions(t *testing.T) { @@ -117,11 +117,11 @@ func TestShouldFailDeviceMapperWithNoRootPartitions(t *testing.T) { err := testConfig.IsValid() assert.Error(t, err) - assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\ncan't find a root ('/') [PartitionSetting] to work with either [ReadOnlyVerityRoot] or [Encryption]", err.Error()) + assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\ncan't find a root ('/') [PartitionSetting] to work with [Encryption]", err.Error()) err = remarshalJSON(testConfig, &checkedConfig) assert.Error(t, err) - assert.Equal(t, "failed to parse [Config]:\nfailed to parse [SystemConfig]: invalid [ReadOnlyVerityRoot] or [Encryption]: must have a partition mounted at '/'", err.Error()) + assert.Equal(t, "failed to parse [Config]:\nfailed to parse [SystemConfig]: invalid [Encryption]: must have a partition mounted at '/'", err.Error()) } @@ -170,12 +170,12 @@ func TestShouldFailDeviceMapperWithMultipleRoots(t *testing.T) { err := testConfig.IsValid() assert.Error(t, err) - assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\n[SystemConfig] (SmallerDisk) includes two (or more) device mapper root [PartitionSettings] (MyRootfs) and (MySecondRootfs), include only one", err.Error()) + assert.Equal(t, "a config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\n[SystemConfig] (SmallerDisk) includes two (or more) device mapper root [PartitionSettings] (MyRootfs) and (MySecondRootfs), include only one", err.Error()) // remarshal runs IsValid() on [SystemConfig] prior to running it on [Config], so we get a different error message here. err = remarshalJSON(testConfig, &checkedConfig) assert.Error(t, err) - assert.Equal(t, "failed to parse [Config]:\na config in [SystemConfigs] enables a device mapper based root (Encryption or Read-Only), but partitions are miss-configured:\n[SystemConfig] (SmallerDisk) includes two (or more) device mapper root [PartitionSettings] (MyRootfs) and (MySecondRootfs), include only one", err.Error()) + assert.Equal(t, "failed to parse [Config]:\na config in [SystemConfigs] enables a device mapper based root (Encryption), but partitions are miss-configured:\n[SystemConfig] (SmallerDisk) includes two (or more) device mapper root [PartitionSettings] (MyRootfs) and (MySecondRootfs), include only one", err.Error()) } @@ -522,17 +522,7 @@ var expectedConfiguration Config = Config{ Enable: true, Password: "EncryptPassphrase123", }, - RemoveRpmDb: false, - ReadOnlyVerityRoot: ReadOnlyVerityRoot{ - Enable: false, - Name: "verity_root_fs", - ErrorCorrectionEnable: true, - ErrorCorrectionEncodingRoots: 2, - RootHashSignatureEnable: false, - VerityErrorBehavior: "", - TmpfsOverlays: nil, - TmpfsOverlaySize: "20%", - }, + RemoveRpmDb: false, EnableHidepid: true, }, { diff --git a/toolkit/tools/imagegen/configuration/systemconfig.go b/toolkit/tools/imagegen/configuration/systemconfig.go index 3f01c6593d8..ab398feb657 100644 --- a/toolkit/tools/imagegen/configuration/systemconfig.go +++ b/toolkit/tools/imagegen/configuration/systemconfig.go @@ -41,7 +41,6 @@ type SystemConfig struct { Encryption RootEncryption `json:"Encryption"` RemoveRpmDb bool `json:"RemoveRpmDb"` PreserveTdnfCache bool `json:"PreserveTdnfCache"` - ReadOnlyVerityRoot ReadOnlyVerityRoot `json:"ReadOnlyVerityRoot"` EnableHidepid bool `json:"EnableHidepid"` DisableRpmDocs bool `json:"DisableRpmDocs"` OverrideRpmLocales string `json:"OverrideRpmLocales"` @@ -159,26 +158,16 @@ func (s *SystemConfig) IsValid() (err error) { } } - if s.ReadOnlyVerityRoot.Enable || s.Encryption.Enable { + if s.Encryption.Enable { if len(mountPointUsed) == 0 { - logger.Log.Warnf("[ReadOnlyVerityRoot] or [Encryption] is enabled, but no partitions are listed as part of System Config '%s'. This is only valid for ISO installers", s.Name) + logger.Log.Warnf("[Encryption] is enabled, but no partitions are listed as part of System Config '%s'. This is only valid for ISO installers", s.Name) } else { if !mountPointUsed["/"] { - return fmt.Errorf("invalid [ReadOnlyVerityRoot] or [Encryption]: must have a partition mounted at '/'") - } - if s.ReadOnlyVerityRoot.Enable && s.Encryption.Enable { - return fmt.Errorf("invalid [ReadOnlyVerityRoot] and [Encryption]: verity root currently does not support root encryption") - } - if s.ReadOnlyVerityRoot.Enable && !mountPointUsed["/boot"] { - return fmt.Errorf("invalid [ReadOnlyVerityRoot]: must have a separate partition mounted at '/boot'") + return fmt.Errorf("invalid [Encryption]: must have a partition mounted at '/'") } } } - if err = s.ReadOnlyVerityRoot.IsValid(); err != nil { - return fmt.Errorf("invalid [ReadOnlyVerityRoot]: %w", err) - } - if err = s.KernelCommandLine.IsValid(); err != nil { return fmt.Errorf("invalid [KernelCommandLine]: %w", err) } diff --git a/toolkit/tools/imagegen/configuration/testdata/test_configuration.json b/toolkit/tools/imagegen/configuration/testdata/test_configuration.json index a5556c54b65..237f7e06b4e 100644 --- a/toolkit/tools/imagegen/configuration/testdata/test_configuration.json +++ b/toolkit/tools/imagegen/configuration/testdata/test_configuration.json @@ -236,9 +236,6 @@ "Password": "EncryptPassphrase123" }, "RemoveRpmDb": false, - "ReadOnlyVerityRoot": { - "Enable": false - }, "EnableHidepid": true }, { diff --git a/toolkit/tools/imagegen/configuration/veritydisk.go b/toolkit/tools/imagegen/configuration/veritydisk.go deleted file mode 100644 index 551798a7807..00000000000 --- a/toolkit/tools/imagegen/configuration/veritydisk.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// Parser for the image builder's configuration schemas. - -package configuration - -import ( - "encoding/json" - "fmt" - "regexp" -) - -// ReadOnlyVerityRoot controls DM-Verity read-only filesystems which will be mounted at startup -// It will create a verity disk from the partition mounted at "/". The verity data is stored as -// part of the image's initramfs. -// - Enable: Enable dm-verity on the root filesystem and add the root hash to the -// initramfs -// - Name: Custom name for the mounted root (default is "verity_root_fs") -// - ErrorCorrectionEnable: Enable Reed-Solomon forward error correction of read-only data and -// add the FEC data to the initramfs -// - ErrorCorrectionEncodingRoots: Increase overhead to increase resiliency, default is 2 -// encoding bytes per 255 bytes of real data) giving 0.8% overhead ( RS(255,253) ) -// For a given N (where N = 255 - #Roots), the number of consecutive recoverable blocks is: -// ceiling(# of 4k blocks in disk / (N)) * (255-N) -// ie for 2GiB disk: ceiling(524288 / 253) * (255-253) = 2073 * 2 = 4146 blocks = ~16MiB -// - RootHashSignatureEnable: Validate the root hash against a key stored in the kernel's -// system keyring. The signature file should be called ".p7" and must be stored in -// the initramfs. This signature WILL NOT BE included automatically in the initramfs. It must -// be included via an out of band build step (extract initramfs, create signature from root, -// add signature file, recompress). -// - ValidateOnBoot: Run a validation of the full disk at boot time, normally blocks are validated -// only as needed. This can take several minutes if the disk is corrupted. -// - VerityErrorBehavior: System behavior when encountering an unrecoverable verity corruption. One -// of 'ignore', 'restart', 'panic' -// - TmpfsOverlays: Mount these paths as writable overlays backed by a tmpfs in memory. They are -// discarded on reboot. Overlays should not overlap each other. If a directory is not already -// present it will be created automatically. Persistant overlays can be created by mounting -// writable partitions as normal. -// - TmpfsOverlayDebugEnabled: Make the tmpfs overlay mounts easily accessible for debugging -// purposes. They can be found in /mnt/verity_overlay_debug_tmpfs -type ReadOnlyVerityRoot struct { - Enable bool `json:"Enable"` - Name string `json:"Name"` - ErrorCorrectionEnable bool `json:"ErrorCorrectionEnable"` - ErrorCorrectionEncodingRoots int `json:"ErrorCorrectionEncodingRoots"` - RootHashSignatureEnable bool `json:"RootHashSignatureEnable"` - ValidateOnBoot bool `json:"ValidateOnBoot"` - VerityErrorBehavior VerityErrorBehavior `json:"VerityErrorBehavior"` - TmpfsOverlays []string `json:"TmpfsOverlays"` - TmpfsOverlaySize string `json:"TmpfsOverlaySize"` - TmpfsOverlayDebugEnabled bool `json:"TmpfsOverlayDebugEnabled"` -} - -const ( - defaultName = "verity_root_fs" - // Default values used for Android's dm-verity FEC, gives 16MiB recovery for a 2GiB disk with 0.8% overhead - defaultErrorCorrectionEncodingN = 2 - maxErrorCorrectionEncodingRoots = 24 - minErrorCorrectionEncodingRoots = 2 - defaultOverlaySize = "20%" -) - -var ( - defaultReadOnlyVerityRoot ReadOnlyVerityRoot = ReadOnlyVerityRoot{ - Name: defaultName, - ErrorCorrectionEnable: true, - VerityErrorBehavior: VerityErrorBehaviorDefault, - ErrorCorrectionEncodingRoots: defaultErrorCorrectionEncodingN, - TmpfsOverlaySize: defaultOverlaySize, - } - // The tmpfs overlay size must be of the form: 1234, 1234(k,m,g), or 20% - tmpfsOverlaySizeRegex = regexp.MustCompile(`^(\d+)([kmg%]?)$`) -) - -// GetDefaultReadOnlyVerityRoot returns a copy of the default verity root config -func GetDefaultReadOnlyVerityRoot() (defaultVal ReadOnlyVerityRoot) { - defaultVal = defaultReadOnlyVerityRoot - return defaultVal -} - -// IsValid returns an error if the ReadOnlyVerityRoot is not valid -func (v *ReadOnlyVerityRoot) IsValid() (err error) { - if v.Enable { - return fmt.Errorf("verity root is deprecated and should not be used, use ImageCustomizer instead") - } - return -} - -// UnmarshalJSON Unmarshals a ReadOnlyVerityRoot entry -func (v *ReadOnlyVerityRoot) UnmarshalJSON(b []byte) (err error) { - // Use an intermediate type which will use the default JSON unmarshal implementation - type IntermediateTypeReadOnlyVerityRoot ReadOnlyVerityRoot - - // Populate non-standard default values - *v = GetDefaultReadOnlyVerityRoot() - - err = json.Unmarshal(b, (*IntermediateTypeReadOnlyVerityRoot)(v)) - if err != nil { - return fmt.Errorf("failed to parse [ReadOnlyVerityRoot]: %w", err) - } - - // Now validate the resulting unmarshaled object - err = v.IsValid() - if err != nil { - return fmt.Errorf("failed to parse [ReadOnlyVerityRoot]: %w", err) - } - return -} diff --git a/toolkit/tools/imagegen/configuration/verityerrorbehavior.go b/toolkit/tools/imagegen/configuration/verityerrorbehavior.go deleted file mode 100644 index 248d85fc003..00000000000 --- a/toolkit/tools/imagegen/configuration/verityerrorbehavior.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// Parser for the image builder's configuration schemas. - -package configuration - -import ( - "encoding/json" - "fmt" -) - -// VerityErrorBehavior sets the error behavior for the root FS verity disk -type VerityErrorBehavior string - -const ( - // VerityErrorBehaviorIgnore ignores corruption - VerityErrorBehaviorIgnore VerityErrorBehavior = "ignore" - // VerityErrorBehaviorRestart restarts the device when corrupt blocks are found - VerityErrorBehaviorRestart VerityErrorBehavior = "restart" - // VerityErrorBehaviorPanic panics the kernel when corrupt blocks are found - VerityErrorBehaviorPanic VerityErrorBehavior = "panic" - // VerityErrorBehaviorDefault does not explicitly set the error behavior - VerityErrorBehaviorDefault VerityErrorBehavior = "" -) - -func (v VerityErrorBehavior) String() string { - return fmt.Sprint(string(v)) -} - -// GetValidVerityErrorBehaviors returns a list of all the supported -// verity error handling behaviors -func (v *VerityErrorBehavior) GetValidVerityErrorBehaviors() (types []VerityErrorBehavior) { - return []VerityErrorBehavior{ - VerityErrorBehaviorIgnore, - VerityErrorBehaviorRestart, - VerityErrorBehaviorPanic, - VerityErrorBehaviorDefault, - } -} - -// IsValid returns an error if the VerityErrorBehavior is not valid -func (v *VerityErrorBehavior) IsValid() (err error) { - for _, valid := range v.GetValidVerityErrorBehaviors() { - if *v == valid { - return - } - } - return fmt.Errorf("invalid value for VerityErrorBehavior (%s)", v) -} - -// UnmarshalJSON Unmarshals an VerityErrorBehavior entry -func (v *VerityErrorBehavior) UnmarshalJSON(b []byte) (err error) { - // Use an intermediate type which will use the default JSON unmarshal implementation - type IntermediateTypeVerityErrorBehavior VerityErrorBehavior - err = json.Unmarshal(b, (*IntermediateTypeVerityErrorBehavior)(v)) - if err != nil { - return fmt.Errorf("failed to parse [VerityErrorBehavior]: %w", err) - } - - // Now validate the resulting unmarshaled object - err = v.IsValid() - if err != nil { - return fmt.Errorf("failed to parse [VerityErrorBehavior]: %w", err) - } - return -} diff --git a/toolkit/tools/imagegen/configuration/verityerrorbehavior_test.go b/toolkit/tools/imagegen/configuration/verityerrorbehavior_test.go deleted file mode 100644 index 7ac3f6c532c..00000000000 --- a/toolkit/tools/imagegen/configuration/verityerrorbehavior_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Microsoft Corporation. -// Licensed under the MIT License. - -package configuration - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// TestMain found in configuration_test.go. - -var ( - validVerityErrorBehaviors = []VerityErrorBehavior{ - VerityErrorBehavior("ignore"), - VerityErrorBehavior("restart"), - VerityErrorBehavior("panic"), - VerityErrorBehavior(""), - } - invalidVerityErrorBehavior = VerityErrorBehavior("not_a_behavior") - validVerityErrorBehaviorJSON = `"ignore"` - invalidVerityErrorBehaviorJSON = `1234` -) - -func TestShouldSucceedValidVerityErrorBehaviorsMatch_VerityErrorBehavior(t *testing.T) { - var behavior VerityErrorBehavior - assert.Equal(t, len(validVerityErrorBehaviors), len(behavior.GetValidVerityErrorBehaviors())) - - for _, errorBehavior := range validVerityErrorBehaviors { - found := false - for _, validErrorBehavior := range behavior.GetValidVerityErrorBehaviors() { - if errorBehavior == validErrorBehavior { - found = true - } - } - assert.True(t, found) - } -} - -func TestShouldSucceedParsingValidErrorBehaviors_VerityErrorBehavior(t *testing.T) { - for _, validErrorBehavior := range validVerityErrorBehaviors { - var checkedBehavior VerityErrorBehavior - - assert.NoError(t, validErrorBehavior.IsValid()) - err := remarshalJSON(validErrorBehavior, &checkedBehavior) - assert.NoError(t, err) - assert.Equal(t, validErrorBehavior, checkedBehavior) - } -} - -func TestShouldFailParsingInvalidErrorBehavior_VerityErrorBehavior(t *testing.T) { - var checkedBehavior VerityErrorBehavior - - err := invalidVerityErrorBehavior.IsValid() - assert.Error(t, err) - assert.Equal(t, "invalid value for VerityErrorBehavior (not_a_behavior)", err.Error()) - - err = remarshalJSON(invalidVerityErrorBehavior, &checkedBehavior) - assert.Error(t, err) - assert.Equal(t, "failed to parse [VerityErrorBehavior]: invalid value for VerityErrorBehavior (not_a_behavior)", err.Error()) -} - -func TestShouldSucceedParsingValidJSON_VerityErrorBehavior(t *testing.T) { - var checkedBehavior VerityErrorBehavior - - err := marshalJSONString(validVerityErrorBehaviorJSON, &checkedBehavior) - assert.NoError(t, err) - assert.Equal(t, validVerityErrorBehaviors[0], checkedBehavior) -} - -func TestShouldFailParsingInvalidJSON_VerityErrorBehavior(t *testing.T) { - var checkedBehavior VerityErrorBehavior - - err := marshalJSONString(invalidVerityErrorBehaviorJSON, &checkedBehavior) - assert.Error(t, err) - assert.Equal(t, "failed to parse [VerityErrorBehavior]: json: cannot unmarshal number into Go value of type configuration.IntermediateTypeVerityErrorBehavior", err.Error()) -} diff --git a/toolkit/tools/imagegen/diskutils/diskutils.go b/toolkit/tools/imagegen/diskutils/diskutils.go index 289b7d6eceb..72f0c8e02af 100644 --- a/toolkit/tools/imagegen/diskutils/diskutils.go +++ b/toolkit/tools/imagegen/diskutils/diskutils.go @@ -434,8 +434,8 @@ func WaitForDevicesToSettle() error { // CreatePartitions creates partitions on the specified disk according to the disk config func CreatePartitions(diskDevPath string, disk configuration.Disk, rootEncryption configuration.RootEncryption, - readOnlyRootConfig configuration.ReadOnlyVerityRoot, diskKnownToBeEmpty bool, -) (partDevPathMap map[string]string, partIDToFsTypeMap map[string]string, encryptedRoot EncryptedRootDevice, readOnlyRoot VerityDevice, err error) { + diskKnownToBeEmpty bool, +) (partDevPathMap map[string]string, partIDToFsTypeMap map[string]string, encryptedRoot EncryptedRootDevice, err error) { const timeoutInSeconds = "5" partDevPathMap = make(map[string]string) partIDToFsTypeMap = make(map[string]string) @@ -489,29 +489,22 @@ func CreatePartitions(diskDevPath string, disk configuration.Disk, rootEncryptio partedSupportsEmptyStringArgs) if err != nil { err = fmt.Errorf("failed to create single partition:\n%w", err) - return partDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err + return partDevPathMap, partIDToFsTypeMap, encryptedRoot, err } partFsType, err := FormatSinglePartition(partDevPath, partition) if err != nil { err = fmt.Errorf("failed to format partition:\n%w", err) - return partDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err + return partDevPathMap, partIDToFsTypeMap, encryptedRoot, err } if rootEncryption.Enable && partition.HasFlag(configuration.PartitionFlagDeviceMapperRoot) { encryptedRoot, err = encryptRootPartition(partDevPath, partition, rootEncryption) if err != nil { err = fmt.Errorf("failed to initialize encrypted root:\n%w", err) - return partDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err + return partDevPathMap, partIDToFsTypeMap, encryptedRoot, err } partDevPathMap[partition.ID] = GetEncryptedRootVolMapping() - } else if readOnlyRootConfig.Enable && partition.HasFlag(configuration.PartitionFlagDeviceMapperRoot) { - readOnlyRoot, err = PrepReadOnlyDevice(partDevPath, partition, readOnlyRootConfig) - if err != nil { - err = fmt.Errorf("failed to initialize read only root:\n%w", err) - return partDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err - } - partDevPathMap[partition.ID] = readOnlyRoot.MappedDevice } else { partDevPathMap[partition.ID] = partDevPath } diff --git a/toolkit/tools/imagegen/diskutils/initramfs.go b/toolkit/tools/imagegen/diskutils/initramfs.go deleted file mode 100644 index 939ba6496ce..00000000000 --- a/toolkit/tools/imagegen/diskutils/initramfs.go +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// Utility to create or modify Initramfs files - -package diskutils - -import ( - "bytes" - "fmt" - "io" - "os" - - "github.com/microsoft/azurelinux/toolkit/tools/internal/logger" - - "github.com/cavaliercoder/go-cpio" - "github.com/klauspost/pgzip" -) - -// InitramfsMount represented an editable initramfs -type InitramfsMount struct { - pgzWriter *pgzip.Writer - cpioWriter *cpio.Writer - outputBuffer *bytes.Buffer - initramfsOutputFile *os.File -} - -// CreateInitramfs creates a new initramfs -// Caller is responsible for calling initramfs.Close() when finished -func CreateInitramfs(initramfsPath string) (initramfs InitramfsMount, err error) { - // Initramfs traditionally is -rw------- - const initramfsModeBits = os.FileMode(0600) - - initramfs.outputBuffer = new(bytes.Buffer) - initramfs.pgzWriter = pgzip.NewWriter(initramfs.outputBuffer) - initramfs.cpioWriter = cpio.NewWriter(initramfs.pgzWriter) - - initramfs.initramfsOutputFile, err = os.OpenFile(initramfsPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, initramfsModeBits) - if err != nil { - err = fmt.Errorf("failed to create a new initramfs at (%s):\n%w", initramfsPath, err) - } - - return -} - -// OpenInitramfs makes an existing initramfs editable -// Caller is responsible for calling initramfs.Close() when finished -func OpenInitramfs(initramfsPath string) (initramfs InitramfsMount, err error) { - logger.Log.Debugf("Opening intramfs '%s'", initramfsPath) - inputFile, err := os.Open(initramfsPath) - if err != nil { - return - } - defer inputFile.Close() - - gzReader, err := pgzip.NewReader(inputFile) - if err != nil { - return - } - defer gzReader.Close() - cpioReader := cpio.NewReader(gzReader) - //cpio.Reader has no Close() function to defer - - initramfs.outputBuffer = new(bytes.Buffer) - initramfs.pgzWriter = pgzip.NewWriter(initramfs.outputBuffer) - initramfs.cpioWriter = cpio.NewWriter(initramfs.pgzWriter) - - // Read until EOF or other error - for { - var ( - bytesIO int64 - linkPayload []byte - nextFileHeader *cpio.Header - ) - - nextFileHeader, err = cpioReader.Next() - if err == io.EOF { - // Expected end of archive - err = nil - break - } - if err != nil { - return - } - - // For a given symlink, generate a payload that contains the read value of the link. - // The payload should be written after the header. - // e.g. A link from (/bin -> /usr/bin) would have a payload of "/usr/bin". - // cpio.ModeSymlink is two bits (one of which is reused), need to check for actual - // equality rather than non-zero after masking - isLink := ((nextFileHeader.Mode & cpio.ModeType) == cpio.ModeSymlink) - if isLink { - linkPayload = []byte(nextFileHeader.Linkname) - nextFileHeader.Size = int64(len(linkPayload)) - } - - err = initramfs.cpioWriter.WriteHeader(nextFileHeader) - if err != nil { - return - } - - if isLink { - var bytesWrittenInt int - - // Write returns an int, cast it to an int64 afterwards - logger.Log.Tracef("Creating link %s -> %s", nextFileHeader.Name, nextFileHeader.Linkname) - bytesWrittenInt, err = initramfs.cpioWriter.Write(linkPayload) - bytesIO = int64(bytesWrittenInt) - } else { - bytesIO, err = io.Copy(initramfs.cpioWriter, cpioReader) - } - - if err != nil { - return - } - - logger.Log.Tracef("File %s caused %d bytes to be transferred to new archive", nextFileHeader.Name, bytesIO) - logger.Log.Tracef("Buffer unread length: %d", initramfs.outputBuffer.Len()) - } - - inputFile.Close() - - fileInfo, err := os.Stat(initramfsPath) - if err != nil { - return - } - - // We can't edit a CPIO archive in place, completely overwrite the file with truncate - // The output buffer in memory will be used to re-create the initramfs. - initramfs.initramfsOutputFile, err = os.OpenFile(initramfsPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileInfo.Mode()) - - return -} - -// Close flushes the archives and closes all initramfs resources -func (i *InitramfsMount) Close() (err error) { - var bytesIO int - - logger.Log.Debugf("Closing initramfs file '%s'", i.initramfsOutputFile.Name()) - - // Defer close calls to make sure we handle any errors, failing to - // close the file means we can't close the install root. - defer i.initramfsOutputFile.Close() - defer i.pgzWriter.Close() - defer i.cpioWriter.Close() - - err = i.cpioWriter.Close() - if err != nil { - err = fmt.Errorf("failed to close initramfs cpio writer:\n%w", err) - return - } - err = i.pgzWriter.Close() - if err != nil { - err = fmt.Errorf("failed to close initramfs pgzip writer:\n%w", err) - return - } - - logger.Log.Debugf("Writing %d bytes to file", i.outputBuffer.Len()) - bytesIO, err = i.initramfsOutputFile.Write(i.outputBuffer.Bytes()) - if err != nil { - err = fmt.Errorf("failed to write initramfs file:\n%w", err) - return - } - logger.Log.Infof("Bytes writen to file: %d", bytesIO) - - // Explicit call to fsync, archive corruption was occuring occasionally otherwise. - err = i.initramfsOutputFile.Sync() - if err != nil { - err = fmt.Errorf("failed to sync initramfs file:\n%w", err) - return - } - - err = i.initramfsOutputFile.Close() - if err != nil { - err = fmt.Errorf("failed to close initramfs:\n%w", err) - return - } - return -} - -// AddFileToInitramfs places a single file in the initramfs at the destination path. -// - sourcePath: Path to file which is to be added -// - destPath: Final destination in the initramfs -func (i *InitramfsMount) AddFileToInitramfs(sourcePath, destPath string) (err error) { - var bytesIO int64 - fileInfo, err := os.Lstat(sourcePath) - if err != nil { - return - } - file, err := os.Open(sourcePath) - if err != nil { - return - } - defer file.Close() - - // Symlinks need to be resolved to their target file to be added to the cpio archive. - var linkDestination string - // This is a Go symlink mode iota, distinct from the cpio symlink mode bits used in OpenInitramfs(), - // and may be checked directly. - isSymlink := (fileInfo.Mode() & os.ModeSymlink) != 0 - if isSymlink { - linkDestination, err = os.Readlink(sourcePath) - if err != nil { - return - } - - logger.Log.Debugf("--> Adding link: (%s) -> (%s)", sourcePath, linkDestination) - } - - // Convert the OS header into a CPIO header - // Only symlink files will use the linkDestination parameter, may be "" for other files. - header, err := cpio.FileInfoHeader(fileInfo, linkDestination) - if err != nil { - return - } - header.Name = destPath - - err = i.cpioWriter.WriteHeader(header) - if err != nil { - return - } - - if fileInfo.Mode().IsRegular() { - bytesIO, err = io.Copy(i.cpioWriter, file) - if err != nil { - err = fmt.Errorf("failed to add regular file (%s) into initramfs:\n%w", header.Name, err) - return - } - logger.Log.Debugf("New file '%s' caused %d bytes to be transferred to new archive", header.Name, bytesIO) - } else { - // Special files (unix sockets, directories, symlinks, ...) need to be handled differently - // since a simple byte transfer of the file's content into the CPIO archive can't be achieved. - logger.Log.Debugf("Adding special file '%s' to initramfs", header.Name) - - // For a symlink the reported size will be the size (in bytes) of the link's target. - // Write this data into the archive. - if isSymlink { - _, err = i.cpioWriter.Write([]byte(linkDestination)) - if err != nil { - err = fmt.Errorf("failed to add symlink (%s)->(%s) to initramfs:\n%w", header.Name, linkDestination, err) - return - } - } - - // For all other special files, they will be of size 0 and only contain the header in the archive. - } - - return -} diff --git a/toolkit/tools/imagegen/diskutils/verity.go b/toolkit/tools/imagegen/diskutils/verity.go deleted file mode 100644 index df9cf622f56..00000000000 --- a/toolkit/tools/imagegen/diskutils/verity.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// Utility to create read-only root partitions - -package diskutils - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - - "github.com/microsoft/azurelinux/toolkit/tools/imagegen/configuration" - "github.com/microsoft/azurelinux/toolkit/tools/internal/logger" - "github.com/microsoft/azurelinux/toolkit/tools/internal/randomization" - "github.com/microsoft/azurelinux/toolkit/tools/internal/shell" -) - -const ( - mappingVerityPrefix = "verity-" - debugMountPoint = "/mnt/verity_overlay_debug_tmpfs" -) - -var ( - // The roothash line will be of the form "Root hash: 123456789abcd" - // Turn on multiline mode with ?m - rootHashLineRegex = regexp.MustCompile(`(?m)^Root hash:\s+(\S*)$`) -) - -// VerityDevice represents a device mapper linear device used for a dm-verity read-only partition. -// - MappedName is the desired device mapper name -// - MappedDevice is the full path of the created device mapper device -// - BackingDevice is the underlying file/device which backs the partition -// - FecRoots is the number of error correcting roots, 0 to omit error correction -// - ValidateOnBoot will cause a full, user-mode analysis of the verity disk during boot (good for debugging) -// - UseRootHashSignature indicates a signature file has been included with the verity disk and should be checked -// - ErrorBehavior is what dm-verity should do in the event of corruption (ignore, panic, restart) -// - TmpfsOverlays is a list of tmpfs overlays which will be created after the verity partition is mounted -// - TmpfsOverlaySize is the size argument to pass to the tmpfs mount command (1234, 1234, 20%) -// - TmpfsOverlaysDebugMount indicates if the overlays should be made accessible for debugging purposes -type VerityDevice struct { - MappedName string - MappedDevice string - BackingDevice string - FecRoots int - ValidateOnBoot bool - UseRootHashSignature bool - ErrorBehavior string - TmpfsOverlays []string - TmpfsOverlaySize string - TmpfsOverlaysDebugMount string -} - -// AddRootVerityFilesToInitramfs adds files needed for a verity root to the initramfs -// - workingFolder is a temporary folder to extract the initramfs to -// - initramfsPath is the path to the initramfs -func (v *VerityDevice) AddRootVerityFilesToInitramfs(workingFolder, initramfsPath string) (err error) { - verityWorkingDirectory := filepath.Join(workingFolder, v.MappedName) - - // Measure the disk and generate the hash and fec files - err = v.createVerityDisk(verityWorkingDirectory) - if err != nil { - return fmt.Errorf("failed while generating a verity disk:\n%w", err) - } - - // Now place them in the initramfs - logger.Log.Info("Adding dm-verity read-only root files into initramfs") - initramfs, err := OpenInitramfs(initramfsPath) - defer initramfs.Close() - if err != nil { - return fmt.Errorf("failed to open the initramfs:\n%w", err) - } - - verityFiles, err := os.ReadDir(verityWorkingDirectory) - if err != nil { - return - } - - for _, file := range verityFiles { - filePath := filepath.Join(verityWorkingDirectory, file.Name()) - logger.Log.Debugf("Adding '%s' to initramfs", filePath) - // Place each file in the root of the initramfs - err = initramfs.AddFileToInitramfs(filePath, file.Name()) - if err != nil { - return fmt.Errorf("failed to add (%s) to initramfs:\n%w", filePath, err) - } - } - - return -} - -func (v *VerityDevice) createVerityDisk(verityDirectory string) (err error) { - const ( - saltLength = 64 - hashAlg = "sha256" - ) - var ( - verityFecArgs []string - verityArgs []string - verityVerifyArgs []string - fileBase = filepath.Join(verityDirectory, v.MappedName) - rootHashPath = fmt.Sprintf("%s.roothash", fileBase) - hashtreePath = fmt.Sprintf("%s.hashtree", fileBase) - fecFilePath = fmt.Sprintf("%s.fec", fileBase) - ) - - err = os.MkdirAll(verityDirectory, os.ModePerm) - if err != nil { - return - } - - rootHashFile, err := os.Create(rootHashPath) - if err != nil { - return - } - defer rootHashFile.Close() - - salt, err := randomization.RandomString(64, randomization.LegalCharactersHex) - if err != nil { - return - } - - if v.FecRoots > 0 { - verityFecArgs = []string{ - fmt.Sprintf("--fec-device=%s", fecFilePath), - fmt.Sprintf("--fec-roots=%d", v.FecRoots), - } - } - - verityArgs = []string{ - "--salt", - salt, - "--hash", - hashAlg, - "--verbose", - "--debug", - "format", - v.MappedDevice, - hashtreePath, - } - - logger.Log.Info("Generating a dm-verity read-only partition") - verityOutput, stderr, err := shell.Execute("veritysetup", append(verityFecArgs, verityArgs...)...) - if err != nil { - err = fmt.Errorf("failed to create verity disk:\n%v:\n%w", stderr, err) - return - } - - // Searches for a line like: "Root hash: 1234567890abcdefg..." - matches := rootHashLineRegex.FindStringSubmatch(verityOutput) - - if len(matches) != 2 { - err = fmt.Errorf("failed to extract root hash from veritysetup output: (%s), matched: (%#v)", verityOutput, matches) - return - } - rootHash := matches[1] - - logger.Log.Infof("Verity partition completed, root hash: '%s'", rootHash) - _, err = rootHashFile.WriteString(rootHash) - if err != nil { - return - } - - //Verify the disk was created correctly: - verityVerifyArgs = []string{ - "--verbose", - "--debug", - "verify", - v.MappedDevice, - hashtreePath, - rootHash, - } - - logger.Log.Info("Verifying the verity partition") - verityOutput, stderr, err = shell.Execute("veritysetup", verityVerifyArgs...) - if err != nil { - err = fmt.Errorf("failed to validate new verity disk (%s):\n%v\n%w", verityOutput, stderr, err) - } - - return -} - -// PrepReadOnlyDevice sets up a device mapper linear map. -// This map will have the correct name of the final verity disk, and can be -// switched to read-only when the final image is ready for measurement. -// - partDevPath is the path of the root partition device (likely a loopback device) -// - partition is the disk configuration -// - readOnlyConfig is the root read-only settings -func PrepReadOnlyDevice(partDevPath string, partition configuration.Partition, readOnlyConfig configuration.ReadOnlyVerityRoot) (readOnlyDevice VerityDevice, err error) { - const ( - linearTable = `0 %d linear %s 0` - ) - - if !readOnlyConfig.Enable { - err = fmt.Errorf("verity is not enabled, can't update partition (%s)", partition.ID) - return - } - - // Save the required information from the config into the device struct - finalDeviceName := fmt.Sprintf("%s%s", mappingVerityPrefix, readOnlyConfig.Name) - readOnlyDevice.BackingDevice = partDevPath - readOnlyDevice.MappedName = finalDeviceName - readOnlyDevice.MappedDevice = filepath.Join("/dev/mapper/", finalDeviceName) - readOnlyDevice.ErrorBehavior = readOnlyConfig.VerityErrorBehavior.String() - readOnlyDevice.ValidateOnBoot = readOnlyConfig.ValidateOnBoot - if readOnlyConfig.ErrorCorrectionEnable { - readOnlyDevice.FecRoots = readOnlyConfig.ErrorCorrectionEncodingRoots - } else { - readOnlyDevice.FecRoots = 0 - } - readOnlyDevice.TmpfsOverlays = readOnlyConfig.TmpfsOverlays - if len(readOnlyDevice.TmpfsOverlays) > 0 { - readOnlyDevice.TmpfsOverlaySize = readOnlyConfig.TmpfsOverlaySize - } - if readOnlyConfig.TmpfsOverlayDebugEnabled { - readOnlyDevice.TmpfsOverlaysDebugMount = debugMountPoint - } - readOnlyDevice.UseRootHashSignature = readOnlyConfig.RootHashSignatureEnable - - // linear mappings need to know the size of the disk in blocks ahead of time - deviceSizeStr, stderr, err := shell.Execute("blockdev", "--getsz", readOnlyDevice.BackingDevice) - if err != nil { - err = fmt.Errorf("failed to get loopback device size (%s):\n%v\n%w", partDevPath, stderr, err) - return - } - deviceSizeInt, err := strconv.ParseUint(strings.TrimSpace(deviceSizeStr), 10, 64) - if err != nil { - err = fmt.Errorf("failed to convert disk size (%s) to integer:\n%w", deviceSizeStr, err) - return - } - - populatedTable := fmt.Sprintf(linearTable, deviceSizeInt, readOnlyDevice.BackingDevice) - dmsetupArgs := []string{ - "create", - readOnlyDevice.MappedName, - "--table", - populatedTable, - } - _, stderr, err = shell.Execute("dmsetup", dmsetupArgs...) - if err != nil { - err = fmt.Errorf("failed to create a device mapper device (%s):\n%w", stderr, err) - return - } - - logger.Log.Debugf("Remapped partition %s for read-only prep to %s", partition.ID, readOnlyDevice.MappedDevice) - - return -} - -// CleanupVerityDevice removes the device mapper linear mapping, but leaves the backing device unchanged -func (v *VerityDevice) CleanupVerityDevice() (err error) { - stdout, stderr, err := shell.Execute("dmsetup", "remove", v.MappedName) - if err != nil { - err = fmt.Errorf("failed to clean up device mapper device:%s\n%v\n%w", stdout, stderr, err) - return - } - return -} - -// SwitchDeviceToReadOnly switches the root device linear map to read only -// Will also re-mount the moint point to respect this. -// - mountPointOrDevice is either the location of the mount, or the device which was mounted (mount command will take either) -// - mountArgs are any special mount options used which should continue to be used -func (v *VerityDevice) SwitchDeviceToReadOnly(mountPointOrDevice, mountArgs string) (err error) { - const ( - remountOptions = "remount,ro" - ) - - // Suspending the mapped device will force a sync - _, stderr, err := shell.Execute("dmsetup", "suspend", v.MappedName) - if err != nil { - return fmt.Errorf("failed to suspend device (%s):\n%v\n%w", v.MappedDevice, stderr, err) - } - - // Need to get the table data to "recreate" the device with read-only set - table, stderr, err := shell.Execute("dmsetup", "table", v.MappedName) - if err != nil { - return fmt.Errorf("failed to get table for device (%s):\n%v\n%w", v.MappedDevice, stderr, err) - } - - // Switch the linear map to read-only - dmsetupArgs := []string{ - "reload", - "--readonly", - v.MappedName, - "--table", - table, - } - _, stderr, err = shell.Execute("dmsetup", dmsetupArgs...) - if err != nil { - return fmt.Errorf("failed to reload device (%s) in read-only mode:\n%v\n%w", v.MappedDevice, stderr, err) - } - - // Re-enable the device - _, stderr, err = shell.Execute("dmsetup", "resume", v.MappedName) - if err != nil { - return fmt.Errorf("failed to resume device (%s):\n%v\n%w", v.MappedDevice, stderr, err) - } - - // Mounts don't respect the read-only nature of the underlying device, force a remount - _, stderr, err = shell.Execute("mount", "-o", mountArgs+remountOptions, mountPointOrDevice) - if err != nil { - return fmt.Errorf("failed to remount (%s):\n%v\n%w", mountPointOrDevice, stderr, err) - } - return -} - -// IsReadOnlyDevice checks if a given device is a dm-verity read-only device -// - devicePath is the device to check -func IsReadOnlyDevice(devicePath string) (result bool) { - verityPrefix := filepath.Join(mappingFilePath, mappingVerityPrefix) - return strings.HasPrefix(devicePath, verityPrefix) -} diff --git a/toolkit/tools/imagegen/installutils/installutils.go b/toolkit/tools/imagegen/installutils/installutils.go index 319d24cd931..375b22e485a 100644 --- a/toolkit/tools/imagegen/installutils/installutils.go +++ b/toolkit/tools/imagegen/installutils/installutils.go @@ -1130,7 +1130,6 @@ func addEntryToFstab(fullFstabPath, mountPoint, devicePath, fsType, mountArgs st defaultOptions = "defaults" swapFsType = "swap" swapOptions = "sw" - readOnlyOptions = "ro" defaultDump = "0" disablePass = "0" rootPass = "1" @@ -1141,9 +1140,6 @@ func addEntryToFstab(fullFstabPath, mountPoint, devicePath, fsType, mountArgs st if mountArgs == "" { options = defaultOptions - if diskutils.IsReadOnlyDevice(devicePath) { - options = fmt.Sprintf("%s,%s", options, readOnlyOptions) - } } else { options = mountArgs } @@ -1154,7 +1150,7 @@ func addEntryToFstab(fullFstabPath, mountPoint, devicePath, fsType, mountArgs st // Get the block device var device string - if diskutils.IsEncryptedDevice(devicePath) || diskutils.IsReadOnlyDevice(devicePath) || doPseudoFsMount { + if diskutils.IsEncryptedDevice(devicePath) || doPseudoFsMount { device = devicePath } else { device, err = FormatMountIdentifier(identifierType, devicePath) @@ -1222,10 +1218,9 @@ func addEntryToCrypttab(installRoot string, devicePath string, encryptedRoot dis return } -func ConfigureDiskBootloader(bootType string, encryptionEnable bool, readOnlyVerityRootEnable bool, - partitionSettings []configuration.PartitionSetting, kernelCommandLine configuration.KernelCommandLine, - installChroot *safechroot.Chroot, diskDevPath string, mountPointMap map[string]string, - encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, enableGrubMkconfig bool, +func ConfigureDiskBootloader(bootType string, encryptionEnable bool, partitionSettings []configuration.PartitionSetting, + kernelCommandLine configuration.KernelCommandLine, installChroot *safechroot.Chroot, diskDevPath string, + mountPointMap map[string]string, encryptedRoot diskutils.EncryptedRootDevice, enableGrubMkconfig bool, includeLegacyGrubCfg bool, ) (err error) { timestamp.StartEvent("configuring bootloader", nil) @@ -1243,16 +1238,14 @@ func ConfigureDiskBootloader(bootType string, encryptionEnable bool, readOnlyVer } rootMountIdentifier := rootPartitionSetting.MountIdentifier - return ConfigureDiskBootloaderWithRootMountIdType(bootType, encryptionEnable, readOnlyVerityRootEnable, - rootMountIdentifier, kernelCommandLine, installChroot, diskDevPath, mountPointMap, encryptedRoot, readOnlyRoot, - enableGrubMkconfig, includeLegacyGrubCfg) + return ConfigureDiskBootloaderWithRootMountIdType(bootType, encryptionEnable, rootMountIdentifier, kernelCommandLine, + installChroot, diskDevPath, mountPointMap, encryptedRoot, enableGrubMkconfig, includeLegacyGrubCfg) } -func ConfigureDiskBootloaderWithRootMountIdType(bootType string, encryptionEnable bool, readOnlyVerityRootEnable bool, +func ConfigureDiskBootloaderWithRootMountIdType(bootType string, encryptionEnable bool, rootMountIdentifier configuration.MountIdentifier, kernelCommandLine configuration.KernelCommandLine, installChroot *safechroot.Chroot, diskDevPath string, mountPointMap map[string]string, - encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, enableGrubMkconfig bool, - includeLegacyGrubCfg bool, + encryptedRoot diskutils.EncryptedRootDevice, enableGrubMkconfig bool, includeLegacyGrubCfg bool, ) (err error) { // Add bootloader. Prefer a separate boot partition if one exists. bootDevice, isBootPartitionSeparate := mountPointMap[bootMountPoint] @@ -1286,14 +1279,6 @@ func ConfigureDiskBootloaderWithRootMountIdType(bootType string, encryptionEnabl if encryptionEnable { // Encrypted devices don't currently support identifiers rootDevice = mountPointMap[rootMountPoint] - } else if readOnlyVerityRootEnable { - var partIdentifier string - partIdentifier, err = FormatMountIdentifier(rootMountIdentifier, readOnlyRoot.BackingDevice) - if err != nil { - err = fmt.Errorf("failed to get partIdentifier: %s", err) - return - } - rootDevice = fmt.Sprintf("verityroot:%v", partIdentifier) } else { var partIdentifier string partIdentifier, err = FormatMountIdentifier(rootMountIdentifier, mountPointMap[rootMountPoint]) @@ -1307,7 +1292,7 @@ func ConfigureDiskBootloaderWithRootMountIdType(bootType string, encryptionEnabl // Grub will always use filesystem UUID, never PARTUUID or PARTLABEL err = InstallGrubDefaults(installChroot.RootDir(), rootDevice, bootUUID, bootPrefix, encryptedRoot, - kernelCommandLine, readOnlyRoot, isBootPartitionSeparate, includeLegacyGrubCfg) + kernelCommandLine, isBootPartitionSeparate, includeLegacyGrubCfg) if err != nil { err = fmt.Errorf("failed to install main grub config file: %s", err) return @@ -1362,11 +1347,11 @@ func InstallGrubEnv(installRoot string) (err error) { // This boot partition specifically indicates where to find the kernel, config files, and initrd func InstallGrubDefaults(installRoot, rootDevice, bootUUID, bootPrefix string, encryptedRoot diskutils.EncryptedRootDevice, kernelCommandLine configuration.KernelCommandLine, - readOnlyRoot diskutils.VerityDevice, isBootPartitionSeparate bool, includeLegacyCfg bool, + isBootPartitionSeparate bool, includeLegacyCfg bool, ) (err error) { // Copy the bootloader's /etc/default/grub and set the file permission err = installGrubTemplateFile(resources.AssetsGrubDefFile, GrubDefFile, installRoot, rootDevice, bootUUID, - bootPrefix, encryptedRoot, kernelCommandLine, readOnlyRoot, isBootPartitionSeparate) + bootPrefix, encryptedRoot, kernelCommandLine, isBootPartitionSeparate) if err != nil { logger.Log.Warnf("Failed to install (%s): %v", GrubDefFile, err) return @@ -1375,7 +1360,7 @@ func InstallGrubDefaults(installRoot, rootDevice, bootUUID, bootPrefix string, if includeLegacyCfg { // Add the legacy /boot/grub2/grub.cfg file, which was used in Azure Linux 2.0. err = installGrubTemplateFile(resources.AssetsGrubCfgFile, GrubCfgFile, installRoot, rootDevice, bootUUID, - bootPrefix, encryptedRoot, kernelCommandLine, readOnlyRoot, isBootPartitionSeparate) + bootPrefix, encryptedRoot, kernelCommandLine, isBootPartitionSeparate) if err != nil { logger.Log.Warnf("Failed to install (%s): %v", GrubCfgFile, err) return @@ -1387,7 +1372,7 @@ func InstallGrubDefaults(installRoot, rootDevice, bootUUID, bootPrefix string, func installGrubTemplateFile(assetFile, targetFile, installRoot, rootDevice, bootUUID, bootPrefix string, encryptedRoot diskutils.EncryptedRootDevice, kernelCommandLine configuration.KernelCommandLine, - readOnlyRoot diskutils.VerityDevice, isBootPartitionSeparate bool, + isBootPartitionSeparate bool, ) (err error) { installGrubDefFile := filepath.Join(installRoot, targetFile) @@ -1439,12 +1424,6 @@ func installGrubTemplateFile(assetFile, targetFile, installRoot, rootDevice, boo return } - err = setGrubCfgReadOnlyVerityRoot(installGrubDefFile, readOnlyRoot) - if err != nil { - logger.Log.Warnf("Failed to set verity root in in %s: %v", installGrubDefFile, err) - return - } - err = setGrubCfgSELinux(installGrubDefFile, kernelCommandLine) if err != nil { logger.Log.Warnf("Failed to set SELinux in %s: %v", installGrubDefFile, err) @@ -2635,56 +2614,6 @@ func setGrubCfgCGroup(grubPath string, kernelCommandline configuration.KernelCom return } -// setGrubCfgReadOnlyVerityRoot populates the arguments needed to boot with a dm-verity read-only root partition -func setGrubCfgReadOnlyVerityRoot(grubPath string, readOnlyRoot diskutils.VerityDevice) (err error) { - var ( - verityMountArg = fmt.Sprintf("rd.verityroot.devicename=%s", readOnlyRoot.MappedName) - verityHashArg = fmt.Sprintf("rd.verityroot.hashtree=/%s.hashtree", readOnlyRoot.MappedName) - verityRootHashArg = fmt.Sprintf("rd.verityroot.roothashfile=/%s.roothash", readOnlyRoot.MappedName) - verityRootHashSigArg = fmt.Sprintf("rd.verityroot.roothashsig=/%s.p7", readOnlyRoot.MappedName) - verityFECDataArg = fmt.Sprintf("rd.verityroot.fecdata=/%s.fec", readOnlyRoot.MappedName) - verityFECRootsArg = fmt.Sprintf("rd.verityroot.fecroots=%d", readOnlyRoot.FecRoots) - verityErrorHandling = fmt.Sprintf("rd.verityroot.verityerrorhandling=%s", readOnlyRoot.ErrorBehavior) - verityValidateOnBootArg = fmt.Sprintf("rd.verityroot.validateonboot=%v", readOnlyRoot.ValidateOnBoot) - verityOverlaysArg = fmt.Sprintf("rd.verityroot.overlays=\"%s\"", strings.Join(readOnlyRoot.TmpfsOverlays, " ")) - verityOverlaySizeArg = fmt.Sprintf("rd.verityroot.overlaysize=\"%s\"", readOnlyRoot.TmpfsOverlaySize) - verityDebugMountsArg = fmt.Sprintf("rd.verityroot.overlays_debug_mount=%s", readOnlyRoot.TmpfsOverlaysDebugMount) - verityPattern = "{{.ReadOnlyVerityRoot}}" - verityArgs = "" - - cmdline configuration.KernelCommandLine - ) - - if readOnlyRoot.MappedName != "" { - // Basic set of verity arguments common to all use cases - verityArgs = fmt.Sprintf("%s %s %s %s %s %s %s %s", - verityMountArg, - verityHashArg, - verityRootHashArg, - verityErrorHandling, - verityValidateOnBootArg, - verityOverlaysArg, - verityOverlaySizeArg, - verityDebugMountsArg, - ) - // Only include the FEC arguments if we have FEC enabled - if readOnlyRoot.FecRoots > 0 { - verityArgs = fmt.Sprintf("%s %s %s", verityArgs, verityFECDataArg, verityFECRootsArg) - } - if readOnlyRoot.UseRootHashSignature { - verityArgs = fmt.Sprintf("%s %s", verityArgs, verityRootHashSigArg) - } - } - - logger.Log.Debugf("Adding Verity Root ('%s') to %s", verityArgs, grubPath) - err = sed(verityPattern, verityArgs, cmdline.GetSedDelimeter(), grubPath) - if err != nil { - logger.Log.Warnf("Failed to set grub.cfg's Verity Root setting: %v", err) - } - - return -} - func setGrubCfgLVM(grubPath, luksUUID string) (err error) { const ( lvmPrefix = "rd.lvm.lv=" diff --git a/toolkit/tools/imager/imager.go b/toolkit/tools/imager/imager.go index b1bbe9a96fe..80b937a7708 100644 --- a/toolkit/tools/imager/imager.go +++ b/toolkit/tools/imager/imager.go @@ -142,7 +142,6 @@ func buildSystemConfig(systemConfig configuration.SystemConfig, disks []configur diskDevPath string kernelPkg string encryptedRoot diskutils.EncryptedRootDevice - readOnlyRoot diskutils.VerityDevice partIDToDevPathMap map[string]string partIDToFsTypeMap map[string]string mountPointToOverlayMap map[string]*installutils.Overlay @@ -188,7 +187,7 @@ func buildSystemConfig(systemConfig configuration.SystemConfig, disks []configur timestamp.StartEvent("creating raw disk", nil) diskConfig := disks[defaultDiskIndex] - diskDevPath, partIDToDevPathMap, partIDToFsTypeMap, isLoopDevice, encryptedRoot, readOnlyRoot, err = setupDisk(buildDir, defaultTempDiskName, *liveInstallFlag, diskConfig, systemConfig.Encryption, systemConfig.ReadOnlyVerityRoot) + diskDevPath, partIDToDevPathMap, partIDToFsTypeMap, isLoopDevice, encryptedRoot, err = setupDisk(buildDir, defaultTempDiskName, *liveInstallFlag, diskConfig, systemConfig.Encryption) if err != nil { return } @@ -199,10 +198,6 @@ func buildSystemConfig(systemConfig configuration.SystemConfig, disks []configur defer diskutils.BlockOnDiskIO(diskDevPath) } - if systemConfig.ReadOnlyVerityRoot.Enable { - defer readOnlyRoot.CleanupVerityDevice() - } - // Add additional system settings for root encryption err = setupDiskEncryption(&systemConfig, &encryptedRoot, buildDir) if err != nil { @@ -265,7 +260,7 @@ func buildSystemConfig(systemConfig configuration.SystemConfig, disks []configur timestamp.StopEvent(nil) // create offline install env err = setupChroot.Run(func() error { - return buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, mountPointToOverlayMap, packagesToInstall, systemConfig, diskDevPath, encryptedRoot, readOnlyRoot, diffDiskBuild, imgContentFile) + return buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, mountPointToOverlayMap, packagesToInstall, systemConfig, diskDevPath, encryptedRoot, diffDiskBuild, imgContentFile) }) if err != nil { err = fmt.Errorf("failed to build image:\n%w", err) @@ -304,7 +299,7 @@ func buildSystemConfig(systemConfig configuration.SystemConfig, disks []configur } } } else { - err = buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, mountPointToOverlayMap, packagesToInstall, systemConfig, diskDevPath, encryptedRoot, readOnlyRoot, diffDiskBuild, imgContentFile) + err = buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, mountPointToOverlayMap, packagesToInstall, systemConfig, diskDevPath, encryptedRoot, diffDiskBuild, imgContentFile) if err != nil { err = fmt.Errorf("failed to build image:\n%w", err) return @@ -370,27 +365,27 @@ func setupRootFS(outputDir, installRoot string) (extraMountPoints []*safechroot. return } -func setupDisk(outputDir, diskName string, liveInstallFlag bool, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption, readOnlyRootConfig configuration.ReadOnlyVerityRoot) (diskDevPath string, partIDToDevPathMap, partIDToFsTypeMap map[string]string, isLoopDevice bool, encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, err error) { +func setupDisk(outputDir, diskName string, liveInstallFlag bool, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption) (diskDevPath string, partIDToDevPathMap, partIDToFsTypeMap map[string]string, isLoopDevice bool, encryptedRoot diskutils.EncryptedRootDevice, err error) { const ( realDiskType = "path" ) if diskConfig.TargetDisk.Type == realDiskType { if liveInstallFlag { diskDevPath = diskConfig.TargetDisk.Value - partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err = setupRealDisk(diskDevPath, - diskConfig, rootEncryption, readOnlyRootConfig, false /*diskKnownToBeEmpty*/) + partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, err = setupRealDisk(diskDevPath, diskConfig, + rootEncryption, false /*diskKnownToBeEmpty*/) } else { err = fmt.Errorf("target Disk Type is set but --live-install option is not set. Please check your config or enable the --live-install option") return } } else { - diskDevPath, partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err = setupLoopDeviceDisk(outputDir, diskName, diskConfig, rootEncryption, readOnlyRootConfig) + diskDevPath, partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, err = setupLoopDeviceDisk(outputDir, diskName, diskConfig, rootEncryption) isLoopDevice = true } return } -func setupLoopDeviceDisk(outputDir, diskName string, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption, readOnlyRootConfig configuration.ReadOnlyVerityRoot) (diskDevPath string, partIDToDevPathMap, partIDToFsTypeMap map[string]string, encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, err error) { +func setupLoopDeviceDisk(outputDir, diskName string, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption) (diskDevPath string, partIDToDevPathMap, partIDToFsTypeMap map[string]string, encryptedRoot diskutils.EncryptedRootDevice, err error) { defer func() { // Detach the loopback device on failure if err != nil && diskDevPath != "" { @@ -414,8 +409,8 @@ func setupLoopDeviceDisk(outputDir, diskName string, diskConfig configuration.Di return } - partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err = setupRealDisk(diskDevPath, diskConfig, - rootEncryption, readOnlyRootConfig, true /*diskKnownToBeEmpty*/) + partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, err = setupRealDisk(diskDevPath, diskConfig, rootEncryption, + true /*diskKnownToBeEmpty*/) if err != nil { err = fmt.Errorf("failed to setup loopback disk partitions (%s):\n%w", rawDisk, err) return @@ -424,12 +419,11 @@ func setupLoopDeviceDisk(outputDir, diskName string, diskConfig configuration.Di return } -func setupRealDisk(diskDevPath string, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption, - readOnlyRootConfig configuration.ReadOnlyVerityRoot, diskKnownToBeEmpty bool, -) (partIDToDevPathMap, partIDToFsTypeMap map[string]string, encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, err error) { +func setupRealDisk(diskDevPath string, diskConfig configuration.Disk, rootEncryption configuration.RootEncryption, diskKnownToBeEmpty bool, +) (partIDToDevPathMap, partIDToFsTypeMap map[string]string, encryptedRoot diskutils.EncryptedRootDevice, err error) { // Set up partitions - partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, readOnlyRoot, err = diskutils.CreatePartitions(diskDevPath, - diskConfig, rootEncryption, readOnlyRootConfig, diskKnownToBeEmpty) + partIDToDevPathMap, partIDToFsTypeMap, encryptedRoot, err = diskutils.CreatePartitions(diskDevPath, diskConfig, + rootEncryption, diskKnownToBeEmpty) if err != nil { err = fmt.Errorf("failed to create partitions on disk (%s):\n%w", diskDevPath, err) return @@ -528,12 +522,11 @@ func cleanupExtraFilesInChroot(chroot *safechroot.Chroot) (err error) { return } -func buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap map[string]string, mountPointToOverlayMap map[string]*installutils.Overlay, packagesToInstall []string, systemConfig configuration.SystemConfig, diskDevPath string, encryptedRoot diskutils.EncryptedRootDevice, readOnlyRoot diskutils.VerityDevice, diffDiskBuild bool, imgContentFile string) (err error) { +func buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap map[string]string, mountPointToOverlayMap map[string]*installutils.Overlay, packagesToInstall []string, systemConfig configuration.SystemConfig, diskDevPath string, encryptedRoot diskutils.EncryptedRootDevice, diffDiskBuild bool, imgContentFile string) (err error) { timestamp.StartEvent("building image", nil) defer timestamp.StopEvent(nil) const ( installRoot = "/installroot" - verityWorkingDir = "verityworkingdir" emptyWorkerTar = "" rootDir = "/" existingChrootDir = true @@ -560,13 +553,6 @@ func buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, setupChrootPackages = append(setupChrootPackages, toolingPackage.Name) } - if systemConfig.ReadOnlyVerityRoot.Enable { - // We will need the veritysetup package (and its dependencies) to manage the verity disk, add them to our - // image setup environment (setuproot chroot or live installer). - verityPackages := []string{"device-mapper", "veritysetup"} - setupChrootPackages = append(setupChrootPackages, verityPackages...) - } - // Create new chroot for the new image installChroot := safechroot.NewChroot(installRoot, existingChrootDir) extraInstallMountPoints := []*safechroot.MountPoint{} @@ -642,12 +628,11 @@ func buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, return } - // Only configure the bootloader or read only partitions for actual disks, a rootfs does not need these + // Only configure the bootloader for actual disks, a rootfs does not need these if !systemConfig.IsRootFS() { err = installutils.ConfigureDiskBootloader(systemConfig.BootType, systemConfig.Encryption.Enable, - systemConfig.ReadOnlyVerityRoot.Enable, systemConfig.PartitionSettings, systemConfig.KernelCommandLine, - installChroot, diskDevPath, mountPointMap, encryptedRoot, readOnlyRoot, - systemConfig.EnableGrubMkconfig, false) + systemConfig.PartitionSettings, systemConfig.KernelCommandLine, installChroot, diskDevPath, mountPointMap, + encryptedRoot, systemConfig.EnableGrubMkconfig, false) if err != nil { err = fmt.Errorf("failed to configure boot loader:\n%w", err) return @@ -664,28 +649,6 @@ func buildImage(mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, } } - // Snapshot the root filesystem as a read-only verity disk and update the initramfs. - if !systemConfig.IsRootFS() && systemConfig.ReadOnlyVerityRoot.Enable { - timestamp.StartEvent("configure DM Verity", nil) - var initramfsPathList []string - err = readOnlyRoot.SwitchDeviceToReadOnly(mountPointMap["/"], mountPointToMountArgsMap["/"]) - if err != nil { - err = fmt.Errorf("failed to switch root to read-only:\n%w", err) - return - } - installutils.ReportAction("Hashing root for read-only with dm-verity, this may take a long time if error correction is enabled") - initramfsPathList, err = filepath.Glob(filepath.Join(installRoot, "/boot/initramfs-*.img")) - if err != nil || len(initramfsPathList) != 1 { - return fmt.Errorf("could not find single initramfs (%v):\n%w", initramfsPathList, err) - } - err = readOnlyRoot.AddRootVerityFilesToInitramfs(verityWorkingDir, initramfsPathList[0]) - if err != nil { - err = fmt.Errorf("failed to include read-only root files in initramfs:\n%w", err) - return - } - timestamp.StopEvent(nil) // configure DM Verity - } - // Run finalize image scripts from within the installroot chroot err = installutils.RunFinalizeImageScripts(installChroot, systemConfig) if err != nil { diff --git a/toolkit/tools/internal/resources/assets/grub2/grub b/toolkit/tools/internal/resources/assets/grub2/grub index 6d91828b17e..ab2cdd88a08 100644 --- a/toolkit/tools/internal/resources/assets/grub2/grub +++ b/toolkit/tools/internal/resources/assets/grub2/grub @@ -2,9 +2,9 @@ GRUB_TIMEOUT=0 GRUB_DISTRIBUTOR="AzureLinux" GRUB_DISABLE_SUBMENU=y GRUB_TERMINAL_OUTPUT="console" -GRUB_CMDLINE_LINUX="{{.LuksUUID}} {{.LVM}} {{.IMAPolicy}} {{.ReadOnlyVerityRoot}} {{.SELinux}} {{.FIPS}} rd.auto=1 net.ifnames=0 lockdown=integrity {{.CGroup}}" +GRUB_CMDLINE_LINUX="{{.LuksUUID}} {{.LVM}} {{.IMAPolicy}} {{.SELinux}} {{.FIPS}} rd.auto=1 net.ifnames=0 lockdown=integrity {{.CGroup}}" GRUB_CMDLINE_LINUX_DEFAULT="{{.ExtraCommandLine}} \$kernelopts" - + # =============================notice=============================== # IMPORTANT: package and feature-specific behaviors are defined in # /etc/default/grub.d/*.cfg. The cfg files are sourced last @@ -13,7 +13,7 @@ GRUB_CMDLINE_LINUX_DEFAULT="{{.ExtraCommandLine}} \$kernelopts" # Linux commandline is: # - first GRUB_CMDLINE_LINUX # - then /etc/default/grub.d/*.cfg -# - and finally GRUB_CMDLINE_LINUX_DEFAULT +# - and finally GRUB_CMDLINE_LINUX_DEFAULT # =============================notice=============================== for x in /etc/default/grub.d/*.cfg ; do if [ -e "${x}" ]; then diff --git a/toolkit/tools/internal/resources/assets/grub2/grub.cfg b/toolkit/tools/internal/resources/assets/grub2/grub.cfg index 78298a98c10..b9a160679ea 100644 --- a/toolkit/tools/internal/resources/assets/grub2/grub.cfg +++ b/toolkit/tools/internal/resources/assets/grub2/grub.cfg @@ -19,8 +19,8 @@ fi set rootdevice={{.RootPartition}} menuentry "Azure Linux" { - linux $bootprefix/$mariner_linux {{.LuksUUID}} {{.LVM}} {{.IMAPolicy}} {{.ReadOnlyVerityRoot}} {{.SELinux}} {{.FIPS}} rd.auto=1 root=$rootdevice $mariner_cmdline lockdown=integrity sysctl.kernel.unprivileged_bpf_disabled=1 $systemd_cmdline {{.CGroup}} {{.ExtraCommandLine}} $kernelopts + linux $bootprefix/$mariner_linux {{.LuksUUID}} {{.LVM}} {{.IMAPolicy}} {{.SELinux}} {{.FIPS}} rd.auto=1 root=$rootdevice $mariner_cmdline lockdown=integrity sysctl.kernel.unprivileged_bpf_disabled=1 $systemd_cmdline {{.CGroup}} {{.ExtraCommandLine}} $kernelopts if [ -f $bootprefix/$mariner_initrd ]; then initrd $bootprefix/$mariner_initrd fi -} \ No newline at end of file +} diff --git a/toolkit/tools/internal/safemount/safemount_test.go b/toolkit/tools/internal/safemount/safemount_test.go index 4ef4d24b9a6..b6ae8722936 100644 --- a/toolkit/tools/internal/safemount/safemount_test.go +++ b/toolkit/tools/internal/safemount/safemount_test.go @@ -66,8 +66,8 @@ func TestResourceBusy(t *testing.T) { defer loopback.Close() // Set up partitions. - _, _, _, _, err = diskutils.CreatePartitions(loopback.DevicePath(), diskConfig, - configuration.RootEncryption{}, configuration.ReadOnlyVerityRoot{}, true /*diskKnownToBeEmpty*/) + _, _, _, err = diskutils.CreatePartitions(loopback.DevicePath(), diskConfig, + configuration.RootEncryption{}, true /*diskKnownToBeEmpty*/) if !assert.NoError(t, err, "failed to create partitions on disk", loopback.DevicePath()) { return } diff --git a/toolkit/tools/pkg/imagecustomizerlib/imageutils.go b/toolkit/tools/pkg/imagecustomizerlib/imageutils.go index 7ff6e306941..03a58c85782 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/imageutils.go +++ b/toolkit/tools/pkg/imagecustomizerlib/imageutils.go @@ -149,9 +149,9 @@ func configureDiskBootLoader(imageConnection *ImageConnection, rootMountIdType i } // Configure the boot loader. - err = installutils.ConfigureDiskBootloaderWithRootMountIdType(imagerBootType, false, false, imagerRootMountIdType, + err = installutils.ConfigureDiskBootloaderWithRootMountIdType(imagerBootType, false, imagerRootMountIdType, imagerKernelCommandLine, imageConnection.Chroot(), imageConnection.Loopback().DevicePath(), - mountPointMap, diskutils.EncryptedRootDevice{}, diskutils.VerityDevice{}, grubMkconfigEnabled, + mountPointMap, diskutils.EncryptedRootDevice{}, grubMkconfigEnabled, !grubMkconfigEnabled) if err != nil { return fmt.Errorf("failed to install bootloader:\n%w", err) @@ -176,9 +176,9 @@ func createImageBoilerplate(imageConnection *ImageConnection, filename string, b } // Set up partitions. - partIDToDevPathMap, partIDToFsTypeMap, _, _, err := diskutils.CreatePartitions( + partIDToDevPathMap, partIDToFsTypeMap, _, err := diskutils.CreatePartitions( imageConnection.Loopback().DevicePath(), imagerDiskConfig, configuration.RootEncryption{}, - configuration.ReadOnlyVerityRoot{}, true /*diskKnownToBeEmpty*/) + true /*diskKnownToBeEmpty*/) if err != nil { return nil, "", fmt.Errorf("failed to create partitions on disk (%s):\n%w", imageConnection.Loopback().DevicePath(), err) }