Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove all references to old dm-verity boot tooling [1/3] #10973

Open
wants to merge 2 commits into
base: 3.0-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 4 additions & 53 deletions toolkit/docs/formats/imageconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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 `<Name>.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

Expand Down
2 changes: 0 additions & 2 deletions toolkit/docs/security/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
109 changes: 2 additions & 107 deletions toolkit/docs/security/read-only-roots.md
Original file line number Diff line number Diff line change
@@ -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.
26 changes: 4 additions & 22 deletions toolkit/tools/imageconfigvalidator/imageconfigvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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",
},
},
},
},
}

Expand Down
3 changes: 0 additions & 3 deletions toolkit/tools/imagegen/attendedinstaller/uitext/uitext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 `/`"
Expand Down Expand Up @@ -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"
Expand Down
Loading
Loading