diff --git a/cmd/gcs-sidecar/main.go b/cmd/gcs-sidecar/main.go index 60f23f6928..04952899e0 100644 --- a/cmd/gcs-sidecar/main.go +++ b/cmd/gcs-sidecar/main.go @@ -15,7 +15,6 @@ import ( "github.com/Microsoft/hcsshim/internal/gcs/prot" shimlog "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/oc" - "github.com/Microsoft/hcsshim/internal/pspdriver" "github.com/Microsoft/hcsshim/pkg/securitypolicy" "github.com/sirupsen/logrus" "go.opencensus.io/trace" @@ -214,7 +213,7 @@ func main() { return } - if err := pspdriver.StartPSPDriver(ctx); err != nil { + if err := securitypolicy.StartPSPDriver(ctx); err != nil { // When error happens, pspdriver.GetPspDriverError() returns true. // In that case, gcs-sidecar should keep the initial "deny" policy // and reject all requests from the host. diff --git a/internal/gcs-sidecar/bridge.go b/internal/gcs-sidecar/bridge.go index 1c1cefdcd9..412c8d8532 100644 --- a/internal/gcs-sidecar/bridge.go +++ b/internal/gcs-sidecar/bridge.go @@ -47,9 +47,6 @@ type Bridge struct { // and send responses back to hcsshim respectively. sendToGCSCh chan request sendToShimCh chan bridgeResponse - - // logging target - logWriter io.Writer } // SequenceID is used to correlate requests and responses. @@ -81,7 +78,7 @@ type request struct { } func NewBridge(shimConn io.ReadWriteCloser, inboxGCSConn io.ReadWriteCloser, initialEnforcer securitypolicy.SecurityPolicyEnforcer, logWriter io.Writer) *Bridge { - hostState := NewHost(initialEnforcer) + hostState := NewHost(initialEnforcer, logWriter) return &Bridge{ pending: make(map[sequenceID]*prot.ContainerExecuteProcessResponse), rpcHandlerList: make(map[prot.RPCProc]HandlerFunc), @@ -90,7 +87,6 @@ func NewBridge(shimConn io.ReadWriteCloser, inboxGCSConn io.ReadWriteCloser, ini inboxGCSConn: inboxGCSConn, sendToGCSCh: make(chan request), sendToShimCh: make(chan bridgeResponse), - logWriter: logWriter, } } diff --git a/internal/gcs-sidecar/handlers.go b/internal/gcs-sidecar/handlers.go index e3f423faf8..511d23044e 100644 --- a/internal/gcs-sidecar/handlers.go +++ b/internal/gcs-sidecar/handlers.go @@ -84,7 +84,7 @@ func (b *Bridge) createContainer(req *request) (err error) { user := securitypolicy.IDName{ Name: spec.Process.User.Username, } - _, _, _, err := b.hostState.securityPolicyEnforcer.EnforceCreateContainerPolicyV2(req.ctx, containerID, spec.Process.Args, spec.Process.Env, spec.Process.Cwd, spec.Mounts, user, nil) + _, _, _, err := b.hostState.securityOptions.PolicyEnforcer.EnforceCreateContainerPolicyV2(req.ctx, containerID, spec.Process.Args, spec.Process.Env, spec.Process.Cwd, spec.Mounts, user, nil) if err != nil { return fmt.Errorf("CreateContainer operation is denied by policy: %w", err) @@ -104,48 +104,12 @@ func (b *Bridge) createContainer(req *request) (err error) { b.hostState.RemoveContainer(ctx, containerID) } }(err) - // Write security policy, signed UVM reference and host AMD certificate to - // container's rootfs, so that application and sidecar containers can have - // access to it. The security policy is required by containers which need to - // extract init-time claims found in the security policy. The directory path - // containing the files is exposed via UVM_SECURITY_CONTEXT_DIR env var. - // It may be an error to have a security policy but not expose it to the - // container as in that case it can never be checked as correct by a verifier. - if oci.ParseAnnotationsBool(ctx, spec.Annotations, annotations.WCOWSecurityPolicyEnv, true) { - encodedPolicy := b.hostState.securityPolicyEnforcer.EncodedSecurityPolicy() - hostAMDCert := spec.Annotations[annotations.WCOWHostAMDCertificate] - if len(encodedPolicy) > 0 || len(hostAMDCert) > 0 || len(b.hostState.uvmReferenceInfo) > 0 { - // Use os.MkdirTemp to make sure that the directory is unique. - securityContextDir, err := os.MkdirTemp(spec.Root.Path, securitypolicy.SecurityContextDirTemplate) - if err != nil { - return fmt.Errorf("failed to create security context directory: %w", err) - } - // Make sure that files inside directory are readable - if err := os.Chmod(securityContextDir, 0755); err != nil { - return fmt.Errorf("failed to chmod security context directory: %w", err) - } - if len(encodedPolicy) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.PolicyFilename, []byte(encodedPolicy), 0777); err != nil { - return fmt.Errorf("failed to write security policy: %w", err) - } - } - if len(b.hostState.uvmReferenceInfo) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.ReferenceInfoFilename, []byte(b.hostState.uvmReferenceInfo), 0777); err != nil { - return fmt.Errorf("failed to write UVM reference info: %w", err) - } - } - - if len(hostAMDCert) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.HostAMDCertFilename, []byte(hostAMDCert), 0777); err != nil { - return fmt.Errorf("failed to write host AMD certificate: %w", err) - } - } - - containerCtxDir := fmt.Sprintf("/%s", filepath.Base(securityContextDir)) - secCtxEnv := fmt.Sprintf("UVM_SECURITY_CONTEXT_DIR=%s", containerCtxDir) - spec.Process.Env = append(spec.Process.Env, secCtxEnv) + if oci.ParseAnnotationsBool(ctx, spec.Annotations, annotations.WCOWSecurityPolicyEnv, true) { + if err := b.hostState.securityOptions.WriteSecurityContextDir(&spec); err != nil { + return fmt.Errorf("failed to write security context dir: %w", err) } + cwcowHostedSystemConfig.Spec = spec } // Strip the spec field @@ -190,20 +154,6 @@ func (b *Bridge) createContainer(req *request) (err error) { return nil } -func writeFileInDir(dir string, filename string, data []byte, perm os.FileMode) error { - st, err := os.Stat(dir) - if err != nil { - return err - } - - if !st.IsDir() { - return fmt.Errorf("not a directory %q", dir) - } - - targetFilename := filepath.Join(dir, filename) - return os.WriteFile(targetFilename, data, perm) -} - // processParamEnvToOCIEnv converts an Environment field from ProcessParameters // (a map from environment variable to value) into an array of environment // variable assignments (where each is in the form "=") which @@ -242,7 +192,7 @@ func (b *Bridge) shutdownGraceful(req *request) (err error) { return fmt.Errorf("failed to unmarshal shutdownGraceful: %w", err) } - err = b.hostState.securityPolicyEnforcer.EnforceShutdownContainerPolicy(req.ctx, r.ContainerID) + err = b.hostState.securityOptions.PolicyEnforcer.EnforceShutdownContainerPolicy(req.ctx, r.ContainerID) if err != nil { return fmt.Errorf("rpcShudownGraceful operation not allowed: %w", err) } @@ -295,7 +245,7 @@ func (b *Bridge) executeProcess(req *request) (err error) { if containerID == UVMContainerID { log.G(req.ctx).Tracef("Enforcing policy on external exec process") - _, _, err := b.hostState.securityPolicyEnforcer.EnforceExecExternalProcessPolicy( + _, _, err := b.hostState.securityOptions.PolicyEnforcer.EnforceExecExternalProcessPolicy( req.ctx, commandLine, processParamEnvToOCIEnv(processParams.Environment), @@ -323,7 +273,7 @@ func (b *Bridge) executeProcess(req *request) (err error) { } log.G(req.ctx).Tracef("Enforcing policy on exec in container") - _, _, _, err = b.hostState.securityPolicyEnforcer. + _, _, _, err = b.hostState.securityOptions.PolicyEnforcer. EnforceExecInContainerPolicyV2( req.ctx, containerID, @@ -432,7 +382,7 @@ func (b *Bridge) signalProcess(req *request) (err error) { WindowsSignal: wcowOptions.Signal, WindowsCommand: commandLine, } - err = b.hostState.securityPolicyEnforcer.EnforceSignalContainerProcessPolicyV2(req.ctx, containerID, opts) + err = b.hostState.securityOptions.PolicyEnforcer.EnforceSignalContainerProcessPolicyV2(req.ctx, containerID, opts) if err != nil { return err } @@ -461,7 +411,7 @@ func (b *Bridge) getProperties(req *request) (err error) { defer span.End() defer func() { oc.SetSpanStatus(span, err) }() - if err := b.hostState.securityPolicyEnforcer.EnforceGetPropertiesPolicy(req.ctx); err != nil { + if err := b.hostState.securityOptions.PolicyEnforcer.EnforceGetPropertiesPolicy(req.ctx); err != nil { return errors.Wrapf(err, "get properties denied due to policy") } @@ -599,11 +549,14 @@ func (b *Bridge) modifySettings(req *request) (err error) { log.G(ctx).Tracef("hcsschema.MappedDirectory { %v }", settings) case guestresource.ResourceTypeSecurityPolicy: - securityPolicyRequest := modifyGuestSettingsRequest.Settings.(*guestresource.WCOWConfidentialOptions) + securityPolicyRequest := modifyGuestSettingsRequest.Settings.(*guestresource.ConfidentialOptions) log.G(ctx).Tracef("WCOWConfidentialOptions: { %v}", securityPolicyRequest) - err := b.hostState.SetWCOWConfidentialUVMOptions(req.ctx, securityPolicyRequest, b.logWriter) + err := b.hostState.securityOptions.SetConfidentialOptions(ctx, + securityPolicyRequest.EnforcerType, + securityPolicyRequest.EncodedSecurityPolicy, + securityPolicyRequest.EncodedUVMReference) if err != nil { - return errors.Wrap(err, "error creating enforcer") + return errors.Wrap(err, "Failed to set Confidentia UVM Options") } // Send response back to shim resp := &prot.ResponseBase{ @@ -616,12 +569,11 @@ func (b *Bridge) modifySettings(req *request) (err error) { } return nil case guestresource.ResourceTypePolicyFragment: - //Note: Reusing the same type LCOWSecurityPolicyFragment for CWCOW. - r, ok := modifyGuestSettingsRequest.Settings.(*guestresource.LCOWSecurityPolicyFragment) + r, ok := modifyGuestSettingsRequest.Settings.(*guestresource.SecurityPolicyFragment) if !ok { - return errors.New("the request settings are not of type LCOWSecurityPolicyFragment") + return errors.New("the request settings are not of type SecurityPolicyFragment") } - return b.hostState.InjectFragment(ctx, r) + return b.hostState.securityOptions.InjectFragment(ctx, r) case guestresource.ResourceTypeWCOWBlockCims: // This is request to mount the merged cim at given volumeGUID if modifyGuestSettingsRequest.RequestType == guestrequest.RequestTypeRemove { @@ -635,7 +587,6 @@ func (b *Bridge) modifySettings(req *request) (err error) { // The block device takes some time to show up. Wait for a few seconds. time.Sleep(2 * time.Second) - //TODO(Mahati) : test and verify CIM hashes var layerCIMs []*cimfs.BlockCIM layerHashes := make([]string, len(wcowBlockCimMounts.BlockCIMs)) layerDigests := make([][]byte, len(wcowBlockCimMounts.BlockCIMs)) @@ -671,7 +622,7 @@ func (b *Bridge) modifySettings(req *request) (err error) { hashesToVerify = layerHashes[1:] } - err := b.hostState.securityPolicyEnforcer.EnforceVerifiedCIMsPolicy(req.ctx, containerID, hashesToVerify) + err := b.hostState.securityOptions.PolicyEnforcer.EnforceVerifiedCIMsPolicy(req.ctx, containerID, hashesToVerify) if err != nil { return errors.Wrap(err, "CIM mount is denied by policy") } @@ -711,7 +662,7 @@ func (b *Bridge) modifySettings(req *request) (err error) { containerID, settings.CombinedLayers.ContainerRootPath, settings.CombinedLayers.Layers, settings.CombinedLayers.ScratchPath) //Since unencrypted scratch is not an option, always pass true - if err := b.hostState.securityPolicyEnforcer.EnforceScratchMountPolicy(ctx, settings.CombinedLayers.ContainerRootPath, true); err != nil { + if err := b.hostState.securityOptions.PolicyEnforcer.EnforceScratchMountPolicy(ctx, settings.CombinedLayers.ContainerRootPath, true); err != nil { return fmt.Errorf("scratch mounting denied by policy: %w", err) } // The following two folders are expected to be present in the scratch. diff --git a/internal/gcs-sidecar/host.go b/internal/gcs-sidecar/host.go index 82519ac414..643ac39ef0 100644 --- a/internal/gcs-sidecar/host.go +++ b/internal/gcs-sidecar/host.go @@ -5,38 +5,22 @@ package bridge import ( "context" - "crypto/sha256" - "encoding/base64" - "fmt" "io" - "os" - "path/filepath" "sync" - "time" - "github.com/Microsoft/cosesign1go/pkg/cosesign1" - didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver" "github.com/Microsoft/hcsshim/internal/bridgeutils/gcserr" hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/logfields" - "github.com/Microsoft/hcsshim/internal/protocol/guestresource" - "github.com/Microsoft/hcsshim/internal/pspdriver" "github.com/Microsoft/hcsshim/pkg/securitypolicy" oci "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) type Host struct { + securityOptions *securitypolicy.SecurityOptions containersMutex sync.Mutex containers map[string]*Container - - // state required for the security policy enforcement - policyMutex sync.Mutex - securityPolicyEnforcer securitypolicy.SecurityPolicyEnforcer - securityPolicyEnforcerSet bool - uvmReferenceInfo string } type Container struct { @@ -55,136 +39,17 @@ type containerProcess struct { pid uint32 } -func NewHost(initialEnforcer securitypolicy.SecurityPolicyEnforcer) *Host { - return &Host{ - containers: make(map[string]*Container), - securityPolicyEnforcer: initialEnforcer, - securityPolicyEnforcerSet: false, - } -} - -// InjectFragment extends current security policy with additional constraints -// from the incoming fragment. Note that it is base64 encoded over the bridge/ -// -// There are three checking steps: -// 1 - Unpack the cose document and check it was actually signed with the cert -// chain inside its header -// 2 - Check that the issuer field did:x509 identifier is for that cert chain -// (ie fingerprint of a non leaf cert and the subject matches the leaf cert) -// 3 - Check that this issuer/feed match the requirement of the user provided -// security policy (done in the regoby LoadFragment) -func (h *Host) InjectFragment(ctx context.Context, fragment *guestresource.LCOWSecurityPolicyFragment) (err error) { - log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("GCS Host.InjectFragment") - - raw, err := base64.StdEncoding.DecodeString(fragment.Fragment) - if err != nil { - return err - } - blob := []byte(fragment.Fragment) - // keep a copy of the fragment, so we can manually figure out what went wrong - // will be removed eventually. Give it a unique name to avoid any potential - // race conditions. - sha := sha256.New() - sha.Write(blob) - timestamp := time.Now() - fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli()) - _ = os.WriteFile(filepath.Join(os.TempDir(), fragmentPath), blob, 0644) - - unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw) - if err != nil { - return fmt.Errorf("InjectFragment failed COSE validation: %w", err) - } - - payloadString := string(unpacked.Payload[:]) - issuer := unpacked.Issuer - feed := unpacked.Feed - chainPem := unpacked.ChainPem - - log.G(ctx).WithFields(logrus.Fields{ - "issuer": issuer, // eg the DID:x509:blah.... - "feed": feed, - "cty": unpacked.ContentType, - "chainPem": chainPem, - }).Debugf("unpacked COSE1 cert chain") - - log.G(ctx).WithFields(logrus.Fields{ - "payload": payloadString, - }).Tracef("unpacked COSE1 payload") - - if len(issuer) == 0 || len(feed) == 0 { // must both be present - return fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header") - } - - // Resolve returns a did doc that we don't need - // we only care if there was an error or not - _, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true) - if err != nil { - log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error()) - return err - } - - // now offer the payload fragment to the policy - err = h.securityPolicyEnforcer.LoadFragment(ctx, issuer, feed, payloadString) - if err != nil { - return fmt.Errorf("InjectFragment failed policy load: %w", err) - } - log.G(ctx).Printf("passed fragment into the enforcer.") - - return nil -} - -func (h *Host) SetWCOWConfidentialUVMOptions(ctx context.Context, securityPolicyRequest *guestresource.WCOWConfidentialOptions, logWriter io.Writer) error { - h.policyMutex.Lock() - defer h.policyMutex.Unlock() - - if h.securityPolicyEnforcerSet { - return errors.New("security policy has already been set") - } - - if err := pspdriver.GetPspDriverError(); err != nil { - // For this case gcs-sidecar will keep initial deny policy. - return errors.Wrapf(err, "an error occurred while using PSP driver") - } - - // Fetch report and validate host_data - hostData, err := securitypolicy.NewSecurityPolicyDigest(securityPolicyRequest.EncodedSecurityPolicy) - if err != nil { - return err - } - - if err := pspdriver.ValidateHostData(ctx, hostData[:]); err != nil { - // For this case gcs-sidecar will keep initial deny policy. - return err - } - - // 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 := securitypolicy.CreateSecurityPolicyEnforcer( - securityPolicyRequest.EnforcerType, - securityPolicyRequest.EncodedSecurityPolicy, - DefaultCRIMounts(), - DefaultCRIPrivilegedMounts(), - maxErrorMessageLength, +func NewHost(initialEnforcer securitypolicy.SecurityPolicyEnforcer, logWriter io.Writer) *Host { + securityPolicyOptions := securitypolicy.NewSecurityOptions( + initialEnforcer, + false, + "", + logWriter, ) - if err != nil { - return fmt.Errorf("error creating security policy enforcer: %w", err) - } - - if err = p.EnforceRuntimeLoggingPolicy(ctx); err == nil { - logrus.SetOutput(logWriter) - } else { - logrus.SetOutput(io.Discard) + return &Host{ + containers: make(map[string]*Container), + securityOptions: securityPolicyOptions, } - - h.securityPolicyEnforcer = p - h.securityPolicyEnforcerSet = true - h.uvmReferenceInfo = securityPolicyRequest.EncodedUVMReference - - return nil } func (h *Host) AddContainer(ctx context.Context, id string, c *Container) error { diff --git a/internal/gcs-sidecar/policy.go b/internal/gcs-sidecar/policy.go deleted file mode 100644 index 13b96ce64d..0000000000 --- a/internal/gcs-sidecar/policy.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build windows -// +build windows - -package bridge - -import ( - oci "github.com/opencontainers/runtime-spec/specs-go" -) - -// DefaultCRIMounts returns default mounts added to windows spec by containerD. -func DefaultCRIMounts() []oci.Mount { - return []oci.Mount{} -} - -// DefaultCRIPrivilegedMounts returns a slice of mounts which are added to the -// windows container spec when a container runs in a privileged mode. -func DefaultCRIPrivilegedMounts() []oci.Mount { - return []oci.Mount{} -} diff --git a/internal/gcs-sidecar/uvm.go b/internal/gcs-sidecar/uvm.go index 9578e0273c..b3a7792fd4 100644 --- a/internal/gcs-sidecar/uvm.go +++ b/internal/gcs-sidecar/uvm.go @@ -92,7 +92,7 @@ func unmarshalContainerModifySettings(req *request) (_ *prot.ContainerModifySett modifyGuestSettingsRequest.Settings = settings case guestresource.ResourceTypeSecurityPolicy: - securityPolicyRequest := &guestresource.WCOWConfidentialOptions{} + securityPolicyRequest := &guestresource.ConfidentialOptions{} if err := commonutils.UnmarshalJSONWithHresult(rawGuestRequest, securityPolicyRequest); err != nil { return nil, fmt.Errorf("invalid ResourceTypeSecurityPolicy request: %w", err) } diff --git a/internal/guest/policy/default.go b/internal/guest/policy/default.go deleted file mode 100644 index 98c909fb35..0000000000 --- a/internal/guest/policy/default.go +++ /dev/null @@ -1,98 +0,0 @@ -//go:build linux -// +build linux - -package policy - -import ( - oci "github.com/opencontainers/runtime-spec/specs-go" - - specGuest "github.com/Microsoft/hcsshim/internal/guest/spec" - "github.com/Microsoft/hcsshim/pkg/securitypolicy" -) - -func ExtendPolicyWithNetworkingMounts(sandboxID string, enforcer securitypolicy.SecurityPolicyEnforcer, spec *oci.Spec) error { - roSpec := &oci.Spec{ - Root: spec.Root, - } - networkingMounts := specGuest.GenerateWorkloadContainerNetworkMounts(sandboxID, roSpec) - if err := enforcer.ExtendDefaultMounts(networkingMounts); err != nil { - return err - } - return nil -} - -// DefaultCRIMounts returns default mounts added to linux spec by containerD. -func DefaultCRIMounts() []oci.Mount { - return []oci.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/dev", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - { - Destination: "/dev/shm", - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, - }, - { - Destination: "/dev/mqueue", - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "ro"}, - }, - { - Destination: "/run", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - // cgroup mount is always added by default, regardless if it is present - // in the mount constraints or not. If the user chooses to override it, - // then a corresponding mount constraint should be present. - { - Source: "cgroup", - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"}, - }, - } -} - -// DefaultCRIPrivilegedMounts returns a slice of mounts which are added to the -// linux container spec when a container runs in a privileged mode. -func DefaultCRIPrivilegedMounts() []oci.Mount { - return []oci.Mount{ - { - Source: "cgroup", - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Options: []string{"nosuid", "noexec", "nodev", "relatime", "rw"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "rw"}, - }, - } -} diff --git a/internal/guest/policy/doc.go b/internal/guest/policy/doc.go deleted file mode 100644 index 8cbf7ee3fc..0000000000 --- a/internal/guest/policy/doc.go +++ /dev/null @@ -1 +0,0 @@ -package policy diff --git a/internal/guest/prot/protocol.go b/internal/guest/prot/protocol.go index 576ac5e5f1..16e1f9daa3 100644 --- a/internal/guest/prot/protocol.go +++ b/internal/guest/prot/protocol.go @@ -583,15 +583,15 @@ func UnmarshalContainerModifySettings(b []byte) (*containerModifySettings, error } msr.Settings = cc case guestresource.ResourceTypeSecurityPolicy: - enforcer := &guestresource.LCOWConfidentialOptions{} + enforcer := &guestresource.ConfidentialOptions{} if err := commonutils.UnmarshalJSONWithHresult(msrRawSettings, enforcer); err != nil { - return &request, errors.Wrap(err, "failed to unmarshal settings as LCOWConfidentialOptions") + return &request, errors.Wrap(err, "failed to unmarshal settings as ConfidentialOptions") } msr.Settings = enforcer case guestresource.ResourceTypePolicyFragment: - fragment := &guestresource.LCOWSecurityPolicyFragment{} + fragment := &guestresource.SecurityPolicyFragment{} if err := commonutils.UnmarshalJSONWithHresult(msrRawSettings, fragment); err != nil { - return &request, errors.Wrap(err, "failed to unmarshal settings as LCOWSecurityPolicyFragment") + return &request, errors.Wrap(err, "failed to unmarshal settings as SecurityPolicyFragment") } msr.Settings = fragment default: diff --git a/internal/guest/runtime/hcsv2/hostdata.go b/internal/guest/runtime/hcsv2/hostdata.go deleted file mode 100644 index d75463fcda..0000000000 --- a/internal/guest/runtime/hcsv2/hostdata.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build linux -// +build linux - -package hcsv2 - -import ( - "bytes" - "fmt" - - "github.com/Microsoft/hcsshim/pkg/amdsevsnp" -) - -// validateHostData fetches SNP report (if applicable) and validates `hostData` against -// HostData set at UVM launch. -func validateHostData(hostData []byte) error { - // If the UVM is not SNP, then don't try to fetch an SNP report. - if !amdsevsnp.IsSNP() { - return nil - } - report, err := amdsevsnp.FetchParsedSNPReport(nil) - if err != nil { - return err - } - - if !bytes.Equal(hostData, report.HostData) { - return fmt.Errorf( - "security policy digest %q doesn't match HostData provided at launch %q", - hostData, - report.HostData, - ) - } - return nil -} diff --git a/internal/guest/runtime/hcsv2/uvm.go b/internal/guest/runtime/hcsv2/uvm.go index 7b53536100..677e09f255 100644 --- a/internal/guest/runtime/hcsv2/uvm.go +++ b/internal/guest/runtime/hcsv2/uvm.go @@ -6,8 +6,6 @@ package hcsv2 import ( "bufio" "context" - "crypto/sha256" - "encoding/base64" "encoding/json" "fmt" "io" @@ -20,8 +18,6 @@ import ( "syscall" "time" - "github.com/Microsoft/cosesign1go/pkg/cosesign1" - didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver" cgroups "github.com/containerd/cgroups/v3/cgroup1" cgroup1stats "github.com/containerd/cgroups/v3/cgroup1/stats" "github.com/mattn/go-shellwords" @@ -32,7 +28,6 @@ import ( "github.com/Microsoft/hcsshim/internal/bridgeutils/gcserr" "github.com/Microsoft/hcsshim/internal/debug" - "github.com/Microsoft/hcsshim/internal/guest/policy" "github.com/Microsoft/hcsshim/internal/guest/prot" "github.com/Microsoft/hcsshim/internal/guest/runtime" specGuest "github.com/Microsoft/hcsshim/internal/guest/spec" @@ -89,164 +84,39 @@ type Host struct { devNullTransport transport.Transport // state required for the security policy enforcement - policyMutex sync.Mutex - securityPolicyEnforcer securitypolicy.SecurityPolicyEnforcer - securityPolicyEnforcerSet bool - uvmReferenceInfo string + securityOptions *securitypolicy.SecurityOptions - // logging target - logWriter io.Writer // hostMounts keeps the state of currently mounted devices and file systems, // which is used for GCS hardening. hostMounts *hostMounts } func NewHost(rtime runtime.Runtime, vsock transport.Transport, initialEnforcer securitypolicy.SecurityPolicyEnforcer, logWriter io.Writer) *Host { - return &Host{ - containers: make(map[string]*Container), - externalProcesses: make(map[int]*externalProcess), - virtualPods: make(map[string]*VirtualPod), - containerToVirtualPod: make(map[string]string), - rtime: rtime, - vsock: vsock, - devNullTransport: &transport.DevNullTransport{}, - securityPolicyEnforcerSet: false, - securityPolicyEnforcer: initialEnforcer, - logWriter: logWriter, - hostMounts: newHostMounts(), - } -} - -// SetConfidentialUVMOptions takes guestresource.LCOWConfidentialOptions -// to set up our internal data structures we use to store and enforce -// security policy. The options can contain security policy enforcer type, -// encoded security policy and signed UVM reference information The security -// policy and uvm reference information can be further presented to workload -// containers for validation and attestation purposes. -func (h *Host) SetConfidentialUVMOptions(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error { - h.policyMutex.Lock() - defer h.policyMutex.Unlock() - if h.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 := securitypolicy.CreateSecurityPolicyEnforcer( - r.EnforcerType, - r.EncodedSecurityPolicy, - policy.DefaultCRIMounts(), - policy.DefaultCRIPrivilegedMounts(), - maxErrorMessageLength, + securityPolicyOptions := securitypolicy.NewSecurityOptions( + initialEnforcer, + false, + "", + logWriter, ) - if err != nil { - return err - } - - // 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 + return &Host{ + containers: make(map[string]*Container), + externalProcesses: make(map[int]*externalProcess), + virtualPods: make(map[string]*VirtualPod), + containerToVirtualPod: make(map[string]string), + rtime: rtime, + vsock: vsock, + devNullTransport: &transport.DevNullTransport{}, + hostMounts: newHostMounts(), + securityOptions: securityPolicyOptions, } - - h.securityPolicyEnforcer = p - h.securityPolicyEnforcerSet = true - h.uvmReferenceInfo = r.EncodedUVMReference - - return nil } -// InjectFragment extends current security policy with additional constraints -// from the incoming fragment. Note that it is base64 encoded over the bridge/ -// -// There are three checking steps: -// 1 - Unpack the cose document and check it was actually signed with the cert -// chain inside its header -// 2 - Check that the issuer field did:x509 identifier is for that cert chain -// (ie fingerprint of a non leaf cert and the subject matches the leaf cert) -// 3 - Check that this issuer/feed match the requirement of the user provided -// security policy (done in the regoby LoadFragment) -func (h *Host) InjectFragment(ctx context.Context, fragment *guestresource.LCOWSecurityPolicyFragment) (err error) { - log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("GCS Host.InjectFragment") - - raw, err := base64.StdEncoding.DecodeString(fragment.Fragment) - if err != nil { - return err - } - blob := []byte(fragment.Fragment) - // keep a copy of the fragment, so we can manually figure out what went wrong - // will be removed eventually. Give it a unique name to avoid any potential - // race conditions. - sha := sha256.New() - sha.Write(blob) - timestamp := time.Now() - fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli()) - _ = os.WriteFile(filepath.Join("/tmp", fragmentPath), blob, 0644) - - unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw) - if err != nil { - return fmt.Errorf("InjectFragment failed COSE validation: %w", err) - } - - payloadString := string(unpacked.Payload[:]) - issuer := unpacked.Issuer - feed := unpacked.Feed - chainPem := unpacked.ChainPem - - log.G(ctx).WithFields(logrus.Fields{ - "issuer": issuer, // eg the DID:x509:blah.... - "feed": feed, - "cty": unpacked.ContentType, - "chainPem": chainPem, - }).Debugf("unpacked COSE1 cert chain") - - log.G(ctx).WithFields(logrus.Fields{ - "payload": payloadString, - }).Tracef("unpacked COSE1 payload") - - if len(issuer) == 0 || len(feed) == 0 { // must both be present - return fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header") - } - - // Resolve returns a did doc that we don't need - // we only care if there was an error or not - _, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true) - if err != nil { - log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error()) - return err - } - - // now offer the payload fragment to the policy - err = h.securityPolicyEnforcer.LoadFragment(ctx, issuer, feed, payloadString) - if err != nil { - return fmt.Errorf("InjectFragment failed policy load: %w", err) - } - log.G(ctx).Printf("passed fragment into the enforcer.") - - return nil +func (h *Host) SecurityPolicyEnforcer() securitypolicy.SecurityPolicyEnforcer { + return h.securityOptions.PolicyEnforcer } -func (h *Host) SecurityPolicyEnforcer() securitypolicy.SecurityPolicyEnforcer { - return h.securityPolicyEnforcer +func (h *Host) SecurityOptions() *securitypolicy.SecurityOptions { + return h.securityOptions } func (h *Host) Transport() transport.Transport { @@ -516,7 +386,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM return nil, err } - if err := policy.ExtendPolicyWithNetworkingMounts(id, h.securityPolicyEnforcer, settings.OCISpecification); err != nil { + if err := securitypolicy.ExtendPolicyWithNetworkingMounts(id, h.securityOptions.PolicyEnforcer, settings.OCISpecification); err != nil { return nil, err } case "container": @@ -531,7 +401,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM // Add SEV device when security policy is not empty, except when privileged annotation is // set to "true", in which case all UVMs devices are added. - if len(h.securityPolicyEnforcer.EncodedSecurityPolicy()) > 0 && !oci.ParseAnnotationsBool(ctx, + if len(h.securityOptions.PolicyEnforcer.EncodedSecurityPolicy()) > 0 && !oci.ParseAnnotationsBool(ctx, settings.OCISpecification.Annotations, annotations.LCOWPrivileged, false) { if err := specGuest.AddDevSev(ctx, settings.OCISpecification); err != nil { log.G(ctx).WithError(err).Debug("failed to add SEV device") @@ -543,7 +413,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM _ = os.RemoveAll(settings.OCIBundlePath) } }() - if err := policy.ExtendPolicyWithNetworkingMounts(sandboxID, h.securityPolicyEnforcer, settings.OCISpecification); err != nil { + if err := securitypolicy.ExtendPolicyWithNetworkingMounts(sandboxID, h.securityOptions.PolicyEnforcer, settings.OCISpecification); err != nil { return nil, err } default: @@ -560,7 +430,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM _ = os.RemoveAll(settings.OCIBundlePath) } }() - if err := policy.ExtendPolicyWithNetworkingMounts(id, h.securityPolicyEnforcer, + if err := securitypolicy.ExtendPolicyWithNetworkingMounts(id, h.securityOptions.PolicyEnforcer, settings.OCISpecification); err != nil { return nil, err } @@ -577,7 +447,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM }) } - user, groups, umask, err := h.securityPolicyEnforcer.GetUserInfo(settings.OCISpecification.Process, settings.OCISpecification.Root.Path) + user, groups, umask, err := h.securityOptions.PolicyEnforcer.GetUserInfo(settings.OCISpecification.Process, settings.OCISpecification.Root.Path) if err != nil { return nil, err } @@ -587,7 +457,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM return nil, err } - envToKeep, capsToKeep, allowStdio, err := h.securityPolicyEnforcer.EnforceCreateContainerPolicy( + envToKeep, capsToKeep, allowStdio, err := h.securityOptions.PolicyEnforcer.EnforceCreateContainerPolicy( ctx, sandboxID, id, @@ -645,47 +515,9 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM settings.OCISpecification.Process.Capabilities = capsToKeep } - // Write security policy, signed UVM reference and host AMD certificate to - // container's rootfs, so that application and sidecar containers can have - // access to it. The security policy is required by containers which need to - // extract init-time claims found in the security policy. The directory path - // containing the files is exposed via UVM_SECURITY_CONTEXT_DIR env var. - // It may be an error to have a security policy but not expose it to the - // container as in that case it can never be checked as correct by a verifier. if oci.ParseAnnotationsBool(ctx, settings.OCISpecification.Annotations, annotations.LCOWSecurityPolicyEnv, true) { - encodedPolicy := h.securityPolicyEnforcer.EncodedSecurityPolicy() - hostAMDCert := settings.OCISpecification.Annotations[annotations.LCOWHostAMDCertificate] - if len(encodedPolicy) > 0 || len(hostAMDCert) > 0 || len(h.uvmReferenceInfo) > 0 { - // Use os.MkdirTemp to make sure that the directory is unique. - securityContextDir, err := os.MkdirTemp(settings.OCISpecification.Root.Path, securitypolicy.SecurityContextDirTemplate) - if err != nil { - return nil, fmt.Errorf("failed to create security context directory: %w", err) - } - // Make sure that files inside directory are readable - if err := os.Chmod(securityContextDir, 0755); err != nil { - return nil, fmt.Errorf("failed to chmod security context directory: %w", err) - } - - if len(encodedPolicy) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.PolicyFilename, []byte(encodedPolicy), 0744); err != nil { - return nil, fmt.Errorf("failed to write security policy: %w", err) - } - } - if len(h.uvmReferenceInfo) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.ReferenceInfoFilename, []byte(h.uvmReferenceInfo), 0744); err != nil { - return nil, fmt.Errorf("failed to write UVM reference info: %w", err) - } - } - - if len(hostAMDCert) > 0 { - if err := writeFileInDir(securityContextDir, securitypolicy.HostAMDCertFilename, []byte(hostAMDCert), 0744); err != nil { - return nil, fmt.Errorf("failed to write host AMD certificate: %w", err) - } - } - - containerCtxDir := fmt.Sprintf("/%s", filepath.Base(securityContextDir)) - secCtxEnv := fmt.Sprintf("UVM_SECURITY_CONTEXT_DIR=%s", containerCtxDir) - settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env, secCtxEnv) + if err := h.securityOptions.WriteSecurityContextDir(settings.OCISpecification); err != nil { + return nil, fmt.Errorf("failed to write security context dir: %w", err) } } @@ -783,18 +615,18 @@ func (h *Host) modifyHostSettings(ctx context.Context, containerID string, req * }() } } - return modifyMappedVirtualDisk(ctx, req.RequestType, mvd, h.securityPolicyEnforcer) + return modifyMappedVirtualDisk(ctx, req.RequestType, mvd, h.securityOptions.PolicyEnforcer) case guestresource.ResourceTypeMappedDirectory: - return modifyMappedDirectory(ctx, h.vsock, req.RequestType, req.Settings.(*guestresource.LCOWMappedDirectory), h.securityPolicyEnforcer) + return modifyMappedDirectory(ctx, h.vsock, req.RequestType, req.Settings.(*guestresource.LCOWMappedDirectory), h.securityOptions.PolicyEnforcer) case guestresource.ResourceTypeVPMemDevice: - return modifyMappedVPMemDevice(ctx, req.RequestType, req.Settings.(*guestresource.LCOWMappedVPMemDevice), h.securityPolicyEnforcer) + return modifyMappedVPMemDevice(ctx, req.RequestType, req.Settings.(*guestresource.LCOWMappedVPMemDevice), h.securityOptions.PolicyEnforcer) case guestresource.ResourceTypeCombinedLayers: cl := req.Settings.(*guestresource.LCOWCombinedLayers) // when cl.ScratchPath == "", we mount overlay as read-only, in which case // we don't really care about scratch encryption, since the host already // knows about the layers and the overlayfs. encryptedScratch := cl.ScratchPath != "" && h.hostMounts.IsEncrypted(cl.ScratchPath) - return modifyCombinedLayers(ctx, req.RequestType, req.Settings.(*guestresource.LCOWCombinedLayers), encryptedScratch, h.securityPolicyEnforcer) + return modifyCombinedLayers(ctx, req.RequestType, req.Settings.(*guestresource.LCOWCombinedLayers), encryptedScratch, h.securityOptions.PolicyEnforcer) case guestresource.ResourceTypeNetwork: return modifyNetwork(ctx, req.RequestType, req.Settings.(*guestresource.LCOWNetworkAdapter)) case guestresource.ResourceTypeVPCIDevice: @@ -806,17 +638,20 @@ func (h *Host) modifyHostSettings(ctx context.Context, containerID string, req * } return c.modifyContainerConstraints(ctx, req.RequestType, req.Settings.(*guestresource.LCOWContainerConstraints)) case guestresource.ResourceTypeSecurityPolicy: - r, ok := req.Settings.(*guestresource.LCOWConfidentialOptions) + r, ok := req.Settings.(*guestresource.ConfidentialOptions) if !ok { - return errors.New("the request's settings are not of type LCOWConfidentialOptions") + return errors.New("the request's settings are not of type ConfidentialOptions") } - return h.SetConfidentialUVMOptions(ctx, r) + return h.securityOptions.SetConfidentialOptions(ctx, + r.EnforcerType, + r.EncodedSecurityPolicy, + r.EncodedUVMReference) case guestresource.ResourceTypePolicyFragment: - r, ok := req.Settings.(*guestresource.LCOWSecurityPolicyFragment) + r, ok := req.Settings.(*guestresource.SecurityPolicyFragment) if !ok { - return errors.New("the request settings are not of type LCOWSecurityPolicyFragment") + return errors.New("the request settings are not of type SecurityPolicyFragment") } - return h.InjectFragment(ctx, r) + return h.securityOptions.InjectFragment(ctx, r) default: return errors.Errorf("the ResourceType %q is not supported for UVM", req.ResourceType) } @@ -856,7 +691,7 @@ func (h *Host) ShutdownContainer(ctx context.Context, containerID string, gracef return err } - err = h.securityPolicyEnforcer.EnforceShutdownContainerPolicy(ctx, containerID) + err = h.securityOptions.PolicyEnforcer.EnforceShutdownContainerPolicy(ctx, containerID) if err != nil { return err } @@ -883,7 +718,7 @@ func (h *Host) SignalContainerProcess(ctx context.Context, containerID string, p signalingInitProcess := processID == c.initProcess.pid startupArgList := p.(*containerProcess).spec.Args - err = h.securityPolicyEnforcer.EnforceSignalContainerProcessPolicy(ctx, containerID, signal, signalingInitProcess, startupArgList) + err = h.securityOptions.PolicyEnforcer.EnforceSignalContainerProcessPolicy(ctx, containerID, signal, signalingInitProcess, startupArgList) if err != nil { return err } @@ -898,7 +733,7 @@ func (h *Host) ExecProcess(ctx context.Context, containerID string, params prot. if params.IsExternal || containerID == UVMContainerID { var envToKeep securitypolicy.EnvList var allowStdioAccess bool - envToKeep, allowStdioAccess, err = h.securityPolicyEnforcer.EnforceExecExternalProcessPolicy( + envToKeep, allowStdioAccess, err = h.securityOptions.PolicyEnforcer.EnforceExecExternalProcessPolicy( ctx, params.CommandArgs, processParamEnvToOCIEnv(params.Environment), @@ -940,12 +775,12 @@ func (h *Host) ExecProcess(ctx context.Context, containerID string, params prot. var umask string var allowStdioAccess bool - user, groups, umask, err = h.securityPolicyEnforcer.GetUserInfo(params.OCIProcess, c.spec.Root.Path) + user, groups, umask, err = h.securityOptions.PolicyEnforcer.GetUserInfo(params.OCIProcess, c.spec.Root.Path) if err != nil { return 0, err } - envToKeep, capsToKeep, allowStdioAccess, err = h.securityPolicyEnforcer.EnforceExecInContainerPolicy( + envToKeep, capsToKeep, allowStdioAccess, err = h.securityOptions.PolicyEnforcer.EnforceExecInContainerPolicy( ctx, containerID, params.OCIProcess.Args, @@ -994,7 +829,7 @@ func (h *Host) GetExternalProcess(pid int) (Process, error) { } func (h *Host) GetProperties(ctx context.Context, containerID string, query prot.PropertyQuery) (*prot.PropertiesV2, error) { - err := h.securityPolicyEnforcer.EnforceGetPropertiesPolicy(ctx) + err := h.securityOptions.PolicyEnforcer.EnforceGetPropertiesPolicy(ctx) if err != nil { return nil, errors.Wrapf(err, "get properties denied due to policy") } @@ -1041,7 +876,7 @@ func (h *Host) GetProperties(ctx context.Context, containerID string, query prot } func (h *Host) GetStacks(ctx context.Context) (string, error) { - err := h.securityPolicyEnforcer.EnforceDumpStacksPolicy(ctx) + err := h.securityOptions.PolicyEnforcer.EnforceDumpStacksPolicy(ctx) if err != nil { return "", errors.Wrapf(err, "dump stacks denied due to policy") } @@ -1418,20 +1253,6 @@ func isPrivilegedContainerCreationRequest(ctx context.Context, spec *specs.Spec) return oci.ParseAnnotationsBool(ctx, spec.Annotations, annotations.LCOWPrivileged, false) } -func writeFileInDir(dir string, filename string, data []byte, perm os.FileMode) error { - st, err := os.Stat(dir) - if err != nil { - return err - } - - if !st.IsDir() { - return fmt.Errorf("not a directory %q", dir) - } - - targetFilename := filepath.Join(dir, filename) - return os.WriteFile(targetFilename, data, perm) -} - // Virtual Pod Management Methods // InitializeVirtualPodSupport sets up the parent cgroup for virtual pods diff --git a/internal/protocol/guestresource/resources.go b/internal/protocol/guestresource/resources.go index b956069107..8a58949281 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -229,20 +229,14 @@ type SignalProcessOptionsWCOW struct { Signal guestrequest.SignalValueWCOW `json:",omitempty"` } -// LCOWConfidentialOptions is used to set various confidential container specific +// ConfidentialOptions is used to set various confidential container specific // options. -type LCOWConfidentialOptions struct { +type ConfidentialOptions struct { EnforcerType string `json:"EnforcerType,omitempty"` EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"` EncodedUVMReference string `json:"EncodedUVMReference,omitempty"` } -type LCOWSecurityPolicyFragment struct { +type SecurityPolicyFragment struct { Fragment string `json:"Fragment,omitempty"` } - -type WCOWConfidentialOptions struct { - EnforcerType string `json:"EnforcerType,omitempty"` - EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"` - EncodedUVMReference string `json:"EncodedUVMReference,omitempty"` -} diff --git a/internal/uvm/security_policy.go b/internal/uvm/security_policy.go index 0dcf4fe693..3fa47e87b1 100644 --- a/internal/uvm/security_policy.go +++ b/internal/uvm/security_policy.go @@ -16,11 +16,11 @@ import ( "github.com/Microsoft/hcsshim/pkg/ctrdtaskapi" ) -type ConfidentialUVMOpt func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error +type ConfidentialUVMOpt func(ctx context.Context, r *guestresource.ConfidentialOptions) error // WithSecurityPolicy sets the desired security policy for the resource. func WithSecurityPolicy(policy string) ConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error { + return func(ctx context.Context, r *guestresource.ConfidentialOptions) error { r.EncodedSecurityPolicy = policy return nil } @@ -28,75 +28,12 @@ func WithSecurityPolicy(policy string) ConfidentialUVMOpt { // WithSecurityPolicyEnforcer sets the desired enforcer type for the resource. func WithSecurityPolicyEnforcer(enforcer string) ConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error { + return func(ctx context.Context, r *guestresource.ConfidentialOptions) error { r.EnforcerType = enforcer return nil } } -// TODO (Mahati): Move this block out later -type WCOWConfidentialUVMOpt func(ctx context.Context, r *guestresource.WCOWConfidentialOptions) error - -// WithSecurityPolicy sets the desired security policy for the resource. -func WithWCOWSecurityPolicy(policy string) WCOWConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.WCOWConfidentialOptions) error { - r.EncodedSecurityPolicy = policy - return nil - } -} - -// WithSecurityPolicyEnforcer sets the desired enforcer type for the resource. -func WithWCOWSecurityPolicyEnforcer(enforcer string) WCOWConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.WCOWConfidentialOptions) error { - r.EnforcerType = enforcer - return nil - } -} - -// WithUVMReferenceInfo reads UVM reference info file and base64 encodes the -// content before setting it for the resource. This is no-op if the -// path is empty or the file doesn't exist. -func WithWCOWUVMReferenceInfo(path string) WCOWConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.WCOWConfidentialOptions) error { - encoded, err := base64EncodeFileContents(path) - if err != nil { - if os.IsNotExist(err) { - log.G(ctx).WithField("filePath", path).Debug("UVM reference info file not found") - return nil - } - return fmt.Errorf("failed to read UVM reference info file: %w", err) - } - r.EncodedUVMReference = encoded - return nil - } -} - -func (uvm *UtilityVM) SetWCOWConfidentialUVMOptions(ctx context.Context, opts ...WCOWConfidentialUVMOpt) error { - if uvm.operatingSystem != "windows" { - return errNotSupported - } - uvm.m.Lock() - defer uvm.m.Unlock() - confOpts := &guestresource.WCOWConfidentialOptions{} - for _, o := range opts { - if err := o(ctx, confOpts); err != nil { - return err - } - } - modification := &hcsschema.ModifySettingRequest{ - RequestType: guestrequest.RequestTypeAdd, - GuestRequest: guestrequest.ModificationRequest{ - ResourceType: guestresource.ResourceTypeSecurityPolicy, - RequestType: guestrequest.RequestTypeAdd, - Settings: *confOpts, - }, - } - if err := uvm.modify(ctx, modification); err != nil { - return fmt.Errorf("uvm::Policy: failed to modify utility VM configuration: %w", err) - } - return nil -} - func base64EncodeFileContents(filePath string) (string, error) { if filePath == "" { return "", nil @@ -112,7 +49,7 @@ func base64EncodeFileContents(filePath string) (string, error) { // content before setting it for the resource. This is no-op if the // `referenceName` is empty or the file doesn't exist. func WithUVMReferenceInfo(referenceRoot string, referenceName string) ConfidentialUVMOpt { - return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error { + return func(ctx context.Context, r *guestresource.ConfidentialOptions) error { if referenceName == "" { return nil } @@ -137,14 +74,10 @@ func WithUVMReferenceInfo(referenceRoot string, referenceName string) Confidenti // This has to happen before we start mounting things or generally changing // the state of the UVM after is has been measured at startup func (uvm *UtilityVM) SetConfidentialUVMOptions(ctx context.Context, opts ...ConfidentialUVMOpt) error { - if uvm.operatingSystem != "linux" { - return errNotSupported - } - uvm.m.Lock() defer uvm.m.Unlock() - confOpts := &guestresource.LCOWConfidentialOptions{} + confOpts := &guestresource.ConfidentialOptions{} for _, o := range opts { if err := o(ctx, confOpts); err != nil { return err @@ -174,7 +107,7 @@ func (uvm *UtilityVM) InjectPolicyFragment(ctx context.Context, fragment *ctrdta GuestRequest: guestrequest.ModificationRequest{ ResourceType: guestresource.ResourceTypePolicyFragment, RequestType: guestrequest.RequestTypeAdd, - Settings: guestresource.LCOWSecurityPolicyFragment{ + Settings: guestresource.SecurityPolicyFragment{ Fragment: fragment.Fragment, }, }, diff --git a/internal/uvm/start.go b/internal/uvm/start.go index 321f5af67a..62dd999da5 100644 --- a/internal/uvm/start.go +++ b/internal/uvm/start.go @@ -326,28 +326,29 @@ func (uvm *UtilityVM) Start(ctx context.Context) (err error) { } uvm.SCSIManager = mgr - if uvm.confidentialUVMOptions != nil && uvm.OS() == "linux" { + var policy, enforcer, referenceInfoFileRoot, referenceInfoFilePath string + + if uvm.confidentialUVMOptions != nil || uvm.HasConfidentialPolicy() { + if uvm.confidentialUVMOptions != nil && uvm.OS() == "linux" { + policy = uvm.confidentialUVMOptions.SecurityPolicy + enforcer = uvm.confidentialUVMOptions.SecurityPolicyEnforcer + referenceInfoFilePath = uvm.confidentialUVMOptions.UVMReferenceInfoFile + referenceInfoFileRoot = defaultLCOWOSBootFilesPath() + } else if uvm.HasConfidentialPolicy() && uvm.OS() == "windows" { + policy = uvm.createOpts.(*OptionsWCOW).SecurityPolicy + enforcer = uvm.createOpts.(*OptionsWCOW).SecurityPolicyEnforcer + referenceInfoFilePath = uvm.createOpts.(*OptionsWCOW).UVMReferenceInfoFile + } copts := []ConfidentialUVMOpt{ - WithSecurityPolicy(uvm.confidentialUVMOptions.SecurityPolicy), - WithSecurityPolicyEnforcer(uvm.confidentialUVMOptions.SecurityPolicyEnforcer), - WithUVMReferenceInfo(defaultLCOWOSBootFilesPath(), uvm.confidentialUVMOptions.UVMReferenceInfoFile), + WithSecurityPolicy(policy), + WithSecurityPolicyEnforcer(enforcer), + WithUVMReferenceInfo(referenceInfoFileRoot, referenceInfoFilePath), } if err := uvm.SetConfidentialUVMOptions(ctx, copts...); err != nil { return err } } - if uvm.HasConfidentialPolicy() && uvm.OS() == "windows" { - copts := []WCOWConfidentialUVMOpt{ - WithWCOWSecurityPolicy(uvm.createOpts.(*OptionsWCOW).SecurityPolicy), - WithWCOWSecurityPolicyEnforcer(uvm.createOpts.(*OptionsWCOW).SecurityPolicyEnforcer), - WithWCOWUVMReferenceInfo(uvm.createOpts.(*OptionsWCOW).UVMReferenceInfoFile), - } - if err := uvm.SetWCOWConfidentialUVMOptions(ctx, copts...); err != nil { - return err - } - } - return nil } diff --git a/internal/pspdriver/pspdriver.go b/pkg/securitypolicy/pspdriver.go similarity index 95% rename from internal/pspdriver/pspdriver.go rename to pkg/securitypolicy/pspdriver.go index db41384853..869eaeb04c 100644 --- a/internal/pspdriver/pspdriver.go +++ b/pkg/securitypolicy/pspdriver.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -package pspdriver +package securitypolicy import ( "bytes" @@ -217,7 +217,7 @@ func GetPspDriverError() error { } // IsSNPMode() returns true if it's in SNP mode. -func IsSNPMode(ctx context.Context) (bool, error) { +func IsSNPMode() (bool, error) { if pspDriverError != nil { return false, pspDriverError @@ -249,7 +249,7 @@ func IsSNPMode(ctx context.Context) (bool, error) { } // FetchRawSNPReport returns attestation report bytes. -func FetchRawSNPReport(ctx context.Context, reportData []byte) ([]byte, error) { +func FetchRawSNPReport(reportData []byte) ([]byte, error) { if pspDriverError != nil { return nil, pspDriverError } @@ -291,8 +291,8 @@ func FetchRawSNPReport(ctx context.Context, reportData []byte) ([]byte, error) { } // FetchParsedSNPReport parses raw attestation response into proper structs. -func FetchParsedSNPReport(ctx context.Context, reportData []byte) (Report, error) { - rawBytes, err := FetchRawSNPReport(ctx, reportData) +func FetchParsedSNPReport(reportData []byte) (Report, error) { + rawBytes, err := FetchRawSNPReport(reportData) if err != nil { return Report{}, err } @@ -308,16 +308,16 @@ func FetchParsedSNPReport(ctx context.Context, reportData []byte) (Report, error // TODO: Based on internal\guest\runtime\hcsv2\hostdata.go and it's duplicated. // ValidateHostData fetches SNP report (if applicable) and validates `hostData` against // HostData set at UVM launch. -func ValidateHostData(ctx context.Context, hostData []byte) error { +func ValidateHostDataPSP(hostData []byte) error { // If the UVM is not SNP, then don't try to fetch an SNP report. - isSnpMode, err := IsSNPMode(ctx) + isSnpMode, err := IsSNPMode() if err != nil { return err } if !isSnpMode { return nil } - report, err := FetchParsedSNPReport(ctx, nil) + report, err := FetchParsedSNPReport(nil) if err != nil { return err } diff --git a/pkg/securitypolicy/securitypolicy_linux.go b/pkg/securitypolicy/securitypolicy_linux.go index cb04e03d92..278038ac67 100644 --- a/pkg/securitypolicy/securitypolicy_linux.go +++ b/pkg/securitypolicy/securitypolicy_linux.go @@ -4,12 +4,14 @@ package securitypolicy import ( + "bytes" "fmt" "os" "path/filepath" "strconv" specInternal "github.com/Microsoft/hcsshim/internal/guest/spec" + "github.com/Microsoft/hcsshim/pkg/amdsevsnp" "github.com/moby/sys/user" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -18,6 +20,114 @@ import ( //nolint:unused const osType = "linux" +// validateHostData fetches SNP report (if applicable) and validates `hostData` against +// HostData set at UVM launch. +func validateHostData(hostData []byte) error { + // If the UVM is not SNP, then don't try to fetch an SNP report. + if !amdsevsnp.IsSNP() { + return nil + } + report, err := amdsevsnp.FetchParsedSNPReport(nil) + if err != nil { + return err + } + + if !bytes.Equal(hostData, report.HostData) { + return fmt.Errorf( + "security policy digest %q doesn't match HostData provided at launch %q", + hostData, + report.HostData, + ) + } + return nil +} + +func ExtendPolicyWithNetworkingMounts(sandboxID string, enforcer SecurityPolicyEnforcer, spec *oci.Spec) error { + roSpec := &oci.Spec{ + Root: spec.Root, + } + networkingMounts := specInternal.GenerateWorkloadContainerNetworkMounts(sandboxID, roSpec) + if err := enforcer.ExtendDefaultMounts(networkingMounts); err != nil { + return err + } + return nil +} + +func DefaultCRIMounts() []oci.Mount { + return []oci.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + { + Destination: "/run", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + // cgroup mount is always added by default, regardless if it is present + // in the mount constraints or not. If the user chooses to override it, + // then a corresponding mount constraint should be present. + { + Source: "cgroup", + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"}, + }, + } +} + +// DefaultCRIPrivilegedMounts returns a slice of mounts which are added to the +// linux container spec when a container runs in a privileged mode. +func DefaultCRIPrivilegedMounts() []oci.Mount { + return []oci.Mount{ + { + Source: "cgroup", + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Options: []string{"nosuid", "noexec", "nodev", "relatime", "rw"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "rw"}, + }, + } +} + // SandboxMountsDir returns sandbox mounts directory inside UVM/host. func SandboxMountsDir(sandboxID string) string { return specInternal.SandboxMountsDir((sandboxID)) diff --git a/pkg/securitypolicy/securitypolicy_options.go b/pkg/securitypolicy/securitypolicy_options.go new file mode 100644 index 0000000000..b2b469e0bb --- /dev/null +++ b/pkg/securitypolicy/securitypolicy_options.go @@ -0,0 +1,227 @@ +package securitypolicy + +import ( + "context" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "os" + "path/filepath" + "sync" + "time" + + "github.com/Microsoft/cosesign1go/pkg/cosesign1" + didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/protocol/guestresource" + "github.com/Microsoft/hcsshim/pkg/annotations" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type SecurityOptions struct { + // state required for the security policy enforcement + PolicyEnforcer SecurityPolicyEnforcer + PolicyEnforcerSet bool + UvmReferenceInfo string + policyMutex sync.Mutex + logWriter io.Writer +} + +func NewSecurityOptions(enforcer SecurityPolicyEnforcer, enforcerSet bool, uvmReferenceInfo string, logWriter io.Writer) *SecurityOptions { + return &SecurityOptions{ + PolicyEnforcer: enforcer, + PolicyEnforcerSet: enforcerSet, + UvmReferenceInfo: uvmReferenceInfo, + logWriter: logWriter, + } +} + +// SetConfidentialOptions takes guestresource.ConfidentialOptions +// to set up our internal data structures we use to store and enforce +// security policy. The options can contain security policy enforcer type, +// encoded security policy and signed UVM reference information The security +// policy and uvm reference information can be further presented to workload +// containers for validation and attestation purposes. +func (s *SecurityOptions) SetConfidentialOptions(ctx context.Context, enforcerType string, encodedSecurityPolicy string, encodedUVMReference string) error { + s.policyMutex.Lock() + defer s.policyMutex.Unlock() + + if s.PolicyEnforcerSet { + return errors.New("security policy has already been set") + } + + hostData, err := NewSecurityPolicyDigest(encodedSecurityPolicy) + if err != nil { + return err + } + + if err := validateHostData(hostData[:]); err != nil { + return err + } + + // 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 := CreateSecurityPolicyEnforcer( + enforcerType, + encodedSecurityPolicy, + DefaultCRIMounts(), + DefaultCRIPrivilegedMounts(), + maxErrorMessageLength, + ) + if err != nil { + return fmt.Errorf("error creating security policy enforcer: %w", err) + } + + // 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 = s.PolicyEnforcer.EnforceRuntimeLoggingPolicy(ctx); err == nil { + logrus.SetOutput(s.logWriter) + } else { + logrus.SetOutput(io.Discard) + } + + s.PolicyEnforcer = p + s.PolicyEnforcerSet = true + s.UvmReferenceInfo = encodedUVMReference + + return nil +} + +// Fragment extends current security policy with additional constraints +// from the incoming fragment. Note that it is base64 encoded over the bridge/ +// +// There are three checking steps: +// 1 - Unpack the cose document and check it was actually signed with the cert +// chain inside its header +// 2 - Check that the issuer field did:x509 identifier is for that cert chain +// (ie fingerprint of a non leaf cert and the subject matches the leaf cert) +// 3 - Check that this issuer/feed match the requirement of the user provided +// security policy (done in the regoby LoadFragment) +func (s *SecurityOptions) InjectFragment(ctx context.Context, fragment *guestresource.SecurityPolicyFragment) (err error) { + log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("VerifyAndExtractFragment") + + raw, err := base64.StdEncoding.DecodeString(fragment.Fragment) + if err != nil { + return fmt.Errorf("failed to decode fragment: %w", err) + } + blob := []byte(fragment.Fragment) + // keep a copy of the fragment, so we can manually figure out what went wrong + // will be removed eventually. Give it a unique name to avoid any potential + // race conditions. + sha := sha256.New() + sha.Write(blob) + timestamp := time.Now() + fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli()) + _ = os.WriteFile(filepath.Join(os.TempDir(), fragmentPath), blob, 0644) + + unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw) + if err != nil { + return fmt.Errorf("InjectFragment failed COSE validation: %w", err) + } + + payloadString := string(unpacked.Payload[:]) + issuer := unpacked.Issuer + feed := unpacked.Feed + chainPem := unpacked.ChainPem + + log.G(ctx).WithFields(logrus.Fields{ + "issuer": issuer, // eg the DID:x509:blah.... + "feed": feed, + "cty": unpacked.ContentType, + "chainPem": chainPem, + }).Debugf("unpacked COSE1 cert chain") + + log.G(ctx).WithFields(logrus.Fields{ + "payload": payloadString, + }).Tracef("unpacked COSE1 payload") + + if len(issuer) == 0 || len(feed) == 0 { // must both be present + return fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header") + } + + // Resolve returns a did doc that we don't need + // we only care if there was an error or not + _, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true) + if err != nil { + log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error()) + return fmt.Errorf("failed to resolve DID: %w", err) + } + + // now offer the payload fragment to the policy + err = s.PolicyEnforcer.LoadFragment(ctx, issuer, feed, payloadString) + if err != nil { + return fmt.Errorf("error loading security policy fragment: %w", err) + } + return nil +} + +func writeFileInDir(dir string, filename string, data []byte, perm os.FileMode) error { + st, err := os.Stat(dir) + if err != nil { + return err + } + + if !st.IsDir() { + return fmt.Errorf("not a directory %q", dir) + } + + targetFilename := filepath.Join(dir, filename) + return os.WriteFile(targetFilename, data, perm) +} + +// Write security policy, signed UVM reference and host AMD certificate to +// container's rootfs, so that application and sidecar containers can have +// access to it. The security policy is required by containers which need to +// extract init-time claims found in the security policy. The directory path +// containing the files is exposed via UVM_SECURITY_CONTEXT_DIR env var. +// It may be an error to have a security policy but not expose it to the +// container as in that case it can never be checked as correct by a verifier. +func (s *SecurityOptions) WriteSecurityContextDir(spec *specs.Spec) error { + encodedPolicy := s.PolicyEnforcer.EncodedSecurityPolicy() + hostAMDCert := spec.Annotations[annotations.WCOWHostAMDCertificate] + if len(encodedPolicy) > 0 || len(hostAMDCert) > 0 || len(s.UvmReferenceInfo) > 0 { + // Use os.MkdirTemp to make sure that the directory is unique. + securityContextDir, err := os.MkdirTemp(spec.Root.Path, SecurityContextDirTemplate) + if err != nil { + return fmt.Errorf("failed to create security context directory: %w", err) + } + // Make sure that files inside directory are readable + if err := os.Chmod(securityContextDir, 0755); err != nil { + return fmt.Errorf("failed to chmod security context directory: %w", err) + } + + if len(encodedPolicy) > 0 { + if err := writeFileInDir(securityContextDir, PolicyFilename, []byte(encodedPolicy), 0777); err != nil { + return fmt.Errorf("failed to write security policy: %w", err) + } + } + if len(s.UvmReferenceInfo) > 0 { + if err := writeFileInDir(securityContextDir, ReferenceInfoFilename, []byte(s.UvmReferenceInfo), 0777); err != nil { + return fmt.Errorf("failed to write UVM reference info: %w", err) + } + } + + if len(hostAMDCert) > 0 { + if err := writeFileInDir(securityContextDir, HostAMDCertFilename, []byte(hostAMDCert), 0777); err != nil { + return fmt.Errorf("failed to write host AMD certificate: %w", err) + } + } + + containerCtxDir := fmt.Sprintf("/%s", filepath.Base(securityContextDir)) + secCtxEnv := fmt.Sprintf("UVM_SECURITY_CONTEXT_DIR=%s", containerCtxDir) + spec.Process.Env = append(spec.Process.Env, secCtxEnv) + + } + return nil +} diff --git a/pkg/securitypolicy/securitypolicy_windows.go b/pkg/securitypolicy/securitypolicy_windows.go index 6f873fef26..1582a756af 100644 --- a/pkg/securitypolicy/securitypolicy_windows.go +++ b/pkg/securitypolicy/securitypolicy_windows.go @@ -3,11 +3,29 @@ package securitypolicy -import oci "github.com/opencontainers/runtime-spec/specs-go" +import ( + oci "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) //nolint:unused const osType = "windows" +// validateHostData fetches SNP report (if applicable) and validates `hostData` against +// HostData set at UVM launch. +func validateHostData(hostData []byte) error { + if err := GetPspDriverError(); err != nil { + // For this case gcs-sidecar will keep initial deny policy. + return errors.Wrapf(err, "an error occurred while using PSP driver") + } + + if err := ValidateHostDataPSP(hostData[:]); err != nil { + // For this case gcs-sidecar will keep initial deny policy. + return err + } + return nil +} + // SandboxMountsDir returns sandbox mounts directory inside UVM/host. func SandboxMountsDir(sandboxID string) string { return "" @@ -21,3 +39,14 @@ func HugePagesMountsDir(sandboxID string) string { func GetAllUserInfo(process *oci.Process, rootPath string) (IDName, []IDName, string, error) { return IDName{}, []IDName{}, "", nil } + +// DefaultCRIMounts returns default mounts added to windows spec by containerD. +func DefaultCRIMounts() []oci.Mount { + return []oci.Mount{} +} + +// DefaultCRIPrivilegedMounts returns a slice of mounts which are added to the +// windows container spec when a container runs in a privileged mode. +func DefaultCRIPrivilegedMounts() []oci.Mount { + return []oci.Mount{} +} diff --git a/pkg/securitypolicy/securitypolicyenforcer.go b/pkg/securitypolicy/securitypolicyenforcer.go index 760f7a4996..59c3780638 100644 --- a/pkg/securitypolicy/securitypolicyenforcer.go +++ b/pkg/securitypolicy/securitypolicyenforcer.go @@ -30,7 +30,6 @@ type CreateContainerOptions struct { Capabilities *oci.LinuxCapabilities SeccompProfileSHA256 string } - type SignalContainerOptions struct { IsInitProcess bool // One of these will be set depending on platform @@ -121,7 +120,7 @@ type SecurityPolicyEnforcer interface { EnforceGetPropertiesPolicy(ctx context.Context) error EnforceDumpStacksPolicy(ctx context.Context) error EnforceRuntimeLoggingPolicy(ctx context.Context) (err error) - LoadFragment(ctx context.Context, issuer string, feed string, code string) error + LoadFragment(ctx context.Context, issuer string, feed string, rego string) error EnforceScratchMountPolicy(ctx context.Context, scratchPath string, encrypted bool) (err error) EnforceScratchUnmountPolicy(ctx context.Context, scratchPath string) (err error) GetUserInfo(spec *oci.Process, rootPath string) (IDName, []IDName, string, error) diff --git a/pkg/securitypolicy/securitypolicyenforcer_rego.go b/pkg/securitypolicy/securitypolicyenforcer_rego.go index f12d8d1fb1..bb2fc27530 100644 --- a/pkg/securitypolicy/securitypolicyenforcer_rego.go +++ b/pkg/securitypolicy/securitypolicyenforcer_rego.go @@ -59,17 +59,6 @@ type regoEnforcer struct { var _ SecurityPolicyEnforcer = (*regoEnforcer)(nil) -//nolint:unused -/*func (sp SecurityPolicy) toInternal() (*securityPolicyInternal, error) { - policy := new(securityPolicyInternal) - var err error - if policy.Containers, err = sp.Containers.toInternal(); err != nil { - return nil, err - } - - return policy, nil -}*/ - func toStringSet(items []string) stringSet { s := make(stringSet) for _, item := range items { diff --git a/test/gcs/main_test.go b/test/gcs/main_test.go index f4b32b34c8..ce399e0767 100644 --- a/test/gcs/main_test.go +++ b/test/gcs/main_test.go @@ -22,7 +22,6 @@ import ( "github.com/Microsoft/hcsshim/internal/guest/transport" "github.com/Microsoft/hcsshim/internal/guestpath" "github.com/Microsoft/hcsshim/internal/oc" - "github.com/Microsoft/hcsshim/internal/protocol/guestresource" "github.com/Microsoft/hcsshim/pkg/securitypolicy" "github.com/Microsoft/hcsshim/test/internal/util" @@ -167,9 +166,9 @@ func getHost(_ context.Context, tb testing.TB, rt runtime.Runtime) *hcsv2.Host { func getHostErr(rt runtime.Runtime, tp transport.Transport) (*hcsv2.Host, error) { h := hcsv2.NewHost(rt, tp, &securitypolicy.OpenDoorSecurityPolicyEnforcer{}, os.Stdout) - if err := h.SetConfidentialUVMOptions( + if err := h.SecurityOptions().SetConfidentialOptions( context.Background(), - &guestresource.LCOWConfidentialOptions{}, + "", "", "", ); err != nil { return nil, fmt.Errorf("could not set host security policy: %w", err) }