Skip to content

Commit a00144a

Browse files
committed
Add support for running confidential WCOW UVMs
Initial changes to allow creating confidential WCOW UVMs. uvmboot tool is also updated for easier command line testing of confidential UVMs. Signed-off-by: Amit Barve <[email protected]>
1 parent 5def1d7 commit a00144a

File tree

10 files changed

+418
-73
lines changed

10 files changed

+418
-73
lines changed

internal/hcs/schema2/chipset.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ type Chipset struct {
2424

2525
// LinuxKernelDirect - Added in v2.2 Builds >=181117
2626
LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"`
27+
28+
FirmwareFile *FirmwareFile `json:"FirmwareFile,omitempty"`
2729
}

internal/hcs/schema2/firmware.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package hcsschema
2+
3+
type FirmwareFile struct {
4+
// Parameters is an experimental/pre-release field. The field itself or its
5+
// behavior can change in future iterations of the schema. Avoid taking a hard
6+
// dependency on this field.
7+
Parameters []byte `json:"Parameters,omitempty"`
8+
}

internal/hcs/schema2/windows_crash_reporting.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ type WindowsCrashReporting struct {
1313
DumpFileName string `json:"DumpFileName,omitempty"`
1414

1515
MaxDumpSize int64 `json:"MaxDumpSize,omitempty"`
16+
17+
DumpType string `json:"DumpType,omitempty"`
1618
}

internal/layers/wcow_parse.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/Microsoft/hcsshim/internal/copyfile"
1717
"github.com/Microsoft/hcsshim/internal/uvm"
1818
"github.com/Microsoft/hcsshim/internal/uvmfolder"
19+
"github.com/Microsoft/hcsshim/internal/wclayer"
1920
"github.com/Microsoft/hcsshim/pkg/cimfs"
2021
)
2122

@@ -257,16 +258,16 @@ func GetWCOWUVMBootFilesFromLayers(ctx context.Context, rootfs []*types.Mount, l
257258
}
258259

259260
if _, err = os.Stat(scratchVHDPath); os.IsNotExist(err) {
260-
sourceScratch := filepath.Join(uvmFolder, `UtilityVM\SystemTemplate.vhdx`)
261+
sourceScratch := filepath.Join(uvmFolder, wclayer.UtilityVMPath, wclayer.UtilityVMScratchVhd)
261262
if err := copyfile.CopyFile(ctx, sourceScratch, scratchVHDPath, true); err != nil {
262263
return nil, err
263264
}
264265
}
265266
return &uvm.WCOWBootFiles{
266267
BootType: uvm.VmbFSBoot,
267268
VmbFSFiles: &uvm.VmbFSBootFiles{
268-
OSFilesPath: filepath.Join(uvmFolder, `UtilityVM\Files`),
269-
OSRelativeBootDirPath: `\EFI\Microsoft\Boot`,
269+
OSFilesPath: filepath.Join(uvmFolder, wclayer.UtilityVMFilesPath),
270+
OSRelativeBootDirPath: wclayer.BootDirRelativePath,
270271
ScratchVHDPath: scratchVHDPath,
271272
},
272273
}, nil

