Skip to content

Commit 6e7d936

Browse files
committed
builder/lcow: always set ConfidentialConfig when security policy is present
Always set ConfidentialConfig when a security policy is present. Add NoSecurityHardware to SandboxOptions and use isConfidentialSNP to gate SNP-specific HCS document construction (schema V25, confidential boot options, NUMA skip, etc.) separately from policy presence. Signed-off-by: Maksim An <maksiman@microsoft.com>
1 parent fd4b2a8 commit 6e7d936

3 files changed

Lines changed: 69 additions & 19 deletions

File tree

internal/builder/vm/lcow/sandbox_options.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ type SandboxOptions struct {
2222
// FullyPhysicallyBacked indicates all memory allocations are backed by physical memory.
2323
FullyPhysicallyBacked bool
2424

25+
// NoSecurityHardware indicates that SNP hardware is not available. When true,
26+
// the security policy is still plumbed to the GCS but the HCS document uses the
27+
// standard (non-SNP) format.
28+
NoSecurityHardware bool
29+
2530
// ConfidentialConfig carries confidential computing fields that are not
2631
// part of the HCS document but are needed for confidential VM setup.
2732
ConfidentialConfig *ConfidentialConfig

internal/builder/vm/lcow/specs.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ func BuildSandboxConfig(
6565
return nil, nil, fmt.Errorf("failed to parse sandbox options: %w", err)
6666
}
6767

68+
// isConfidentialSNP is true when we have a security policy AND real SNP hardware.
69+
// This gates SNP-specific HCS document construction (schema V25, confidential boot, etc.).
70+
// When no-security-hardware is set, we still plumb the policy but use the standard HCS doc.
71+
isConfidentialSNP := sandboxOptions.ConfidentialConfig != nil && !sandboxOptions.NoSecurityHardware
72+
6873
// ================== Parse Topology (CPU, Memory, NUMA) options =================
6974
// ===============================================================================
7075

@@ -86,10 +91,10 @@ func BuildSandboxConfig(
8691
return nil, nil, fmt.Errorf("failed to parse memory parameters: %w", err)
8792
}
8893

89-
// Parse NUMA settings only for non-confidential VMs.
94+
// Parse NUMA settings only for SNP confidential VMs.
9095
var numa *hcsschema.Numa
9196
var numaProcessors *hcsschema.NumaProcessors
92-
if sandboxOptions.ConfidentialConfig == nil {
97+
if !isConfidentialSNP {
9398
numa, numaProcessors, err = parseNUMAOptions(
9499
ctx,
95100
spec.Annotations,
@@ -121,11 +126,11 @@ func BuildSandboxConfig(
121126
// ================== Parse Boot options =========================================
122127
// ===============================================================================
123128

124-
// For confidential VMs, we don't use the standard boot options - the UEFI secure boot
129+
// For SNP confidential VMs, we don't use the standard boot options - the UEFI secure boot
125130
// settings will be set by parseConfidentialOptions.
126131
bootOptions := &hcsschema.Chipset{}
127132
var rootFsFullPath string
128-
if sandboxOptions.ConfidentialConfig == nil {
133+
if !isConfidentialSNP {
129134
bootOptions, rootFsFullPath, err = parseBootOptions(ctx, opts, spec.Annotations)
130135
if err != nil {
131136
return nil, nil, fmt.Errorf("failed to parse boot options: %w", err)
@@ -141,9 +146,9 @@ func BuildSandboxConfig(
141146
spec.Annotations,
142147
spec.Devices,
143148
rootFsFullPath,
144-
numa != nil && numaProcessors != nil, // isNumaEnabled
145-
sandboxOptions.FullyPhysicallyBacked, // isFullyPhysicallyBacked
146-
sandboxOptions.ConfidentialConfig != nil, // isConfidential
149+
numa != nil && numaProcessors != nil, // isNumaEnabled
150+
sandboxOptions.FullyPhysicallyBacked, // isFullyPhysicallyBacked
151+
isConfidentialSNP, // isConfidential
147152
)
148153
if err != nil {
149154
return nil, nil, fmt.Errorf("failed to parse device options: %w", err)
@@ -156,7 +161,7 @@ func BuildSandboxConfig(
156161
hvSocketConfig, comPorts, err := setAdditionalOptions(
157162
ctx,
158163
spec.Annotations,
159-
sandboxOptions.ConfidentialConfig != nil, // isConfidential
164+
isConfidentialSNP, // isConfidential
160165
)
161166
if err != nil {
162167
return nil, nil, fmt.Errorf("failed to parse additional parameters: %w", err)
@@ -169,7 +174,7 @@ func BuildSandboxConfig(
169174
var securitySettings *hcsschema.SecuritySettings
170175
var guestState *hcsschema.GuestState
171176
var filesToCleanOnError []string
172-
if sandboxOptions.ConfidentialConfig != nil {
177+
if isConfidentialSNP {
173178
bootOptions,
174179
securitySettings,
175180
guestState,
@@ -204,9 +209,9 @@ func BuildSandboxConfig(
204209
// ===============================================================================
205210

206211
// Build the kernel command line after all options are parsed.
207-
// For confidential VMs (SNP mode), kernel args are embedded in VMGS file, so skip this.
212+
// For SNP confidential VMs, kernel args are embedded in VMGS file, so skip this.
208213
var kernelArgs string
209-
if sandboxOptions.ConfidentialConfig == nil {
214+
if !isConfidentialSNP {
210215
kernelArgs, err = buildKernelArgs(
211216
ctx,
212217
opts,
@@ -238,7 +243,7 @@ func BuildSandboxConfig(
238243
// Use Schema V21 for non-confidential cases.
239244
// Use Schema V25 for confidential cases.
240245
schema := schemaversion.SchemaV21()
241-
if sandboxOptions.ConfidentialConfig != nil {
246+
if isConfidentialSNP {
242247
schema = schemaversion.SchemaV25()
243248
}
244249

@@ -332,12 +337,13 @@ func parseSandboxOptions(ctx context.Context, platform string, annotations map[s
332337
// Determine if this is a confidential VM early, as it affects boot options parsing
333338
securityPolicy := oci.ParseAnnotationsString(annotations, shimannotations.LCOWSecurityPolicy, "")
334339
noSecurityHardware := oci.ParseAnnotationsBool(ctx, annotations, shimannotations.NoSecurityHardware, false)
335-
if securityPolicy != "" && !noSecurityHardware {
340+
if len(securityPolicy) > 0 {
336341
sandboxOptions.ConfidentialConfig = &ConfidentialConfig{
337342
SecurityPolicy: securityPolicy,
338-
SecurityPolicyEnforcer: oci.ParseAnnotationsString(annotations, shimannotations.LCOWSecurityPolicyEnforcer, ""),
343+
SecurityPolicyEnforcer: oci.ParseAnnotationsString(annotations, shimannotations.LCOWSecurityPolicyEnforcer, "rego"),
339344
UvmReferenceInfoFile: oci.ParseAnnotationsString(annotations, shimannotations.LCOWReferenceInfoFile, vmutils.DefaultUVMReferenceInfoFile),
340345
}
346+
sandboxOptions.NoSecurityHardware = noSecurityHardware
341347

342348
log.G(ctx).WithFields(logrus.Fields{
343349
"securityPolicy": securityPolicy,

internal/builder/vm/lcow/specs_test.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ func TestBuildSandboxConfig_SecurityPolicyInteractions(t *testing.T) {
11111111
errContains: "v2 shims do not support vPMem devices",
11121112
},
11131113
{
1114-
name: "scratch encryption defaults to false when security hardware is bypassed",
1114+
name: "scratch encryption defaults to true when security hardware is bypassed",
11151115
spec: &vm.Spec{
11161116
Annotations: map[string]string{
11171117
shimannotations.LCOWSecurityPolicy: "eyJ0ZXN0IjoidGVzdCJ9",
@@ -1120,10 +1120,10 @@ func TestBuildSandboxConfig_SecurityPolicyInteractions(t *testing.T) {
11201120
},
11211121
validate: func(t *testing.T, doc *hcsschema.ComputeSystem, sandboxOpts *SandboxOptions) {
11221122
t.Helper()
1123-
// When NoSecurityHardware is true, isConfidential is false,
1124-
// so EnableScratchEncryption defaults to false
1125-
if sandboxOpts.EnableScratchEncryption != false {
1126-
t.Error("expected scratch encryption disabled by default when security hardware is bypassed")
1123+
// When NoSecurityHardware is true but a security policy is present,
1124+
// ConfidentialConfig is still set, so EnableScratchEncryption defaults to true
1125+
if sandboxOpts.EnableScratchEncryption != true {
1126+
t.Error("expected scratch encryption enabled by default when security policy is present")
11271127
}
11281128
},
11291129
},
@@ -1152,6 +1152,45 @@ func TestBuildSandboxConfig_SecurityPolicyInteractions(t *testing.T) {
11521152
}
11531153
},
11541154
},
1155+
{
1156+
name: "no-security-hardware sets ConfidentialConfig but uses standard HCS doc",
1157+
spec: &vm.Spec{
1158+
Annotations: map[string]string{
1159+
shimannotations.LCOWSecurityPolicy: "eyJ0ZXN0IjoidGVzdCJ9",
1160+
shimannotations.LCOWSecurityPolicyEnforcer: "rego",
1161+
shimannotations.NoSecurityHardware: "true",
1162+
},
1163+
},
1164+
validate: func(t *testing.T, doc *hcsschema.ComputeSystem, sandboxOpts *SandboxOptions) {
1165+
t.Helper()
1166+
// ConfidentialConfig should be set for policy plumbing
1167+
if sandboxOpts.ConfidentialConfig == nil {
1168+
t.Fatal("expected ConfidentialConfig to be set even with no-security-hardware")
1169+
}
1170+
if sandboxOpts.ConfidentialConfig.SecurityPolicy != "eyJ0ZXN0IjoidGVzdCJ9" {
1171+
t.Errorf("expected security policy to be set, got %q", sandboxOpts.ConfidentialConfig.SecurityPolicy)
1172+
}
1173+
if sandboxOpts.ConfidentialConfig.SecurityPolicyEnforcer != "rego" {
1174+
t.Errorf("expected security policy enforcer 'rego', got %q", sandboxOpts.ConfidentialConfig.SecurityPolicyEnforcer)
1175+
}
1176+
if !sandboxOpts.NoSecurityHardware {
1177+
t.Error("expected NoSecurityHardware to be true")
1178+
}
1179+
// HCS doc should use standard schema (V21), not SNP schema (V25)
1180+
if doc.SchemaVersion.Major != 2 || doc.SchemaVersion.Minor != 1 {
1181+
t.Errorf("expected schema V2.1 for no-security-hardware, got V%d.%d", doc.SchemaVersion.Major, doc.SchemaVersion.Minor)
1182+
}
1183+
// GuestState should NOT be set (no SNP boot)
1184+
if doc.VirtualMachine.GuestState != nil {
1185+
t.Error("expected no GuestState in no-security-hardware mode")
1186+
}
1187+
// Standard boot options should be used (kernel direct or UEFI)
1188+
chipset := doc.VirtualMachine.Chipset
1189+
if chipset.LinuxKernelDirect == nil && chipset.Uefi == nil {
1190+
t.Error("expected standard boot options in no-security-hardware mode")
1191+
}
1192+
},
1193+
},
11551194
}
11561195

11571196
runTestCases(t, ctx, defaultOpts, tests)

0 commit comments

Comments
 (0)