Skip to content

Commit 4612d59

Browse files
marma-devManish Ranjan Mahanta
andauthored
Embedding ETW Name-GUID Mapping for Log Forward Service (microsoft#2617)
* Initial check-in for ETW Name-GUID Mapping code Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * Moving to VMUtils and static analysis fix Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * sidecar-GCS changes to forward modifyServiceSettings Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * Making the default sources and map in native go, remvoing unreferenced methods and addressing review comments Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * Addressing review comments Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * Erroring out instead of silent override, and bubbling up the error to the caller Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> * Removing stale test Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> --------- Signed-off-by: Manish Ranjan Mahanta <mmahanta@microsoft.com> Co-authored-by: Manish Ranjan Mahanta <mmahanta@microsoft.com>
1 parent 3e63b84 commit 4612d59

15 files changed

Lines changed: 2162 additions & 37 deletions

File tree

internal/gcs-sidecar/bridge.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ func (b *Bridge) AssignHandlers() {
172172
b.HandleFunc(prot.RPCDeleteContainerState, b.deleteContainerState)
173173
b.HandleFunc(prot.RPCUpdateContainer, b.updateContainer)
174174
b.HandleFunc(prot.RPCLifecycleNotification, b.lifecycleNotification)
175+
b.HandleFunc(prot.RPCModifyServiceSettings, b.modifyServiceSettings)
175176
}
176177

177178
// readMessage reads the message from io.Reader

internal/gcs-sidecar/handlers.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
oci "github.com/Microsoft/hcsshim/internal/oci"
2424
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
2525
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
26+
"github.com/Microsoft/hcsshim/internal/vm/vmutils/etw"
2627
"github.com/Microsoft/hcsshim/internal/windevice"
2728
"github.com/Microsoft/hcsshim/pkg/annotations"
2829
"github.com/Microsoft/hcsshim/pkg/cimfs"
@@ -491,6 +492,70 @@ func (b *Bridge) lifecycleNotification(req *request) (err error) {
491492
return nil
492493
}
493494

495+
func (b *Bridge) modifyServiceSettings(req *request) (err error) {
496+
_, span := oc.StartSpan(req.ctx, "sidecar::modifyServiceSettings")
497+
defer span.End()
498+
defer func() { oc.SetSpanStatus(span, err) }()
499+
500+
// Todo: Add policy enforcement for modifying service settings
501+
modifyRequest, err := unmarshalModifyServiceSettings(req)
502+
if err != nil {
503+
return fmt.Errorf("failed to unmarshal modifyServiceSettings request: %w", err)
504+
}
505+
506+
switch modifyRequest.PropertyType {
507+
case string(prot.LogForwardService):
508+
if modifyRequest.Settings != nil {
509+
log.G(req.ctx).Tracef("modifyServiceSettings for LogForwardService with RPCModifyServiceSettings, enforcing policy for log sources")
510+
settings := modifyRequest.Settings.(*guestrequest.LogForwardServiceRPCRequest)
511+
512+
switch settings.RPCType {
513+
case guestrequest.RPCModifyServiceSettings, guestrequest.RPCStartLogForwarding, guestrequest.RPCStopLogForwarding:
514+
log.G(req.ctx).Tracef("%v request received for LogForwardService, proceeding with policy enforcement for log sources", settings.RPCType)
515+
// Enforce the policy for log sources in the request and update the settings with allowed log sources.
516+
// For cwcow, the sidecar-GCS will verify the allowed log sources against policy and append the necessary GUIDs to the ones allowed. Rest are dropped.
517+
// The Enforcer will have to unmarshal the log sources, enforce the policy and then marshal it back to a Base64 encoded JSON string which is what inbox GCS expects.
518+
// It can query etw.GetDefaultLogSources to get the default log sources if the policy allows, and allow providers matching the default list during policy enforcement.
519+
// This is because the log sources can be a combination of default and user specified log sources for which GUIDs need to be appended based on the policy enforcement.
520+
if settings.Settings != "" {
521+
// <EXAMPLE CALL>
522+
// allowedLogSources, err := b.hostState.securityOptions.PolicyEnforcer.EnforceLogForwardServiceSettingsPolicy(req.ctx, settings.LogSources)
523+
524+
// For now, we are skipping the policy enforcement and allowing all log sources as the policy enforcer implementation is in progress. We will add the enforcement back once it's implemented.
525+
allowedLogSources := settings.Settings // This is Base64 encoded JSON string of log sources
526+
log.G(req.ctx).Tracef("Allowed log sources after policy enforcement: %v", allowedLogSources)
527+
528+
// Update the allowed log sources in the settings. This will be forwarded to inbox GCS which expects the log sources in a JSON string format with GUIDs for providers included.
529+
allowedLogSources, err := etw.UpdateLogSources(allowedLogSources, false, true)
530+
if err != nil {
531+
return fmt.Errorf("failed to update log sources: %w", err)
532+
}
533+
settings.Settings = allowedLogSources
534+
}
535+
default:
536+
log.G(req.ctx).Warningf("modifyServiceSettings for LogForwardService with RPCType: %v, skipping policy enforcement", settings.RPCType)
537+
}
538+
modifyRequest.Settings = settings
539+
buf, err := json.Marshal(modifyRequest)
540+
if err != nil {
541+
return fmt.Errorf("failed to marshal modifyServiceSettings request: %w", err)
542+
}
543+
var newRequest request
544+
newRequest.ctx = req.ctx
545+
newRequest.header = req.header
546+
newRequest.header.Size = uint32(len(buf)) + prot.HdrSize
547+
newRequest.message = buf
548+
req = &newRequest
549+
} else {
550+
log.G(req.ctx).Warningf("modifyServiceSettings for LogForwardService with empty settings, skipping policy enforcement")
551+
}
552+
default:
553+
log.G(req.ctx).Warningf("modifyServiceSettings with PropertyType: %v, skipping policy enforcement", modifyRequest.PropertyType)
554+
}
555+
b.forwardRequestToGcs(req)
556+
return nil
557+
}
558+
494559
func volumeGUIDFromLayerPath(path string) (string, bool) {
495560
if p, ok := strings.CutPrefix(path, `\\?\Volume{`); ok {
496561
if q, ok := strings.CutSuffix(p, `}\Files`); ok {

internal/gcs-sidecar/uvm.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,37 @@ import (
1717
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
1818
)
1919

20+
func unmarshalModifyServiceSettings(req *request) (_ *prot.ServiceModificationRequest, err error) {
21+
ctx, span := oc.StartSpan(req.ctx, "sidecar::unmarshalModifyServiceSettings")
22+
defer span.End()
23+
defer func() { oc.SetSpanStatus(span, err) }()
24+
25+
var serviceModifyRequest prot.ServiceModificationRequest
26+
var requestRawSettings json.RawMessage
27+
serviceModifyRequest.Settings = &requestRawSettings
28+
if err := commonutils.UnmarshalJSONWithHresult(req.message, &serviceModifyRequest); err != nil {
29+
return nil, fmt.Errorf("failed to unmarshal rpcModifySettings: %w", err)
30+
}
31+
32+
if serviceModifyRequest.PropertyType != "" {
33+
switch serviceModifyRequest.PropertyType {
34+
case string(prot.LogForwardService):
35+
log.G(ctx).Info("Unmarshalling log forward service modify settings")
36+
settings := &guestrequest.LogForwardServiceRPCRequest{}
37+
if err := commonutils.UnmarshalJSONWithHresult(requestRawSettings, settings); err != nil {
38+
return nil, fmt.Errorf("invalid LogForwardService modify settings request: %w", err)
39+
}
40+
serviceModifyRequest.Settings = settings
41+
default:
42+
// Invalid request
43+
log.G(ctx).Errorf("Invalid ServiceModificationRequest: %v", serviceModifyRequest.PropertyType)
44+
return nil, fmt.Errorf("invalid ServiceModificationRequest")
45+
}
46+
}
47+
48+
return &serviceModifyRequest, nil
49+
}
50+
2051
func unmarshalContainerModifySettings(req *request) (_ *prot.ContainerModifySettings, err error) {
2152
ctx, span := oc.StartSpan(req.ctx, "sidecar::unmarshalContainerModifySettings")
2253
defer span.End()

internal/oci/uvm.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -419,12 +419,14 @@ func SpecToUVMCreateOpts(ctx context.Context, s *specs.Spec, id, owner string) (
419419
if err := handleWCOWSecurityPolicy(ctx, s.Annotations, wopts); err != nil {
420420
return nil, err
421421
}
422-
// If security policy is enable, wopts.ForwardLogs default value should be false
422+
// If security policy is enable, wopts.LogForwardingEnabled default value should be false (CWCOW should not allow log forwarding by default)
423423
if wopts.SecurityPolicyEnabled {
424-
wopts.ForwardLogs = false
424+
wopts.LogForwardingEnabled = false
425425
}
426426
wopts.LogSources = ParseAnnotationsString(s.Annotations, annotations.LogSources, wopts.LogSources)
427-
wopts.ForwardLogs = ParseAnnotationsBool(ctx, s.Annotations, annotations.ForwardLogs, wopts.ForwardLogs)
427+
wopts.LogForwardingEnabled = ParseAnnotationsBool(ctx, s.Annotations, annotations.LogForwardingEnabled, wopts.LogForwardingEnabled)
428+
wopts.DefaultLogSourcesEnabled = ParseAnnotationsBool(ctx, s.Annotations, annotations.DefaultLogSourcesEnabled, wopts.DefaultLogSourcesEnabled)
429+
428430
return wopts, nil
429431
}
430432
return nil, errors.New("cannot create UVM opts spec is not LCOW or WCOW")

internal/uvm/create_wcow.go

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ type OptionsWCOW struct {
7070
// AdditionalRegistryKeys are Registry keys and their values to additionally add to the uVM.
7171
AdditionalRegistryKeys []hcsschema.RegistryValue
7272

73-
OutputHandlerCreator vmutils.OutputHandlerCreator // Creates an [OutputHandler] that controls how output received over HVSocket from the UVM is handled. Defaults to parsing output as ETW Log events
74-
LogSources string // ETW providers to be set for the logging service
75-
ForwardLogs bool // Whether to forward logs to the host or not
73+
OutputHandlerCreator vmutils.OutputHandlerCreator // Creates an [OutputHandler] that controls how output received over HVSocket from the UVM is handled. Defaults to parsing output as ETW Log events
74+
LogSources string // ETW providers to be set for the logging service
75+
LogForwardingEnabled bool // Whether to enable forwarding of logs to the host or not
76+
DefaultLogSourcesEnabled bool // Whether to enable using default log sources
7677
}
7778

7879
func defaultConfidentialWCOWOSBootFilesPath() string {
@@ -111,9 +112,10 @@ func NewDefaultOptionsWCOW(id, owner string) *OptionsWCOW {
111112
SecurityPolicyEnabled: false,
112113
},
113114
},
114-
OutputHandlerCreator: vmutils.ParseGCSLogrus,
115-
ForwardLogs: true, // Default to true for WCOW, and set to false for CWCOW in internal/oci/uvm.go SpecToUVMCreateOpts
116-
LogSources: "",
115+
OutputHandlerCreator: vmutils.ParseGCSLogrus,
116+
LogForwardingEnabled: true, // Default to true for WCOW, and set to false for CWCOW in internal/oci/uvm.go SpecToUVMCreateOpts
117+
DefaultLogSourcesEnabled: true,
118+
LogSources: "",
117119
}
118120
}
119121

@@ -291,7 +293,7 @@ func prepareCommonConfigDoc(ctx context.Context, uvm *UtilityVM, opts *OptionsWC
291293
}
292294

293295
maps.Copy(doc.VirtualMachine.Devices.HvSocket.HvSocketConfig.ServiceTable, opts.AdditionalHyperVConfig)
294-
if opts.ForwardLogs {
296+
if opts.LogForwardingEnabled {
295297
key := prot.WindowsLoggingHvsockServiceID.String()
296298
doc.VirtualMachine.Devices.HvSocket.HvSocketConfig.ServiceTable[key] = hcsschema.HvSocketServiceConfig{
297299
AllowWildcardBinds: true,
@@ -562,22 +564,23 @@ func CreateWCOW(ctx context.Context, opts *OptionsWCOW) (_ *UtilityVM, err error
562564
log.G(ctx).WithField("options", log.Format(ctx, opts)).Debug("uvm::CreateWCOW options")
563565

564566
uvm := &UtilityVM{
565-
id: opts.ID,
566-
owner: opts.Owner,
567-
operatingSystem: "windows",
568-
scsiControllerCount: opts.SCSIControllerCount,
569-
vsmbDirShares: make(map[string]*VSMBShare),
570-
vsmbFileShares: make(map[string]*VSMBShare),
571-
vpciDevices: make(map[VPCIDeviceID]*VPCIDevice),
572-
noInheritHostTimezone: opts.NoInheritHostTimezone,
573-
physicallyBacked: !opts.AllowOvercommit,
574-
devicesPhysicallyBacked: opts.FullyPhysicallyBacked,
575-
vsmbNoDirectMap: opts.NoDirectMap,
576-
noWritableFileShares: opts.NoWritableFileShares,
577-
createOpts: opts,
578-
blockCIMMounts: make(map[string]*UVMMountedBlockCIMs),
579-
logSources: opts.LogSources,
580-
forwardLogs: opts.ForwardLogs,
567+
id: opts.ID,
568+
owner: opts.Owner,
569+
operatingSystem: "windows",
570+
scsiControllerCount: opts.SCSIControllerCount,
571+
vsmbDirShares: make(map[string]*VSMBShare),
572+
vsmbFileShares: make(map[string]*VSMBShare),
573+
vpciDevices: make(map[VPCIDeviceID]*VPCIDevice),
574+
noInheritHostTimezone: opts.NoInheritHostTimezone,
575+
physicallyBacked: !opts.AllowOvercommit,
576+
devicesPhysicallyBacked: opts.FullyPhysicallyBacked,
577+
vsmbNoDirectMap: opts.NoDirectMap,
578+
noWritableFileShares: opts.NoWritableFileShares,
579+
createOpts: opts,
580+
blockCIMMounts: make(map[string]*UVMMountedBlockCIMs),
581+
logSources: opts.LogSources,
582+
logForwardingEnabled: opts.LogForwardingEnabled,
583+
defaultLogSourcesEnabled: opts.DefaultLogSourcesEnabled,
581584
}
582585

583586
defer func() {
@@ -617,7 +620,7 @@ func CreateWCOW(ctx context.Context, opts *OptionsWCOW) (_ *UtilityVM, err error
617620
return nil, fmt.Errorf("error while creating the compute system: %w", err)
618621
}
619622

620-
if opts.ForwardLogs {
623+
if opts.LogForwardingEnabled {
621624
// Create a socket that the executed program can send to. This is usually
622625
// used by Log Forward Service to send log data.
623626
uvm.outputHandler = opts.OutputHandlerCreator(opts.ID)

internal/uvm/log_wcow.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ package uvm
44

55
import (
66
"context"
7+
"fmt"
78

89
"github.com/Microsoft/hcsshim/internal/gcs"
910
"github.com/Microsoft/hcsshim/internal/gcs/prot"
1011
"github.com/Microsoft/hcsshim/internal/log"
1112
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
13+
"github.com/Microsoft/hcsshim/internal/vm/vmutils/etw"
1214
)
1315

1416
func (uvm *UtilityVM) StartLogForwarding(ctx context.Context) error {
@@ -62,13 +64,23 @@ func (uvm *UtilityVM) SetLogSources(ctx context.Context) error {
6264
wcaps := gcs.GetWCOWCapabilities(uvm.gc.Capabilities())
6365
if wcaps != nil && wcaps.IsLogForwardingSupported() {
6466
// Make a call to the GCS to set the ETW providers
67+
68+
// Determines the log sources to be set based on the configuration. If default log sources are enabled,
69+
// we only include them along with user specified log sources.
70+
// For confidential WCOw, we skip the adding guids to the log sources as the sidecar-GCS will verify the
71+
// allowed log sources against policy and append the necessary GUIDs to the ones allowed. Rest are dropped.
72+
// For non-confidential WCOW, we include the GUIDs in the log sources as the hcsshim communicates directly with the inboxGCS.
73+
settings, err := etw.UpdateLogSources(uvm.logSources, uvm.defaultLogSourcesEnabled, !uvm.HasConfidentialPolicy())
74+
if err != nil {
75+
return fmt.Errorf("failed to parse log sources: %w", err)
76+
}
6577
req := guestrequest.LogForwardServiceRPCRequest{
6678
RPCType: guestrequest.RPCModifyServiceSettings,
67-
Settings: uvm.logSources,
79+
Settings: settings,
6880
}
69-
err := uvm.gc.ModifyServiceSettings(ctx, prot.LogForwardService, req)
81+
err = uvm.gc.ModifyServiceSettings(ctx, prot.LogForwardService, req)
7082
if err != nil {
71-
return err
83+
return fmt.Errorf("failed to modify service settings: %w", err)
7284
}
7385
}
7486
return nil

internal/uvm/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ func (uvm *UtilityVM) Start(ctx context.Context) (err error) {
285285
}
286286
}
287287

288-
if uvm.OS() == "windows" && uvm.forwardLogs {
288+
if uvm.OS() == "windows" && uvm.logForwardingEnabled {
289289
// If the UVM is Windows and log forwarding is enabled, set the log sources
290290
// and start the log forwarding service.
291291
if err := uvm.SetLogSources(ctx); err != nil {

internal/uvm/types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,9 @@ type UtilityVM struct {
144144
blockCIMMounts map[string]*UVMMountedBlockCIMs
145145
blockCIMMountLock sync.Mutex
146146

147-
forwardLogs bool // Indicates whether to forward logs from the UVM to the host
148-
logSources string // ETW providers to enable for log forwarding
147+
logForwardingEnabled bool // Indicates whether to forward logs from the UVM to the host
148+
defaultLogSourcesEnabled bool // Specifies whether addition of default list of ETW providers should be disabled
149+
logSources string // ETW providers to enable for log forwarding
149150
}
150151

151152
func (uvm *UtilityVM) ScratchEncryptionEnabled() bool {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package etw
2+
3+
// defaultLogSourcesInfo defines the list of trusted ETW providers
4+
var defaultLogSourcesInfo = LogSourcesInfo{
5+
LogConfig: LogConfig{
6+
Sources: []Source{
7+
{
8+
Type: "ETW",
9+
Providers: []EtwProvider{
10+
{
11+
ProviderName: "microsoft.windows.hyperv.compute",
12+
Level: "Information",
13+
},
14+
{
15+
ProviderName: "microsoft-windows-guest-network-service",
16+
Level: "Information",
17+
},
18+
{
19+
ProviderName: "microsoft.windows.filesystem.cimfs",
20+
Level: "Information",
21+
},
22+
{
23+
ProviderName: "microsoft.windows.filesystem.unionfs",
24+
Level: "Information",
25+
},
26+
{
27+
ProviderName: "microsoft-windows-bitlocker-driver",
28+
Level: "Information",
29+
},
30+
{
31+
ProviderName: "microsoft-windows-bitlocker-api",
32+
Level: "Information",
33+
},
34+
{
35+
ProviderName: "microsoft.windows.security.keyguard",
36+
Level: "Information",
37+
},
38+
{
39+
ProviderName: "microsoft.windows.security.keyguard.attestation.verify",
40+
Level: "Information",
41+
},
42+
{
43+
ProviderName: "microsoft.windows.containers.setup",
44+
Level: "Information",
45+
},
46+
{
47+
ProviderName: "microsoft.windows.containers.storage",
48+
Level: "Information",
49+
},
50+
{
51+
ProviderName: "microsoft.windows.containers.library",
52+
Level: "Information",
53+
},
54+
{
55+
ProviderName: "microsoft.windows.containers.dynamicimage",
56+
Level: "Information",
57+
},
58+
{
59+
ProviderName: "microsoft.windows.logforwardservice.provider",
60+
Level: "Information",
61+
},
62+
},
63+
},
64+
},
65+
},
66+
}

0 commit comments

Comments
 (0)