diff --git a/internal/gcs-sidecar/handlers.go b/internal/gcs-sidecar/handlers.go index a76680fe83..a51c3fd8f5 100644 --- a/internal/gcs-sidecar/handlers.go +++ b/internal/gcs-sidecar/handlers.go @@ -752,9 +752,14 @@ func (b *Bridge) modifySettings(req *request) (err error) { log.G(ctx).Debugf("block CIM layer digest %s, path: %s\n", layerHashes[i], physicalDevPath) } + // Top layer is the merged layer that will also be verified hashesToVerify := layerHashes + mountedCim := []string{layerHashes[0]} + if len(layerHashes) > 1 { + hashesToVerify = layerHashes[1:] + } - err := b.hostState.securityOptions.PolicyEnforcer.EnforceVerifiedCIMsPolicy(req.ctx, containerID, hashesToVerify) + err := b.hostState.securityOptions.PolicyEnforcer.EnforceVerifiedCIMsPolicy(req.ctx, containerID, hashesToVerify, mountedCim) if err != nil { return errors.Wrap(err, "CIM mount is denied by policy") } @@ -763,7 +768,7 @@ func (b *Bridge) modifySettings(req *request) (err error) { volGUID := wcowBlockCimMounts.VolumeGUID // Cache hashes along with volGUID - b.hostState.blockCIMVolumeHashes[volGUID] = hashesToVerify + b.hostState.blockCIMVolumeHashes[volGUID] = layerHashes // Store the containerID (associated with volGUID) to mark that hashes are verified for this container if _, ok := b.hostState.blockCIMVolumeContainers[volGUID]; !ok { @@ -886,10 +891,15 @@ func (b *Bridge) modifySettings(req *request) (err error) { if _, seen := containers[containerID]; !seen { // This is a container with similar layers as an existing container, hence already mounted. // Call EnforceVerifiedCIMsPolicy on this new container. - log.G(ctx).Tracef("Verified CIM hashes for reused mount volume %s (container %s)", volGUID.String(), containerID) - if err := b.hostState.securityOptions.PolicyEnforcer.EnforceVerifiedCIMsPolicy(ctx, containerID, hashes); err != nil { + hashesToVerify := hashes + mountedCim := []string{hashes[0]} + if len(hashes) > 1 { + hashesToVerify = hashes[1:] + } + if err := b.hostState.securityOptions.PolicyEnforcer.EnforceVerifiedCIMsPolicy(ctx, containerID, hashesToVerify, mountedCim); err != nil { return fmt.Errorf("CIM mount is denied by policy for this container: %w", err) } + log.G(ctx).Tracef("Verified CIM hashes for reused mount volume %s (container %s)", volGUID.String(), containerID) containers[containerID] = struct{}{} } } diff --git a/pkg/securitypolicy/framework.rego b/pkg/securitypolicy/framework.rego index de534eb863..daa0fe864e 100644 --- a/pkg/securitypolicy/framework.rego +++ b/pkg/securitypolicy/framework.rego @@ -174,6 +174,7 @@ mount_cims := {"metadata": [addMatches], "allowed": true} { containers := [container | container := candidate_containers[_] layerHashes_ok(container.layers) + input.mountedCim == container.mounted_cim ] count(containers) > 0 diff --git a/pkg/securitypolicy/rego_utils_test.go b/pkg/securitypolicy/rego_utils_test.go index 5ac12a5a0a..2967b5a6b7 100644 --- a/pkg/securitypolicy/rego_utils_test.go +++ b/pkg/securitypolicy/rego_utils_test.go @@ -1857,6 +1857,7 @@ func (c *securityPolicyWindowsContainer) toWindowsContainer() *WindowsContainer Command: CommandArgs(stringArrayToStringMap(c.Command)), EnvRules: envRuleArrayToEnvRules(c.EnvRules), Layers: Layers(stringArrayToStringMap(c.Layers)), + MountedCim: c.MountedCim, WorkingDir: c.WorkingDir, ExecProcesses: execProcesses, Signals: c.Signals, @@ -2163,13 +2164,12 @@ func mountImageForWindowsContainer(policy *regoEnforcer, container *securityPoli } // Mount the CIMFS for the Windows container - err := policy.EnforceVerifiedCIMsPolicy(ctx, containerID, layerHashes) + // layerHashes are the individual layer hashes, mountedCim is the merged CIM from the policy + err := policy.EnforceVerifiedCIMsPolicy(ctx, containerID, layerHashes, container.MountedCim) if err != nil { return "", fmt.Errorf("error mounting CIMFS: %w", err) } - //fmt.Printf("CIMFS mounted successfully for container %s with layers %v\n", containerID, layerHashes) - return containerID, nil } @@ -2306,6 +2306,11 @@ func generateConstraintsWindowsContainer(r *rand.Rand, minNumberOfLayers, maxNum for i := 0; i < numLayers; i++ { c.Layers = append(c.Layers, generateRootHash(r)) } + // For Windows, mounted_cim is the top/merged layer hash + // After reversal in the runtime, this becomes layerHashes[0] + if len(c.Layers) > 0 { + c.MountedCim = []string{c.Layers[len(c.Layers)-1]} + } c.ExecProcesses = generateWindowsExecProcesses(r) c.Signals = generateWindowsSignals(r) c.AllowStdioAccess = randBool(r) diff --git a/pkg/securitypolicy/regopolicy_windows_test.go b/pkg/securitypolicy/regopolicy_windows_test.go index 512e4e7ff9..33b49a64f8 100644 --- a/pkg/securitypolicy/regopolicy_windows_test.go +++ b/pkg/securitypolicy/regopolicy_windows_test.go @@ -325,9 +325,12 @@ func Test_Rego_EnforceVerifiedCIMSPolicy_Multiple_Instances_Same_Container(t *te for i := 1; i <= containersToCreate; i++ { arg := "command " + strconv.Itoa(i) + // layers = individual layer hashes, mounted_cim = merged CIM hash + // The runtime sends hashesToVerify=layers (reversed) and mountedCim=merged c := &securityPolicyWindowsContainer{ - Command: []string{arg}, - Layers: []string{"1", "2"}, + Command: []string{arg}, + Layers: []string{"layer1", "layer2"}, + MountedCim: []string{"merged_hash"}, } constraints.containers = append(constraints.containers, c) @@ -347,8 +350,10 @@ func Test_Rego_EnforceVerifiedCIMSPolicy_Multiple_Instances_Same_Container(t *te layerHashes[len(container.Layers)-1-i] = layer } + // The runtime sends individual layers as hashesToVerify + // and the merged CIM hash separately id := testDataGenerator.uniqueContainerID() - err = policy.EnforceVerifiedCIMsPolicy(constraints.ctx, id, layerHashes) + err = policy.EnforceVerifiedCIMsPolicy(constraints.ctx, id, layerHashes, container.MountedCim) if err != nil { t.Fatalf("failed with %d containers", containersToCreate) } diff --git a/pkg/securitypolicy/securitypolicy.go b/pkg/securitypolicy/securitypolicy.go index c294da2c11..5b05160492 100644 --- a/pkg/securitypolicy/securitypolicy.go +++ b/pkg/securitypolicy/securitypolicy.go @@ -304,6 +304,7 @@ type WindowsContainer struct { Command CommandArgs `json:"command"` EnvRules EnvRules `json:"env_rules"` Layers Layers `json:"layers"` + MountedCim []string `json:"mounted_cim"` WorkingDir string `json:"working_dir"` ExecProcesses []WindowsExecProcessConfig `json:"-"` Signals []guestrequest.SignalValueWCOW `json:"-"` diff --git a/pkg/securitypolicy/securitypolicy_internal.go b/pkg/securitypolicy/securitypolicy_internal.go index 0057de5077..c736fb58ed 100644 --- a/pkg/securitypolicy/securitypolicy_internal.go +++ b/pkg/securitypolicy/securitypolicy_internal.go @@ -184,6 +184,8 @@ type securityPolicyWindowsContainer struct { // order that the layers are overlayed is important and needs to be enforced // as part of policy. Layers []string `json:"layers"` + // The merged CIM hash for Windows containers + MountedCim []string `json:"mounted_cim"` // WorkingDir is a path to container's working directory, which all the processes // will default to. WorkingDir string `json:"working_dir"` diff --git a/pkg/securitypolicy/securitypolicy_marshal.go b/pkg/securitypolicy/securitypolicy_marshal.go index 47836bd5ab..665dc9e4f0 100644 --- a/pkg/securitypolicy/securitypolicy_marshal.go +++ b/pkg/securitypolicy/securitypolicy_marshal.go @@ -392,6 +392,10 @@ func writeLayers(builder *strings.Builder, layers []string, indent string) { writeLine(builder, `%s"layers": %s,`, indent, (stringArray(layers)).marshalRego()) } +func writeMountedCim(builder *strings.Builder, mountedCim []string, indent string) { + writeLine(builder, `%s"mounted_cim": %s,`, indent, (stringArray(mountedCim)).marshalRego()) +} + func writeCapabilities(builder *strings.Builder, capabilities *capabilitiesInternal, indent string) { if capabilities != nil { writeLine(builder, `%s"capabilities": {`, indent) @@ -458,6 +462,7 @@ func writeWindowsContainer(builder *strings.Builder, container *securityPolicyWi writeCommand(builder, container.Command, indent+indentUsing) writeEnvRules(builder, container.EnvRules, indent+indentUsing) writeLayers(builder, container.Layers, indent+indentUsing) + writeMountedCim(builder, container.MountedCim, indent+indentUsing) writeWindowsExecProcesses(builder, container.ExecProcesses, indent+indentUsing) writeWindowsSignals(builder, container.Signals, indent+indentUsing) writeWindowsUser(builder, container.User, indent+indentUsing) diff --git a/pkg/securitypolicy/securitypolicyenforcer.go b/pkg/securitypolicy/securitypolicyenforcer.go index 6cae97118d..2a4edefce1 100644 --- a/pkg/securitypolicy/securitypolicyenforcer.go +++ b/pkg/securitypolicy/securitypolicyenforcer.go @@ -126,7 +126,7 @@ type SecurityPolicyEnforcer interface { 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) - EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string) (err error) + EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string, mountedCim []string) (err error) EnforceRegistryChangesPolicy(ctx context.Context, containerID string, registryValues interface{}) error } @@ -316,7 +316,7 @@ func (OpenDoorSecurityPolicyEnforcer) GetUserInfo(spec *oci.Process, rootPath st return IDName{}, nil, "", nil } -func (OpenDoorSecurityPolicyEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string) error { +func (OpenDoorSecurityPolicyEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string, mountedCim []string) error { return nil } @@ -445,7 +445,7 @@ func (ClosedDoorSecurityPolicyEnforcer) GetUserInfo(spec *oci.Process, rootPath return IDName{}, nil, "", nil } -func (ClosedDoorSecurityPolicyEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string) error { +func (ClosedDoorSecurityPolicyEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string, mountedCim []string) error { return nil } diff --git a/pkg/securitypolicy/securitypolicyenforcer_rego.go b/pkg/securitypolicy/securitypolicyenforcer_rego.go index 1da5d78a0e..96c5613dd6 100644 --- a/pkg/securitypolicy/securitypolicyenforcer_rego.go +++ b/pkg/securitypolicy/securitypolicyenforcer_rego.go @@ -1157,11 +1157,12 @@ func (policy *regoEnforcer) EnforceScratchUnmountPolicy(ctx context.Context, scr return nil } -func (policy *regoEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string) error { +func (policy *regoEnforcer) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string, mountedCim []string) error { log.G(ctx).Tracef("Enforcing verified cims in securitypolicy pkg %+v", layerHashes) input := inputData{ "containerID": containerID, "layerHashes": layerHashes, + "mountedCim": mountedCim, } _, err := policy.enforce(ctx, "mount_cims", input)