diff --git a/cmd/gcs-sidecar/internal/bridge/bridge.go b/cmd/gcs-sidecar/internal/bridge/bridge.go index 81156b336c..d419b1b98b 100644 --- a/cmd/gcs-sidecar/internal/bridge/bridge.go +++ b/cmd/gcs-sidecar/internal/bridge/bridge.go @@ -15,6 +15,7 @@ import ( "github.com/pkg/errors" "golang.org/x/sys/windows" + "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/windowssecuritypolicy" "github.com/Microsoft/hcsshim/internal/guest/gcserr" ) @@ -33,6 +34,19 @@ type responseMessage interface { Base() *responseBase } +type messageHeader struct { + Type uint32 + Size uint32 + ID int64 +} + +type bridgeResponse struct { + // ctx is the context created on request read + // ctx context.Context + header *messageHeader + response interface{} +} + /* // rpc represents an outstanding rpc request to the guest type rpc struct { @@ -69,6 +83,34 @@ type Bridge struct { // waitCh chan struct{} quitChan chan error + + PolicyEnforcer *SecurityPoliyEnforcer +} + +type SecurityPoliyEnforcer struct { + // state required for the security policy enforcement + policyMutex sync.Mutex + securityPolicyEnforcer windowssecuritypolicy.SecurityPolicyEnforcer + securityPolicyEnforcerSet bool + uvmReferenceInfo string +} + +func NewBridge(shimConn io.ReadWriteCloser, inboxGCSConn io.ReadWriteCloser) *Bridge { + return &Bridge{ + shimConn: shimConn, + inboxGCSConn: inboxGCSConn, + handlerList: make(map[rpcProc]HandlerFunc), + sendToGCSChan: make(chan request), + sendToShimCh: make(chan request), + quitChan: make(chan error), + } +} + +func NewPolicyEnforcer(initialEnforcer windowssecuritypolicy.SecurityPolicyEnforcer) *SecurityPoliyEnforcer { + return &SecurityPoliyEnforcer{ + securityPolicyEnforcerSet: false, + securityPolicyEnforcer: initialEnforcer, + } } // TODO: rename request to bridgeMessage @@ -98,17 +140,6 @@ type request struct { message []byte } -func NewBridge(shimConn io.ReadWriteCloser, inboxGCSConn io.ReadWriteCloser) *Bridge { - return &Bridge{ - shimConn: shimConn, - inboxGCSConn: inboxGCSConn, - handlerList: make(map[rpcProc]HandlerFunc), - sendToGCSChan: make(chan request), - sendToShimCh: make(chan request), - quitChan: make(chan error), - } -} - // UnknownMessage represents the default handler logic for an unmatched request // type sent from the bridge. func UnknownMessage(r *request) error { @@ -184,12 +215,6 @@ func (b *Bridge) AssignHandlers() { b.HandleFunc(rpcLifecycleNotification, b.lifecycleNotification) // TODO: Validate this request as well? } -type messageHeader struct { - Type uint32 - Size uint32 - ID int64 -} - func readMessage(r io.Reader) (request, error) { var h [hdrSize]byte _, err := io.ReadFull(r, h[:]) @@ -273,14 +298,14 @@ func (b *Bridge) ListenAndServeShimRequests() error { // 2. Code cleanup on error // ? b.close(err) // b.quitCh <- true // give few seconds delay and close connections? - b.close(err) return } // If we are here, means that the requested operation is allowed. // Forward message to GCS. We handle responses from GCS separately. + log.Printf("hcsshim receive message redirect") - b.sendToGCSChan <- req + // b.sendToGCSChan <- req }(req) } }() @@ -289,7 +314,7 @@ func (b *Bridge) ListenAndServeShimRequests() error { for req := range b.sendToGCSChan { // reconstruct message and forward to gcs var buf bytes.Buffer - log.Printf("bridge send to gcs") + log.Printf("bridge send to gcs, req %v", req) if b.prepareMessageAndSend(req.header, req.message, &buf, b.inboxGCSConn) != nil { // kill bridge? log.Printf("err sending message to ") @@ -349,6 +374,10 @@ func (b *Bridge) ListenAndServeShimRequests() error { } } +func (b *Bridge) forwardMessageToGCS(req request) { + b.sendToGCSChan <- req +} + func (b *Bridge) close(err error) { // TODO: Fail outstanding rpc requests before closing bridge and other channels // This is important to do as valid errors need to be recorded by callers and fail diff --git a/cmd/gcs-sidecar/internal/bridge/handlers.go b/cmd/gcs-sidecar/internal/bridge/handlers.go index 60824126de..6a42c7982a 100644 --- a/cmd/gcs-sidecar/internal/bridge/handlers.go +++ b/cmd/gcs-sidecar/internal/bridge/handlers.go @@ -10,6 +10,7 @@ import ( "log" "strings" + "github.com/Microsoft/go-winio/pkg/guid" hcsschema "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/hcs/schema2" "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/hcs/schema2/resourcepaths" "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/protocol/guestrequest" @@ -37,6 +38,7 @@ func (b *Bridge) createContainer(req *request) error { } var err error + err = nil var uvmConfig uvmConfig var hostedSystemConfig hcsschema.HostedSystem if err = json.Unmarshal(containerConfig, &uvmConfig); err == nil { @@ -57,22 +59,30 @@ func (b *Bridge) createContainer(req *request) error { err = fmt.Errorf("createContainer: invalid containerConfig type. Request: %v", r) } + if err == nil { + b.forwardMessageToGCS(*req) + } + return err } func (b *Bridge) startContainer(req *request) error { var r requestBase - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcStart: %v", req) } log.Printf("rpcStart: \n requestBase: %v", r) + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) shutdownGraceful(req *request) error { var r requestBase - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcShutdownGraceful: %v", req) } log.Printf("rpcShutdownGraceful: \n requestBase: %v", r) @@ -84,12 +94,17 @@ func (b *Bridge) shutdownGraceful(req *request) error { return fmt.Errorf("rpcShudownGraceful operation not allowed: %v", err) } */ + + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) shutdownForced(req *request) error { var r requestBase - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + err = nil + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcShutdownForced: %v", req) } log.Printf("rpcShutdownForced: \n requestBase: %v", r) @@ -102,21 +117,24 @@ func (b *Bridge) shutdownForced(req *request) error { } */ + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) executeProcess(req *request) error { var r containerExecuteProcess var processParamSettings json.RawMessage + var err error + err = nil r.Settings.ProcessParameters.Value = &processParamSettings - if err := json.Unmarshal(req.message, &r); err != nil { + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcExecuteProcess: %v", req) } containerID := r.requestBase.ContainerID stdioRelaySettings := r.Settings.StdioRelaySettings vsockStdioRelaySettings := r.Settings.VsockStdioRelaySettings - var err error var processParams hcsschema.ProcessParameters if err = json.Unmarshal(processParamSettings, &processParams); err != nil { log.Printf("rpcExecProcess: invalid params type for request %v", r.Settings) @@ -125,33 +143,42 @@ func (b *Bridge) executeProcess(req *request) error { log.Printf("rpcExecProcess: \n containerID: %v, schema1.ProcessParameters{ params: %v, stdioRelaySettings: %v, vsockStdioRelaySettings: %v }", containerID, processParams, stdioRelaySettings, vsockStdioRelaySettings) // err = call policy enforcer + + b.forwardMessageToGCS(*req) + return err } func (b *Bridge) waitForProcess(req *request) error { var r containerWaitForProcess - if err := json.Unmarshal(req.message, &r); err != nil { - return fmt.Errorf("failed to unmarshal rpcShutdownForced: %v", req) + var err error + err = nil + if err = json.Unmarshal(req.message, &r); err != nil { + return fmt.Errorf("failed to unmarshal waitForProcess: %v", req) } log.Printf("rpcWaitForProcess: \n containerWaitForProcess{ requestBase: %v, processID: %v, timeoutInMs: %v }", r.requestBase, r.ProcessID, r.TimeoutInMs) // waitForProcess does not have enforcer in clcow, why? + + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) signalProcess(req *request) error { + var err error var r containerSignalProcess var rawOpts json.RawMessage r.Options = &rawOpts - if err := json.Unmarshal(req.message, &r); err != nil { + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcSignalProcess: %v", req) } log.Printf("rpcSignalProcess: request %v", r) - var err error var wcowOptions guestresource.SignalProcessOptionsWCOW if rawOpts == nil { + b.forwardMessageToGCS(*req) return nil } else if err = json.Unmarshal(rawOpts, &wcowOptions); err != nil { log.Printf("rpcSignalProcess: invalid Options type for request %v", r) @@ -165,21 +192,30 @@ func (b *Bridge) signalProcess(req *request) error { return fmt.Errorf("waitForProcess not allowed due to policy") } + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) resizeConsole(req *request) error { var r containerResizeConsole - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcSignalProcess: %v", req) } log.Printf("rpcResizeConsole: \n containerResizeConsole{ requestBase: %v, processID: %v, height: %v, width: %v }", r.requestBase, r.ProcessID, r.Height, r.Width) - err := resizeConsole(r.ContainerID, r.Height, r.Width) + err = resizeConsole(r.ContainerID, r.Height, r.Width) if err != nil { return fmt.Errorf("waitForProcess not allowed due to policy") } + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } @@ -194,15 +230,28 @@ func (b *Bridge) getProperties(req *request) error { return nil } -func isSpecialResourcePaths(resourcePath string, settings interface{}) bool { +func isSpecialResourcePaths(resourcePath string, rawGuestRequest json.RawMessage) bool { if strings.HasPrefix(resourcePath, resourcepaths.HvSocketConfigResourcePrefix) { sid := strings.TrimPrefix(resourcePath, resourcepaths.HvSocketConfigResourcePrefix) - doc := settings.(*hcsschema.HvSocketServiceConfig) + doc := &hcsschema.HvSocketServiceConfig{} + + if err := json.Unmarshal(rawGuestRequest, &doc); err != nil { + log.Printf("invalid rpcModifySettings request %v", rawGuestRequest) + return false + //fmt.Errorf("invalid rpcModifySettings request %v", r) + } + log.Printf(", sid: %v, HvSocketServiceConfig{ %v } \n", sid, doc) return true } else if strings.HasPrefix(resourcePath, resourcepaths.NetworkResourcePrefix) { id := strings.TrimPrefix(resourcePath, resourcepaths.NetworkResourcePrefix) - settings := settings.(*hcsschema.NetworkAdapter) + settings := &hcsschema.NetworkAdapter{} + if err := json.Unmarshal(rawGuestRequest, &settings); err != nil { + log.Printf("invalid rpcModifySettings request %v", rawGuestRequest) + return false + //fmt.Errorf("invalid rpcModifySettings request %v", r) + } + log.Printf(", sid: %v, NetworkAdapter{ %v } \n", id, settings) return true } else if strings.HasPrefix(resourcePath, resourcepaths.SCSIResourcePrefix) { @@ -220,7 +269,28 @@ func isSpecialResourcePaths(resourcePath string, settings interface{}) bool { return false } -func unMarshalAndModifySettings(modifySettingsRequest *hcsschema.ModifySettingRequest, requestRawSettings *json.RawMessage) error { +func (b *Bridge) unMarshalAndModifySettings(req *request) error { + // skipSendToGCS := false + // var err error + // err = nil + var r containerModifySettings + var requestRawSettings json.RawMessage + r.Request = &requestRawSettings + if err := json.Unmarshal(req.message, &r); err != nil { + return fmt.Errorf("failed to unmarshal rpcModifySettings: %v", req) + } + //// TODO (kiashok): Test and optimize! + // Test with crictl/ctr update resources call for wcow hyperv + var modifySettingsRequest hcsschema.ModifySettingRequest + var modifySettingsReqRawSettings json.RawMessage + + modifySettingsRequest.Settings = modifySettingsReqRawSettings + //modifySettingsRequest.GuestRequest = rawGuestRequest + if err := json.Unmarshal(requestRawSettings, &modifySettingsRequest); err != nil { + log.Printf("invalid rpcModifySettings request %v", r) + return fmt.Errorf("invalid rpcModifySettings request %v", r) + } + log.Printf("rpcModifySettings: ModifySettingRequest %v\n", modifySettingsRequest) if modifySettingsRequest.ResourcePath != "" { reqType := modifySettingsRequest.RequestType resourcePath := modifySettingsRequest.ResourcePath @@ -229,26 +299,61 @@ func unMarshalAndModifySettings(modifySettingsRequest *hcsschema.ModifySettingRe switch resourcePath { case resourcepaths.SiloMappedDirectoryResourcePath: - mappedDirectory := modifySettingsRequest.Settings.(*hcsschema.MappedDirectory) + mappedDirectory := &hcsschema.MappedDirectory{} + if err := json.Unmarshal(modifySettingsReqRawSettings, &mappedDirectory); err != nil { + log.Printf("invalid SiloMappedDirectoryResourcePath request %v", r) + return fmt.Errorf("invalid SiloMappedDirectoryResourcePath request %v", r) + } + // TODO: check for Settings to be nil as in some examples log.Printf(", mappedDirectory: %v \n", mappedDirectory) case resourcepaths.SiloMemoryResourcePath: - memoryLimit := modifySettingsRequest.Settings.(*uint64) + var memoryLimit uint64 + if err := json.Unmarshal(modifySettingsReqRawSettings, &memoryLimit); err != nil { + log.Printf("invalid SiloMemoryResourcePath request %v", r) + return fmt.Errorf("invalid SiloMemoryResourcePath request %v", r) + } + log.Printf(", memoryLimit: %v \n", memoryLimit) case resourcepaths.SiloProcessorResourcePath: - processor := modifySettingsRequest.Settings.(*hcsschema.Processor) + processor := &hcsschema.Processor{} + if err := json.Unmarshal(modifySettingsReqRawSettings, &processor); err != nil { + log.Printf("invalid SiloProcessorResourcePath request %v", r) + return fmt.Errorf("invalid SiloProcessorResourcePath request %v", r) + } + log.Printf(", processor: %v \n", processor) case resourcepaths.CPUGroupResourcePath: - cpuGroup := modifySettingsRequest.Settings.(*hcsschema.CpuGroup) + cpuGroup := &hcsschema.CpuGroup{} + if err := json.Unmarshal(modifySettingsReqRawSettings, &cpuGroup); err != nil { + log.Printf("invalid CpuGroup request %v", r) + return fmt.Errorf("invalid CpuGroup request %v", r) + } + log.Printf(", cpuGroup: %v \n", cpuGroup) case resourcepaths.CPULimitsResourcePath: - processorLimits := modifySettingsRequest.Settings.(*hcsschema.ProcessorLimits) + processorLimits := &hcsschema.ProcessorLimits{} + if err := json.Unmarshal(modifySettingsReqRawSettings, &processorLimits); err != nil { + log.Printf("invalid CPULimitsResourcePath request %v", r) + return fmt.Errorf("invalid CPULimitsResourcePath request %v", r) + } + log.Printf(", processorLimits: %v \n", processorLimits) case resourcepaths.MemoryResourcePath: - actualMemory := modifySettingsRequest.Settings.(*uint64) + var actualMemory uint64 + if err := json.Unmarshal(modifySettingsReqRawSettings, &actualMemory); err != nil { + log.Printf("invalid MemoryResourcePath request %v", r) + return fmt.Errorf("invalid MemoryResourcePath request %v", r) + } + log.Printf(", actualMemory: %v \n", actualMemory) case resourcepaths.VSMBShareResourcePath: - virtualSmbShareSettings := modifySettingsRequest.Settings.(*hcsschema.VirtualSmbShare) + virtualSmbShareSettings := &hcsschema.VirtualSmbShare{} + if err := json.Unmarshal(modifySettingsReqRawSettings, &virtualSmbShareSettings); err != nil { + log.Printf("invalid VSMBShareResourcePath request %v", r) + return fmt.Errorf("invalid VSMBShareResourcePath request %v", r) + } + log.Printf(", VirtualSmbShare: %v \n", virtualSmbShareSettings) // TODO: Plan9 is only for LCOW right? // case resourcepaths.Plan9ShareResourcePath: @@ -260,85 +365,130 @@ func unMarshalAndModifySettings(modifySettingsRequest *hcsschema.ModifySettingRe // case resourcepaths.VPMemControllerResourceFormat default: // Handle cases of HvSocketConfigResourcePrefix, NetworkResourceFormatetc as they have data values in resourcePath string - if !isSpecialResourcePaths(resourcePath, modifySettingsRequest.Settings) { + if !isSpecialResourcePaths(resourcePath, modifySettingsReqRawSettings) { return fmt.Errorf("invalid rpcModifySettings resourcePath %v", resourcePath) } } } - if modifySettingsRequest.GuestRequest != nil { - var guestModificationRequest guestrequest.ModificationRequest - if err := json.Unmarshal(*requestRawSettings, &guestModificationRequest); err != nil { - log.Printf("invalid modifySettingsRequest.guestRequest") - return fmt.Errorf("invalid modifySettingsRequest.guestRequest") + //// + var modifyGuestSettingsRequest guestrequest.ModificationRequest + var rawGuestRequest json.RawMessage + modifyGuestSettingsRequest.Settings = &rawGuestRequest + if err := json.Unmarshal(requestRawSettings, &modifyGuestSettingsRequest); err != nil { + log.Printf("invalid rpcModifySettings ModificationRequest request %v", r) + return fmt.Errorf("invalid rpcModifySettings ModificationRequest request %v", r) + } + log.Printf("rpcModifySettings: ModificationRequest %v\n", modifyGuestSettingsRequest) + + //if rawGuestRequest != nil { + guestResourceType := modifyGuestSettingsRequest.ResourceType + guestRequestType := modifyGuestSettingsRequest.RequestType + + log.Printf("rpcModifySettings: guestRequest.ModificationRequest { resourceType: %v \n, requestType: %v", guestResourceType, guestRequestType) + + switch guestResourceType { + case guestresource.ResourceTypeCombinedLayers: + settings := &guestresource.WCOWCombinedLayers{} + if err := json.Unmarshal(rawGuestRequest, settings); err != nil { + log.Printf("invalid ResourceTypeCombinedLayers request %v", r) + return fmt.Errorf("invalid ResourceTypeCombinedLayers request %v", r) } - // modifyRequest.GuestRequest != nil - - guestResourceType := guestModificationRequest.ResourceType - guestRequestType := guestModificationRequest.RequestType - - log.Printf("rpcModifySettings: guestRequest.ModificationRequest { resourceType: %v \n, requestType: %v", guestResourceType, guestRequestType) - - switch guestResourceType { - case guestresource.ResourceTypeCombinedLayers: - settings := guestModificationRequest.Settings.(*guestresource.WCOWCombinedLayers) - log.Printf(", WCOWCombinedLayers {ContainerRootPath: %v, Layers: %v, ScratchPath: %v} \n", settings.ContainerRootPath, settings.Layers, settings.ScratchPath) - case guestresource.ResourceTypeNetworkNamespace: - settings := guestModificationRequest.Settings.(*hcn.HostComputeNamespace) - log.Printf(", HostComputeNamespaces { %v} \n", settings) - case guestresource.ResourceTypeNetwork: - // following valid only for osversion.Build() >= osversion.RS5 - // since Cwcow is available only for latest versions this is ok - settings := guestModificationRequest.Settings.(*guestrequest.NetworkModifyRequest) - log.Printf(", NetworkModifyRequest { %v} \n", settings) - case guestresource.ResourceTypeMappedVirtualDisk: - wcowMappedVirtualDisk := guestModificationRequest.Settings.(*guestresource.WCOWMappedVirtualDisk) - log.Printf(", wcowMappedVirtualDisk { %v} \n", wcowMappedVirtualDisk) - // TODO need a case similar to guestresource.ResourceTypeSecurityPolicy of lcow? - case guestresource.ResourceTypeSecurityPolicy: - log.Printf("This is the C-WCOW security policy invocation") - case guestresource.ResourceTypeHvSocket: - hvSocketAddress := guestModificationRequest.Settings.(*hcsschema.HvSocketAddress) - log.Printf(", hvSocketAddress { %v} \n", hvSocketAddress) - default: - isSpecialGuestRequests(string(guestResourceType), guestModificationRequest.Settings) - // invalid + + log.Printf(", WCOWCombinedLayers {ContainerRootPath: %v, Layers: %v, ScratchPath: %v} \n", settings.ContainerRootPath, settings.Layers, settings.ScratchPath) + case guestresource.ResourceTypeNetworkNamespace: + settings := &hcn.HostComputeNamespace{} + if err := json.Unmarshal(rawGuestRequest, settings); err != nil { + log.Printf("invalid ResourceTypeNetworkNamespace request %v", r) + return fmt.Errorf("invalid ResourceTypeNetworkNamespace request %v", r) } - } - // call policy enforcer - return nil -} + log.Printf(", HostComputeNamespaces { %v} \n", settings) + case guestresource.ResourceTypeNetwork: + // following valid only for osversion.Build() >= osversion.RS5 + // since Cwcow is available only for latest versions this is ok + settings := &guestrequest.NetworkModifyRequest{} + if err := json.Unmarshal(rawGuestRequest, settings); err != nil { + log.Printf("invalid ResourceTypeNetwork request %v", r) + return fmt.Errorf("invalid ResourceTypeNetwork request %v", r) + } -func (b *Bridge) modifySettings(req *request) error { - var r containerModifySettings - var requestRawSettings json.RawMessage - r.Request = &requestRawSettings - if err := json.Unmarshal(req.message, &r); err != nil { - return fmt.Errorf("failed to unmarshal rpcModifySettings: %v", req) - } + log.Printf(", NetworkModifyRequest { %v} \n", settings) + case guestresource.ResourceTypeMappedVirtualDisk: + wcowMappedVirtualDisk := &guestresource.WCOWMappedVirtualDisk{} + if err := json.Unmarshal(rawGuestRequest, wcowMappedVirtualDisk); err != nil { + log.Printf("invalid ResourceTypeMappedVirtualDisk request %v", r) + return fmt.Errorf("invalid ResourceTypeMappedVirtualDisk request %v", r) + } - var err error - var modifySettingsRequest hcsschema.ModifySettingRequest - if err = json.Unmarshal(requestRawSettings, &modifySettingsRequest); err != nil { - log.Printf("invalid rpcModifySettings request %v", r) - return fmt.Errorf("invalid rpcModifySettings request %v", r) + log.Printf(", wcowMappedVirtualDisk { %v} \n", wcowMappedVirtualDisk) + // TODO need a case similar to guestresource.ResourceTypeSecurityPolicy of lcow? + // case guestresource.ResourceTypeSecurityPolicy: + case guestresource.ResourceTypeHvSocket: + hvSocketAddress := &hcsschema.HvSocketAddress{} + if err := json.Unmarshal(rawGuestRequest, hvSocketAddress); err != nil { + log.Printf("invalid ResourceTypeHvSocket request %v", r) + return fmt.Errorf("invalid ResourceTypeHvSocket request %v", r) + } + + log.Printf(", hvSocketAddress { %v} \n", hvSocketAddress) + case guestresource.ResourceTypeSecurityPolicy: + securityPolicyRequest := &guestresource.WCOWConfidentialOptions{} + if err := json.Unmarshal(rawGuestRequest, securityPolicyRequest); err != nil { + log.Printf("invalid ResourceTypeSecurityPolicy request %v", r) + return fmt.Errorf("invalid ResourceTypeSecurityPolicy request %v", r) + } + + log.Printf(", WCOWConfidentialOptions: { %v} \n", securityPolicyRequest) + _ = b.PolicyEnforcer.SetWCOWConfidentialUVMOptions( /*ctx, */ securityPolicyRequest) + // skipSendToGCS = true + // send response back to shim + log.Printf("\n early response to hcsshim? \n") + err := b.sendReplyToShim(rpcModifySettings, *req) + if err != nil { + // + log.Printf("error sending early reply back to hcsshim") + err = fmt.Errorf("error sending early reply back to hcsshim") + return err + } + return nil + //return err, skipSendToGCS + default: + isSpecialGuestRequests(string(guestResourceType), rawGuestRequest) + // invalid } + //} - return unMarshalAndModifySettings(&modifySettingsRequest, &requestRawSettings) + // If we are here, there is no error and we want to + // forward the message to inbox GCS + b.forwardMessageToGCS(*req) + + return nil + //, skipSendToGCS } -func isSpecialGuestRequests(guestResourceType string, settings interface{}) bool { - if strings.HasPrefix(guestResourceType, resourcepaths.MappedPipeResourcePrefix) { - hostPath := strings.TrimPrefix(guestResourceType, resourcepaths.MappedPipeResourcePrefix) - log.Printf(", hostPath: %v \n", hostPath) - return true +// TODO: cleanup helper +func (b *Bridge) sendReplyToShim(rpcProcType rpcProc, req request) error { + respType := msgTypeResponse | msgType(rpcProcType) + activityID, _ := guid.FromString(req.activityID) + resp := &responseBase{ + Result: 0, // 0 means succes! + // ErrorMessage: "", + //fmt.Sprintf("Request %v not allowed", req.typ.String()), + ActivityID: activityID, } - // if we reached here, request is invalid - return false + msgb, err := json.Marshal(resp) + if err != nil { + return err + } + b.sendMessageToShim(respType, req.id, msgb) + + return nil } -func (b *Bridge) sendMessage(typ msgType, id int64, msg []byte) { +// TODO (kiashok): Cleanup. +// Sends early reply to shim +func (b *Bridge) sendMessageToShim(typ msgType, id int64, msg []byte) { var h [hdrSize]byte binary.LittleEndian.PutUint32(h[:], uint32(typ)) binary.LittleEndian.PutUint32(h[4:], uint32(len(msg)+16)) @@ -351,42 +501,96 @@ func (b *Bridge) sendMessage(typ msgType, id int64, msg []byte) { // time.Sleep(2 * time.Second) } +func (b *Bridge) modifySettings(req *request) error { + var err error + + log.Printf("\n rpcModifySettings handler \n") + + //skipSendToGCS := false + if err = b.unMarshalAndModifySettings(req); err != nil { + return err + } + + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + // if !skipSendToGCS { + // b.forwardMessageToGCS(*req) + // } + + return nil +} + +func isSpecialGuestRequests(guestResourceType string, settings interface{}) bool { + if strings.HasPrefix(guestResourceType, resourcepaths.MappedPipeResourcePrefix) { + hostPath := strings.TrimPrefix(guestResourceType, resourcepaths.MappedPipeResourcePrefix) + log.Printf(", hostPath: %v \n", hostPath) + return true + } + // if we reached here, request is invalid + return false +} + func (b *Bridge) negotiateProtocol(req *request) error { var r negotiateProtocolRequest - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcNegotiateProtocol: %v", req) } log.Printf("rpcNegotiateProtocol: negotiateProtocolRequest{ requestBase %v, MinVersion: %v, MaxVersion: %v }", r.requestBase, r.MinimumVersion, r.MaximumVersion) + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) dumpStacks(req *request) error { var r dumpStacksRequest - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcStart: %v", req) } log.Printf("rpcDumpStacks: \n requestBase: %v", r.requestBase) + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) deleteContainerState(req *request) error { var r deleteContainerStateRequest - if err := json.Unmarshal(req.message, &r); err != nil { + var err error + if err = json.Unmarshal(req.message, &r); err != nil { return fmt.Errorf("failed to unmarshal rpcStart: %v", req) } log.Printf("rpcDeleteContainerRequest: \n requestBase: %v", r.requestBase) + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) updateContainer(req *request) error { // No callers in the code for rpcUpdateContainer + + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } func (b *Bridge) lifecycleNotification(req *request) error { // No callers in the code for rpcLifecycleNotification + + // If we've reached here, means the policy has allowed it. + // So forward msg to inbox GCS. + b.forwardMessageToGCS(*req) + return nil } diff --git a/cmd/gcs-sidecar/internal/bridge/policy_helpers.go b/cmd/gcs-sidecar/internal/bridge/policy_helpers.go index c2007fa2dd..e3a1024cb0 100644 --- a/cmd/gcs-sidecar/internal/bridge/policy_helpers.go +++ b/cmd/gcs-sidecar/internal/bridge/policy_helpers.go @@ -5,11 +5,73 @@ package bridge import ( "context" + "errors" + "fmt" hcsschema "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/hcs/schema2" "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/protocol/guestrequest" + "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/protocol/guestresource" + "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/windowssecuritypolicy" ) +func (s *SecurityPoliyEnforcer) SetWCOWConfidentialUVMOptions(securityPolicyRequest *guestresource.WCOWConfidentialOptions) error { + s.policyMutex.Lock() + defer s.policyMutex.Unlock() + + if s.securityPolicyEnforcerSet { + return errors.New("security policy has already been set") + } + + // this limit ensures messages are below the character truncation limit that + // can be imposed by an orchestrator + maxErrorMessageLength := 3 * 1024 + + // Initialize security policy enforcer for a given enforcer type and + // encoded security policy. + p, err := windowssecuritypolicy.CreateSecurityPolicyEnforcer( + securityPolicyRequest.EnforcerType, + securityPolicyRequest.EncodedSecurityPolicy, + DefaultCRIMounts(), + DefaultCRIPrivilegedMounts(), + maxErrorMessageLength, + ) + if err != nil { + return fmt.Errorf("error creating security policy enforcer: %v", err) + } + + /* + // TODO(kiashok): What should be done for c-wcow? + + // This is one of two points at which we might change our logging. + // At this time, we now have a policy and can determine what the policy + // author put as policy around runtime logging. + // The other point is on startup where we take a flag to set the default + // policy enforcer to use before a policy arrives. After that flag is set, + // we use the enforcer in question to set up logging as well. + if err = p.EnforceRuntimeLoggingPolicy(ctx); err == nil { + logrus.SetOutput(h.logWriter) + } else { + logrus.SetOutput(io.Discard) + } + + hostData, err := securitypolicy.NewSecurityPolicyDigest(r.EncodedSecurityPolicy) + if err != nil { + return err + } + + if err := validateHostData(hostData[:]); err != nil { + return err + } + */ + + s.securityPolicyEnforcer = p + s.securityPolicyEnforcerSet = true + // TODO(kiashok): Update the following + // s.uvmReferenceInfo = s.EncodedUVMReference + + return nil +} + func ExecProcess(ctx context.Context, containerID string, params hcsschema.ProcessParameters) error { /* diff --git a/cmd/gcs-sidecar/internal/protocol/guestresource/resources.go b/cmd/gcs-sidecar/internal/protocol/guestresource/resources.go index 869ccc2366..a17684555f 100644 --- a/cmd/gcs-sidecar/internal/protocol/guestresource/resources.go +++ b/cmd/gcs-sidecar/internal/protocol/guestresource/resources.go @@ -187,3 +187,13 @@ type LCOWConfidentialOptions struct { type LCOWSecurityPolicyFragment struct { Fragment string `json:"Fragment,omitempty"` } + +type WCOWConfidentialOptions struct { + EnforcerType string `json:"EnforcerType,omitempty"` + EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"` + + WCOWSecurityPolicy string // Optional security policy + WCOWSecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length + WCOWSecurityPolicyEnforcer string // Set which security policy enforcer to use (open door or rego). This allows for better fallback mechanic. + //WCOWUVMReferenceInfoFile string // Filename under `BootFilesPath` for (potentially signed) UVM image reference information. +} diff --git a/cmd/gcs-sidecar/internal/windowssecuritypolicy/windowssecuritypolicy_enforcer.go b/cmd/gcs-sidecar/internal/windowssecuritypolicy/windowssecuritypolicy_enforcer.go index 8101642e7f..d8594ae523 100644 --- a/cmd/gcs-sidecar/internal/windowssecuritypolicy/windowssecuritypolicy_enforcer.go +++ b/cmd/gcs-sidecar/internal/windowssecuritypolicy/windowssecuritypolicy_enforcer.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "log" "regexp" "strings" "syscall" @@ -163,6 +164,7 @@ func CreateSecurityPolicyEnforcer( enforcer = defaultEnforcer if base64EncodedPolicy == "" { enforcer = openDoorEnforcer + log.Printf("Setting opendoorEnforcer \n") } } if createEnforcer, ok := registeredEnforcers[enforcer]; !ok { @@ -270,7 +272,6 @@ func stringSlicesEqual(slice1, slice2 []string) bool { return true } - type OpenDoorSecurityPolicyEnforcer struct { encodedSecurityPolicy string } diff --git a/cmd/gcs-sidecar/main.go b/cmd/gcs-sidecar/main.go index 178ce0c2b7..1590a31ef8 100644 --- a/cmd/gcs-sidecar/main.go +++ b/cmd/gcs-sidecar/main.go @@ -17,6 +17,7 @@ import ( "golang.org/x/sys/windows/svc/debug" gcsBridge "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/bridge" + "github.com/Microsoft/hcsshim/cmd/gcs-sidecar/internal/windowssecuritypolicy" ) type handler struct { @@ -213,8 +214,23 @@ func main() { return } + // set up our initial stance policy enforcer + var initialEnforcer windowssecuritypolicy.SecurityPolicyEnforcer + initialPolicyStance := "allow" + switch initialPolicyStance { + case "allow": + initialEnforcer = &windowssecuritypolicy.OpenDoorSecurityPolicyEnforcer{} + log.Printf("initial-policy-stance: allow") + case "deny": + initialEnforcer = &windowssecuritypolicy.ClosedDoorSecurityPolicyEnforcer{} + log.Printf("initial-policy-stance: deny") + default: + log.Printf("unknown initial-policy-stance") + } + // 3. Create bridge and initializa brdg := gcsBridge.NewBridge(shimCon, gcsCon) + brdg.PolicyEnforcer = gcsBridge.NewPolicyEnforcer(initialEnforcer) brdg.AssignHandlers() // 3. Listen and serve for hcsshim requests. diff --git a/internal/protocol/guestresource/resources.go b/internal/protocol/guestresource/resources.go index 2b75309b8e..1d870bd7ca 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -198,7 +198,15 @@ type LCOWSecurityPolicyFragment struct { // TODO (Mahati): Move this out later: WCOWConfidentialOptions is used to set various confidential container specific // options. +// TODO(kiashok): ***Copy of this is maintained in cmd/gcs-sidecar/internal/protocol/guestresource/resources.go*** +// for use in cmd/gcs-sidecar package. Please ensure to update the struct there if `WCOWConfidentialOptions` +// is update here! type WCOWConfidentialOptions struct { EnforcerType string `json:"EnforcerType,omitempty"` EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"` + + WCOWSecurityPolicy string // Optional security policy + WCOWSecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length + WCOWSecurityPolicyEnforcer string // Set which security policy enforcer to use (open door or rego). This allows for better fallback mechanic. + //WCOWUVMReferenceInfoFile string // Filename under `BootFilesPath` for (potentially signed) UVM image reference information. } diff --git a/internal/uvm/create_wcow.go b/internal/uvm/create_wcow.go index 7f575b0ddb..77c32b3e41 100644 --- a/internal/uvm/create_wcow.go +++ b/internal/uvm/create_wcow.go @@ -21,6 +21,7 @@ import ( "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/processorinfo" "github.com/Microsoft/hcsshim/internal/protocol/guestrequest" + "github.com/Microsoft/hcsshim/internal/protocol/guestresource" "github.com/Microsoft/hcsshim/internal/schemaversion" "github.com/Microsoft/hcsshim/internal/security" "github.com/Microsoft/hcsshim/internal/uvm/scsi" @@ -28,17 +29,10 @@ import ( "github.com/Microsoft/hcsshim/osversion" ) -type WCOWConfidentialOptions struct { - WCOWSecurityPolicy string // Optional security policy - WCOWSecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length - WCOWSecurityPolicyEnforcer string // Set which security policy enforcer to use (open door or rego). This allows for better fallback mechanic. - //WCOWUVMReferenceInfoFile string // Filename under `BootFilesPath` for (potentially signed) UVM image reference information. -} - // OptionsWCOW are the set of options passed to CreateWCOW() to create a utility vm. type OptionsWCOW struct { *Options - *WCOWConfidentialOptions + *guestresource.WCOWConfidentialOptions BootFiles *WCOWBootFiles @@ -63,7 +57,7 @@ func NewDefaultOptionsWCOW(id, owner string) *OptionsWCOW { return &OptionsWCOW{ Options: newDefaultOptions(id, owner), AdditionalRegistryKeys: []hcsschema.RegistryValue{}, - WCOWConfidentialOptions: &WCOWConfidentialOptions{ + WCOWConfidentialOptions: &guestresource.WCOWConfidentialOptions{ WCOWSecurityPolicyEnabled: false, }, } diff --git a/internal/uvm/types.go b/internal/uvm/types.go index 1af0410dbe..fc1225af83 100644 --- a/internal/uvm/types.go +++ b/internal/uvm/types.go @@ -14,6 +14,7 @@ import ( "github.com/Microsoft/hcsshim/internal/gcs" "github.com/Microsoft/hcsshim/internal/hcs" "github.com/Microsoft/hcsshim/internal/hns" + "github.com/Microsoft/hcsshim/internal/protocol/guestresource" "github.com/Microsoft/hcsshim/internal/uvm/scsi" ) @@ -140,8 +141,7 @@ type UtilityVM struct { // confidentialUVMOptions hold confidential UVM specific options confidentialUVMOptions *ConfidentialOptions - // WCOWconfidentialUVMOptions hold confidential UVM specific options - WCOWconfidentialUVMOptions *WCOWConfidentialOptions + WCOWconfidentialUVMOptions *guestresource.WCOWConfidentialOptions } func (uvm *UtilityVM) ScratchEncryptionEnabled() bool {