internal/tools/uvmboot/conf_wcow.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//go:build windows
2+
3+
package main
4+
5+
import (
6+
"context"
7+
"os"
8+
9+
"github.com/containerd/console"
10+
"github.com/urfave/cli"
11+
12+
"github.com/Microsoft/hcsshim/internal/cmd"
13+
"github.com/Microsoft/hcsshim/internal/log"
14+
"github.com/Microsoft/hcsshim/internal/uvm"
15+
)
16+
17+
const (
18+
confidentialArgName = "confidential"
19+
vmgsFilePathArgName = "vmgs-path"
20+
disableSBArgName = "disable-secure-boot"
21+
isolationTypeArgName = "isolation-type"
22+
)
23+
24+
var (
25+
cwcowBootVHD string
26+
cwcowEFIVHD string
27+
cwcowScratchVHD string
28+
cwcowVMGSPath string
29+
cwcowDisableSecureBoot bool
30+
cwcowIsolationMode string
31+
)
32+
33+
var cwcowCommand = cli.Command{
34+
Name: "cwcow",
35+
Usage: "boot a confidential WCOW UVM",
36+
Flags: []cli.Flag{
37+
cli.StringFlag{
38+
Name: "exec",
39+
Usage: "Command to execute in the UVM.",
40+
Destination: &wcowCommandLine,
41+
},
42+
cli.BoolFlag{
43+
Name: "tty,t",
44+
Usage: "create the process in the UVM with a TTY enabled",
45+
Destination: &wcowUseTerminal,
46+
},
47+
cli.StringFlag{
48+
Name: "efi-vhd",
49+
Usage: "VHD at the provided path MUST have the EFI boot partition and be properly formatted for UEFI boot.",
50+
Destination: &cwcowEFIVHD,
51+
Required: true,
52+
},
53+
cli.StringFlag{
54+
Name: "boot-cim-vhd",
55+
Usage: "A VHD containing the block CIM that contains the OS files.",
56+
Destination: &cwcowBootVHD,
57+
Required: true,
58+
},
59+
cli.StringFlag{
60+
Name: "scratch-vhd",
61+
Usage: "A scratch VHD for the UVM",
62+
Destination: &cwcowScratchVHD,
63+
Required: true,
64+
},
65+
cli.StringFlag{
66+
Name: vmgsFilePathArgName,
67+
Usage: "VMGS file path (only applies when confidential mode is enabled). This option is only applicable in confidential mode.",
68+
Destination: &cwcowVMGSPath,
69+
Required: true,
70+
},
71+
cli.BoolFlag{
72+
Name: disableSBArgName,
73+
Usage: "Disables Secure Boot when running the UVM in confidential mode. This option is only applicable in confidential mode.",
74+
Destination: &cwcowDisableSecureBoot,
75+
},
76+
cli.StringFlag{
77+
Name: isolationTypeArgName,
78+
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.",
79+
Destination: &cwcowIsolationMode,
80+
Required: true,
81+
},
82+
},
83+
Action: func(c *cli.Context) error {
84+
runMany(c, func(id string) error {
85+
options := uvm.NewDefaultOptionsWCOW(id, "")
86+
options.ProcessorCount = 2
87+
options.MemorySizeInMB = 2048
88+
options.AllowOvercommit = false
89+
options.EnableDeferredCommit = false
90+
options.DumpDirectoryPath = "C:\\crashdumps"
91+
92+
// confidential specific options
93+
options.SecurityPolicyEnabled = true
94+
options.DisableSecureBoot = cwcowDisableSecureBoot
95+
options.GuestStateFilePath = cwcowVMGSPath
96+
options.IsolationType = cwcowIsolationMode
97+
// always enable graphics console with uvmboot - helps with testing/debugging
98+
options.EnableGraphicsConsole = true
99+
options.BootFiles = &uvm.WCOWBootFiles{
100+
BootType: uvm.BlockCIMBoot,
101+
BlockCIMFiles: &uvm.BlockCIMBootFiles{
102+
BootCIMVHDPath: cwcowBootVHD,
103+
EFIVHDPath: cwcowEFIVHD,
104+
ScratchVHDPath: cwcowScratchVHD,
105+
},
106+
}
107+
setGlobalOptions(c, options.Options)
108+
tempDir, err := os.MkdirTemp("", "uvmboot")
109+
if err != nil {
110+
return err
111+
}
112+
defer os.RemoveAll(tempDir)
113+
114+
vm, err := uvm.CreateWCOW(context.TODO(), options)
115+
if err != nil {
116+
return err
117+
}
118+
defer vm.Close()
119+
if err := vm.Start(context.TODO()); err != nil {
120+
return err
121+
}
122+
if wcowCommandLine != "" {
123+
cmd := cmd.Command(vm, "cmd.exe", "/c", wcowCommandLine)
124+
cmd.Spec.User.Username = `NT AUTHORITY\SYSTEM`
125+
cmd.Log = log.L.Dup()
126+
if wcowUseTerminal {
127+
cmd.Spec.Terminal = true
128+
cmd.Stdin = os.Stdin
129+
cmd.Stdout = os.Stdout
130+
con, err := console.ConsoleFromFile(os.Stdin)
131+
if err == nil {
132+
err = con.SetRaw()
133+
if err != nil {
134+
return err
135+
}
136+
defer func() {
137+
_ = con.Reset()
138+
}()
139+
}
140+
} else {
141+
cmd.Stdout = os.Stdout
142+
cmd.Stderr = os.Stdout
143+
}
144+
err = cmd.Run()
145+
if err != nil {
146+
return err
147+
}
148+
}
149+
_ = vm.Terminate(context.TODO())
150+
_ = vm.Wait()
151+
return vm.ExitError()
152+
})
153+
return nil
154+
},
155+
}

internal/tools/uvmboot/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
countArgName = "count"
2727

