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

Initial support for creating confidential windows UtilityVMs #2388

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions internal/hcs/schema2/chipset.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ type Chipset struct {

// LinuxKernelDirect - Added in v2.2 Builds >=181117
LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"`

FirmwareFile *FirmwareFile `json:"FirmwareFile,omitempty"`
}
5 changes: 5 additions & 0 deletions internal/hcs/schema2/firmware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package hcsschema

type FirmwareFile struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a note saying this is experimental?

Parameters []byte `json:"Parameters,omitempty"`
}
2 changes: 2 additions & 0 deletions internal/hcs/schema2/windows_crash_reporting.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ type WindowsCrashReporting struct {
DumpFileName string `json:"DumpFileName,omitempty"`

MaxDumpSize int64 `json:"MaxDumpSize,omitempty"`

DumpType string `json:"DumpType,omitempty"`
}
9 changes: 6 additions & 3 deletions internal/layers/wcow_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,11 @@ func GetWCOWUVMBootFilesFromLayers(ctx context.Context, rootfs []*types.Mount, l
}
}
return &uvm.WCOWBootFiles{
OSFilesPath: filepath.Join(uvmFolder, `UtilityVM\Files`),
OSRelativeBootDirPath: `\EFI\Microsoft\Boot`,
ScratchVHDPath: scratchVHDPath,
BootType: uvm.VmbFSBoot,
VmbFSFiles: &uvm.VmbFSBootFiles{
OSFilesPath: filepath.Join(uvmFolder, `UtilityVM\Files`),
OSRelativeBootDirPath: `\EFI\Microsoft\Boot`,
ScratchVHDPath: scratchVHDPath,
},
}, nil
}
156 changes: 156 additions & 0 deletions internal/tools/uvmboot/conf_wcow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//go:build windows

package main

import (
"context"
"os"

"github.com/containerd/console"
"github.com/urfave/cli"

"github.com/Microsoft/hcsshim/internal/cmd"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/uvm"
)

const (
confidentialArgName = "confidential"
vmgsFilePathArgName = "vmgs-path"
disableSBArgName = "disable-secure-boot"
isolationTypeArgName = "isolation-type"
)

var (
cwcowBootVHD string
cwcowEFIVHD string
cwcowScratchVHD string
cwcowEnableConfidential bool

Check failure on line 28 in internal/tools/uvmboot/conf_wcow.go

View workflow job for this annotation

GitHub Actions / lint (windows)

var `cwcowEnableConfidential` is unused (unused)
cwcowVMGSPath string
cwcowDisableSecureBoot bool
cwcowIsolationMode string
)

