From a0fe7d5dc03ab0291cc57a36710c6e515124ae80 Mon Sep 17 00:00:00 2001 From: Amit Barve Date: Thu, 11 Sep 2025 12:12:33 -0400 Subject: [PATCH] Minor bug fixes 1. uvmboot tool wasn't using absolute paths for the VHDs, this fixes it to use absolute paths. 2. Name of the VHD that contains the EFI partition for the confidential WCOW UVM was incorrectly changed to efi.vhd from boot.vhd. Revert that. 3. A new check was added for non base layer UtilityVM files during image pull. This scenario isn't supported but we shouldn't fail the entire image pull due to this. The image can be successfully pulled and used for a container even if it has utilityVM files in non-base layers. You just won't be able to use that image for running utilityVMs. In majority of the cases UtilityVM anyway uses the nanoserver image so this should be fine. 4. Initialize the mounted block CIMs tracking map. 5. Add a writable-efi flag to uvmboot to allow attaching EFI/boot VHD as read-only read-write 6. Fix incorrect UVM creation options type switch case. Signed-off-by: Amit Barve --- internal/tools/uvmboot/conf_wcow.go | 32 +++++++++++++++++++----- internal/uvm/create_wcow.go | 3 ++- internal/uvm/security_policy.go | 7 +++--- internal/wclayer/cim/block_cim_writer.go | 17 +++++++------ 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/internal/tools/uvmboot/conf_wcow.go b/internal/tools/uvmboot/conf_wcow.go index ea609b34ac..086f090ae1 100644 --- a/internal/tools/uvmboot/conf_wcow.go +++ b/internal/tools/uvmboot/conf_wcow.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "os" + "path/filepath" "github.com/containerd/console" "github.com/opencontainers/runtime-spec/specs-go" @@ -21,6 +22,7 @@ const ( vmgsFilePathArgName = "vmgs-path" disableSBArgName = "disable-secure-boot" isolationTypeArgName = "isolation-type" + writableEFIArgName = "writable-efi" // default policy (that allows all operations) used when no policy is provided allowAllPolicy = "cGFja2FnZSBwb2xpY3kKCmFwaV92ZXJzaW9uIDo9ICIwLjExLjAiCmZyYW1ld29ya192ZXJzaW9uIDo9ICIwLjQuMCIKCm1vdW50X2NpbXMgOj0geyJhbGxvd2VkIjogdHJ1ZX0KbW91bnRfZGV2aWNlIDo9IHsiYWxsb3dlZCI6IHRydWV9Cm1vdW50X292ZXJsYXkgOj0geyJhbGxvd2VkIjogdHJ1ZX0KY3JlYXRlX2NvbnRhaW5lciA6PSB7ImFsbG93ZWQiOiB0cnVlLCAiZW52X2xpc3QiOiBudWxsLCAiYWxsb3dfc3RkaW9fYWNjZXNzIjogdHJ1ZX0KdW5tb3VudF9kZXZpY2UgOj0geyJhbGxvd2VkIjogdHJ1ZX0KdW5tb3VudF9vdmVybGF5IDo9IHsiYWxsb3dlZCI6IHRydWV9CmV4ZWNfaW5fY29udGFpbmVyIDo9IHsiYWxsb3dlZCI6IHRydWUsICJlbnZfbGlzdCI6IG51bGx9CmV4ZWNfZXh0ZXJuYWwgOj0geyJhbGxvd2VkIjogdHJ1ZSwgImVudl9saXN0IjogbnVsbCwgImFsbG93X3N0ZGlvX2FjY2VzcyI6IHRydWV9CnNodXRkb3duX2NvbnRhaW5lciA6PSB7ImFsbG93ZWQiOiB0cnVlfQpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0geyJhbGxvd2VkIjogdHJ1ZX0KcGxhbjlfbW91bnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0KcGxhbjlfdW5tb3VudCA6PSB7ImFsbG93ZWQiOiB0cnVlfQpnZXRfcHJvcGVydGllcyA6PSB7ImFsbG93ZWQiOiB0cnVlfQpkdW1wX3N0YWNrcyA6PSB7ImFsbG93ZWQiOiB0cnVlfQpydW50aW1lX2xvZ2dpbmcgOj0geyJhbGxvd2VkIjogdHJ1ZX0KbG9hZF9mcmFnbWVudCA6PSB7ImFsbG93ZWQiOiB0cnVlfQpzY3JhdGNoX21vdW50IDo9IHsiYWxsb3dlZCI6IHRydWV9CnNjcmF0Y2hfdW5tb3VudCA6PSB7ImFsbG93ZWQiOiB0cnVlfQo=" @@ -34,6 +36,7 @@ var ( cwcowDisableSecureBoot bool cwcowIsolationMode string cwcowSecurityPolicy string + cwcowWritableEFI bool ) var cwcowCommand = cli.Command{ @@ -91,6 +94,11 @@ var cwcowCommand = cli.Command{ Destination: &cwcowSecurityPolicy, Value: allowAllPolicy, }, + cli.BoolFlag{ + Name: writableEFIArgName, + Usage: "Attaches the EFI VHD as read-write instead of read-only. This allows the UVM to modify the contents of the VHD, be careful when using this option!", + Destination: &cwcowWritableEFI, + }, }, Action: func(c *cli.Context) error { runMany(c, func(id string) error { @@ -99,7 +107,6 @@ var cwcowCommand = cli.Command{ options.MemorySizeInMB = 2048 options.AllowOvercommit = false options.EnableDeferredCommit = false - options.DumpDirectoryPath = "C:\\crashdumps" // confidential specific options options.SecurityPolicyEnabled = true @@ -109,6 +116,24 @@ var cwcowCommand = cli.Command{ options.IsolationType = cwcowIsolationMode // always enable graphics console with uvmboot - helps with testing/debugging options.EnableGraphicsConsole = true + options.WritableEFI = cwcowWritableEFI + + var err error + cwcowBootVHD, err = filepath.Abs(cwcowBootVHD) + if err != nil { + return err + } + + cwcowEFIVHD, err = filepath.Abs(cwcowEFIVHD) + if err != nil { + return err + } + + cwcowScratchVHD, err = filepath.Abs(cwcowScratchVHD) + if err != nil { + return err + } + options.BootFiles = &uvm.WCOWBootFiles{ BootType: uvm.BlockCIMBoot, BlockCIMFiles: &uvm.BlockCIMBootFiles{ @@ -118,11 +143,6 @@ var cwcowCommand = cli.Command{ }, } setGlobalOptions(c, options.Options) - tempDir, err := os.MkdirTemp("", "uvmboot") - if err != nil { - return err - } - defer os.RemoveAll(tempDir) vm, err := uvm.CreateWCOW(context.TODO(), options) if err != nil { diff --git a/internal/uvm/create_wcow.go b/internal/uvm/create_wcow.go index aedd960776..0ee9274225 100644 --- a/internal/uvm/create_wcow.go +++ b/internal/uvm/create_wcow.go @@ -74,7 +74,7 @@ func GetDefaultConfidentialBootCIMPath() string { } func GetDefaultConfidentialEFIPath() string { - return filepath.Join(defaultConfidentialWCOWOSBootFilesPath(), "efi.vhd") + return filepath.Join(defaultConfidentialWCOWOSBootFilesPath(), "boot.vhd") } // NewDefaultOptionsWCOW creates the default options for a bootable version of @@ -509,6 +509,7 @@ func CreateWCOW(ctx context.Context, opts *OptionsWCOW) (_ *UtilityVM, err error vsmbNoDirectMap: opts.NoDirectMap, noWritableFileShares: opts.NoWritableFileShares, createOpts: *opts, + blockCIMMounts: make(map[string]*UVMMountedBlockCIMs), } defer func() { diff --git a/internal/uvm/security_policy.go b/internal/uvm/security_policy.go index 950179acc4..2346864afe 100644 --- a/internal/uvm/security_policy.go +++ b/internal/uvm/security_policy.go @@ -124,10 +124,11 @@ func (uvm *UtilityVM) InjectPolicyFragment(ctx context.Context, fragment *ctrdta // returns if this instance of the UtilityVM is created with confidential policy func (uvm *UtilityVM) HasConfidentialPolicy() bool { switch opts := uvm.createOpts.(type) { - case *OptionsWCOW: + case OptionsWCOW: return opts.SecurityPolicyEnabled - case *OptionsLCOW: + case OptionsLCOW: return opts.SecurityPolicyEnabled + default: + panic("unexpected options type") } - return false } diff --git a/internal/wclayer/cim/block_cim_writer.go b/internal/wclayer/cim/block_cim_writer.go index 334e391366..16f7c9552d 100644 --- a/internal/wclayer/cim/block_cim_writer.go +++ b/internal/wclayer/cim/block_cim_writer.go @@ -100,13 +100,16 @@ func (cw *BlockCIMLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo, f if name == wclayer.UtilityVMPath && len(cw.parentLayers) > 0 { // If there are UtilityVM files in non base layers, we will have to merge // those files with the parent layer UtilityVM files - either during image - // pull or at runtime (i.e when starting the UVM). In order to merge at image pull time, we will have - // to read parent layer block CIMs and copy all the UtilityVM files from - // those CIMs into this block CIM one by one i.e effectively merge all - // parent layer UtilityVM files in this layer. Or we will need to be able - // to boot the UtilityVM with merged block CIMs. None of these options are - // implemented yet so error out if we see that. - return fmt.Errorf("UtilityVM files in non base layers is not supported for block CIMs") + // pull or at runtime (i.e when starting the UVM). In order to merge at + // image pull time, we will have to read parent layer block CIMs and copy + // all the UtilityVM files from those CIMs into this block CIM one by one + // i.e effectively merge all parent layer UtilityVM files in this + // layer. Or we will need to be able to boot the UtilityVM with merged + // block CIMs. None of these options are implemented yet so log a + // warning. However, this shouldn't cause issues with most of the standard + // use cases because usually the pod is a nanoserver image and that is + // always a single layer. + log.G(cw.ctx).Warn("UtilityVM files in non base layers is not supported for block CIMs") } return cw.cimLayerWriter.Add(name, fileInfo, fileSize, securityDescriptor, extendedAttributes, reparseData) }