2828
execCommandLineArgName = "exec"
29+
uvmConsolePipe = "\\\\.\\pipe\\uvmpipe"
2930
)
3031

3132
var (
@@ -86,6 +87,7 @@ func main() {
8687
app.Commands = []cli.Command{
8788
lcowCommand,
8889
wcowCommand,
90+
cwcowCommand,
8991
}
9092

9193
app.Before = func(c *cli.Context) error {
@@ -120,6 +122,11 @@ func setGlobalOptions(c *cli.Context, options *uvm.Options) {
120122
if c.GlobalIsSet(enableDeferredCommitArgName) {
121123
options.EnableDeferredCommit = c.GlobalBool(enableDeferredCommitArgName)
122124
}
125+
if c.GlobalIsSet(enableDeferredCommitArgName) {
126+
options.EnableDeferredCommit = c.GlobalBool(enableDeferredCommitArgName)
127+
}
128+
// Always set the console pipe in uvmboot, it helps with testing/debugging
129+
options.ConsolePipe = uvmConsolePipe
123130
}
124131

125132
// todo: add a context here to propagate cancel/timeouts to runFunc uvm

internal/uvm/create.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,26 @@ type Options struct {
122122
NumaProcessorCounts []uint32
123123
// NumaMemoryBlocksCounts are the number of memory blocks per vNUMA node.
124124
NumaMemoryBlocksCounts []uint64
125+
126+
EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM
127+
ConsolePipe string // The named pipe path to use for the serial console (COM1). eg \\.\pipe\vmpipe
125128
}
126129

127130
func verifyWCOWBootFiles(bootFiles *WCOWBootFiles) error {
128-
if bootFiles.BootType == VmbFSBoot {
131+
if bootFiles == nil {
132+
return fmt.Errorf("boot files is nil")
133+
}
134+
switch bootFiles.BootType {
135+
case VmbFSBoot:
129136
if bootFiles.VmbFSFiles == nil {
130137
return fmt.Errorf("VmbFS boot files is empty")
131-
} else if bootFiles.BlockCIMFiles != nil {
132-
return fmt.Errorf("confidential boot files should be empty")
133138
}
134-
} else if bootFiles.BootType == BlockCIMBoot {
139+
case BlockCIMBoot:
135140
if bootFiles.BlockCIMFiles == nil {
136-
return fmt.Errorf("Confidential boot files is empty")
137-
} else if bootFiles.VmbFSFiles != nil {
138-
return fmt.Errorf("VmbFS boot files should be empty")
141+
return fmt.Errorf("confidential boot files is empty")
139142
}
140-
} else {
141-
return fmt.Errorf("invalid boot type specified")
143+
default:
144+
return fmt.Errorf("invalid boot type (%d) specified", bootFiles.BootType)
142145
}
143146
return nil
144147
}
@@ -178,6 +181,9 @@ func verifyOptions(_ context.Context, options interface{}) error {
178181
if err := verifyWCOWBootFiles(opts.BootFiles); err != nil {
179182
return err
180183
}
184+
if opts.SecurityPolicyEnabled && opts.GuestStateFilePath == "" {
185+
return fmt.Errorf("GuestStateFilePath must be provided when enabling security policy")
186+
}
181187
}
182188
return nil
183189
}

internal/uvm/create_lcow.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ type OptionsLCOW struct {
115115
KernelDirect bool // Skip UEFI and boot directly to `kernel`
116116
RootFSFile string // Filename under `BootFilesPath` for the UVMs root file system. Defaults to `InitrdFile`
117117
KernelBootOptions string // Additional boot options for the kernel
118-
EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM
119-
ConsolePipe string // The named pipe path to use for the serial console. eg \\.\pipe\vmpipe
120118
UseGuestConnection bool // Whether the HCS should connect to the UVM's GCS. Defaults to true
121119
ExecCommandLine string // The command line to exec from init. Defaults to GCS
122120
ForwardStdout bool // Whether stdout will be forwarded from the executed program. Defaults to false
@@ -164,8 +162,6 @@ func NewDefaultOptionsLCOW(id, owner string) *OptionsLCOW {
164162
KernelDirect: kernelDirectSupported,
165163
RootFSFile: InitrdFile,
166164
KernelBootOptions: "",
167-
EnableGraphicsConsole: false,
168-
ConsolePipe: "",
169165
UseGuestConnection: true,
170166
ExecCommandLine: fmt.Sprintf("/bin/gcs -v4 -log-format json -loglevel %s", logrus.StandardLogger().Level.String()),
171167
ForwardStdout: false,

0 commit comments

Comments
 (0)