var cwcowCommand = cli.Command{
Name: "cwcow",
Usage: "boot a confidential WCOW UVM",
Flags: []cli.Flag{
cli.StringFlag{
Name: "exec",
Usage: "Command to execute in the UVM.",
Destination: &wcowCommandLine,
},
cli.BoolFlag{
Name: "tty,t",
Usage: "create the process in the UVM with a TTY enabled",
Destination: &wcowUseTerminal,
},
cli.StringFlag{
Name: "efi-vhd",
Usage: "VHD at the provided path MUST have the EFI boot partition and be properly formatted for UEFI boot.",
Destination: &cwcowEFIVHD,
Required: true,
},
cli.StringFlag{
Name: "boot-cim-vhd",
Usage: "A VHD containing the block CIM that contains the OS files.",
Destination: &cwcowBootVHD,
Required: true,
},
cli.StringFlag{
Name: "scratch-vhd",
Usage: "A scratch VHD for the UVM",
Destination: &cwcowScratchVHD,
Required: true,
},
cli.StringFlag{
Name: vmgsFilePathArgName,
Usage: "VMGS file path (only applies when confidential mode is enabled). This option is only applicable in confidential mode.",
Destination: &cwcowVMGSPath,
Required: true,
},
cli.BoolFlag{
Name: disableSBArgName,
Usage: "Disables Secure Boot when running the UVM in confidential mode. This option is only applicable in confidential mode.",
Destination: &cwcowDisableSecureBoot,
},
cli.StringFlag{
Name: isolationTypeArgName,
Usage: "VM Isolation type (one of Disabled, GuestStateOnly, VirtualizationBasedSecurity, SecureNestedPaging or TrustDomain). Applicable only when using the confidential mode. This option is only applicable in confidential mode.",
Destination: &cwcowIsolationMode,
Required: true,
},
},
Action: func(c *cli.Context) error {
runMany(c, func(id string) error {
options := uvm.NewDefaultOptionsWCOW(id, "")
options.ProcessorCount = 2
options.MemorySizeInMB = 2048
options.AllowOvercommit = false
options.EnableDeferredCommit = false
options.DumpDirectoryPath = "C:\\crashdumps"

// confidential specific options
options.SecurityPolicyEnabled = true
options.DisableSecureBoot = cwcowDisableSecureBoot
options.GuestStateFilePath = cwcowVMGSPath
options.IsolationType = cwcowIsolationMode
// always enable graphics console with uvmboot - helps with testing/debugging
options.EnableGraphicsConsole = true
options.BootFiles = &uvm.WCOWBootFiles{
BootType: uvm.BlockCIMBoot,
BlockCIMFiles: &uvm.BlockCIMBootFiles{
BootCIMVHDPath: cwcowBootVHD,
EFIVHDPath: cwcowEFIVHD,
ScratchVHDPath: cwcowScratchVHD,
},
}
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 {
return err
}
defer vm.Close()
if err := vm.Start(context.TODO()); err != nil {
return err
}
if wcowCommandLine != "" {
cmd := cmd.Command(vm, "cmd.exe", "/c", wcowCommandLine)
cmd.Spec.User.Username = `NT AUTHORITY\SYSTEM`
cmd.Log = log.L.Dup()
if wcowUseTerminal {
cmd.Spec.Terminal = true
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
con, err := console.ConsoleFromFile(os.Stdin)
if err == nil {
err = con.SetRaw()
if err != nil {
return err
}
defer func() {
_ = con.Reset()
}()
}
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stdout
}
err = cmd.Run()
if err != nil {
return err
}
}
_ = vm.Terminate(context.TODO())
_ = vm.Wait()
return vm.ExitError()
})
return nil
},
}
6 changes: 6 additions & 0 deletions internal/tools/uvmboot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func main() {
app.Commands = []cli.Command{
lcowCommand,
wcowCommand,
cwcowCommand,
}

app.Before = func(c *cli.Context) error {
Expand Down Expand Up @@ -120,6 +121,11 @@ func setGlobalOptions(c *cli.Context, options *uvm.Options) {
if c.GlobalIsSet(enableDeferredCommitArgName) {
options.EnableDeferredCommit = c.GlobalBool(enableDeferredCommitArgName)
}
if c.GlobalIsSet(enableDeferredCommitArgName) {
options.EnableDeferredCommit = c.GlobalBool(enableDeferredCommitArgName)
}
// Always set the console pipe in uvmboot, it helps with testing/debugging
options.ConsolePipe = "\\\\.\\pipe\\uvmpipe"
}

// todo: add a context here to propagate cancel/timeouts to runFunc uvm
Expand Down
28 changes: 28 additions & 0 deletions internal/uvm/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@
NumaProcessorCounts []uint32
// NumaMemoryBlocksCounts are the number of memory blocks per vNUMA node.
NumaMemoryBlocksCounts []uint64

EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM
ConsolePipe string // The named pipe path to use for the serial console (COM1). eg \\.\pipe\vmpipe
}

func verifyWCOWBootFiles(bootFiles *WCOWBootFiles) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make this a method and move it to create_wcow.go?

Suggested change
func verifyWCOWBootFiles(bootFiles *WCOWBootFiles) error {
func (bf *WCOWBootFiles) verify() error {
if bf == nil { return nil }
// ...

if bootFiles.BootType == VmbFSBoot {
if bootFiles.VmbFSFiles == nil {
return fmt.Errorf("VmbFS boot files is empty")
} else if bootFiles.BlockCIMFiles != nil {
return fmt.Errorf("confidential boot files should be empty")
}
} else if bootFiles.BootType == BlockCIMBoot {
if bootFiles.BlockCIMFiles == nil {
return fmt.Errorf("Confidential boot files is empty")

Check failure on line 139 in internal/uvm/create.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1005: error strings should not be capitalized (stylecheck)
} else if bootFiles.VmbFSFiles != nil {
return fmt.Errorf("VmbFS boot files should be empty")
}
} else {
return fmt.Errorf("invalid boot type specified")
}
return nil
Comment on lines +143 to +146
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else {
return fmt.Errorf("invalid boot type specified")
}
return nil
}
return fmt.Errorf("invalid boot type specified: %s", bootFiles.BootType)

might be cleaner overall as:

switch bootFiles.BootType {
case VmbFSBoot:
  // ...
case BlockCIMBoot:
  // ...
default:
}
return fmt.Errorf("invalid boot type specified: %s", bootFiles.BootType)

}

// Verifies that the final UVM options are correct and supported.
Expand Down Expand Up @@ -156,6 +178,12 @@
if opts.SCSIControllerCount != 1 {
return errors.New("exactly 1 SCSI controller is required for WCOW")
}
if err := verifyWCOWBootFiles(opts.BootFiles); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may also want to have a nil check here, just in case someone did something funky with the options

return err
}
if opts.SecurityPolicyEnabled && opts.GuestStateFilePath == "" {
return fmt.Errorf("GuestStateFilePath must be provided when enabling security policy")
}
}
return nil
}
Expand Down
4 changes: 0 additions & 4 deletions internal/uvm/create_lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ type OptionsLCOW struct {
KernelDirect bool // Skip UEFI and boot directly to `kernel`
RootFSFile string // Filename under `BootFilesPath` for the UVMs root file system. Defaults to `InitrdFile`
KernelBootOptions string // Additional boot options for the kernel
EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM
ConsolePipe string // The named pipe path to use for the serial console. eg \\.\pipe\vmpipe
UseGuestConnection bool // Whether the HCS should connect to the UVM's GCS. Defaults to true
ExecCommandLine string // The command line to exec from init. Defaults to GCS
ForwardStdout bool // Whether stdout will be forwarded from the executed program. Defaults to false
Expand Down Expand Up @@ -164,8 +162,6 @@ func NewDefaultOptionsLCOW(id, owner string) *OptionsLCOW {
KernelDirect: kernelDirectSupported,
RootFSFile: InitrdFile,
KernelBootOptions: "",
EnableGraphicsConsole: false,
ConsolePipe: "",
UseGuestConnection: true,
ExecCommandLine: fmt.Sprintf("/bin/gcs -v4 -log-format json -loglevel %s", logrus.StandardLogger().Level.String()),
ForwardStdout: false,
Expand Down
Loading
Loading