From acdad4745df19bb0378c69a3dbcd94327b7ac7a7 Mon Sep 17 00:00:00 2001 From: Mahati Chamarthy Date: Thu, 7 May 2026 22:06:48 +0100 Subject: [PATCH 1/2] CWCOW: Persist environment variable Capture and apply envToKeep from policy enforcement in createContainer, external exec, and in-container exec. Previously the filtered env list was discarded. Add ociEnvToProcessParamEnv and rewriteExecRequest helpers with tests. Signed-off-by: Mahati Chamarthy --- internal/gcs-sidecar/handlers.go | 58 ++++++++++++++++++- internal/gcs-sidecar/handlers_test.go | 80 +++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/internal/gcs-sidecar/handlers.go b/internal/gcs-sidecar/handlers.go index a51c3fd8f5..81e5f9e03f 100644 --- a/internal/gcs-sidecar/handlers.go +++ b/internal/gcs-sidecar/handlers.go @@ -124,12 +124,17 @@ func (b *Bridge) createContainer(req *request) (err error) { user := securitypolicy.IDName{ Name: spec.Process.User.Username, } - _, _, _, err := b.hostState.securityOptions.PolicyEnforcer.EnforceCreateContainerPolicyV2(req.ctx, containerID, spec.Process.Args, spec.Process.Env, spec.Process.Cwd, spec.Mounts, user, nil) + envToKeep, _, allowStdio, 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) } + if envToKeep != nil { + spec.Process.Env = []string(envToKeep) + } + _ = allowStdio // TODO: enforce stdio access for Windows containers + commandLine := len(spec.Process.Args) > 0 c := &Container{ id: containerID, @@ -215,6 +220,39 @@ func processParamEnvToOCIEnv(environment map[string]string) []string { return environmentList } +// ociEnvToProcessParamEnv is the inverse of processParamEnvToOCIEnv. It converts +// an OCI-style env list (["KEY=VALUE", ...]) back to a ProcessParameters +// Environment map. +func ociEnvToProcessParamEnv(envs []string) map[string]string { + paramEnv := make(map[string]string, len(envs)) + for _, env := range envs { + parts := strings.SplitN(env, "=", 2) + if len(parts) == 2 { + paramEnv[parts[0]] = parts[1] + } + } + return paramEnv +} + +// rewriteExecRequest re-marshals an execute process request with updated +// ProcessParameters (e.g., after env filtering by policy). +func rewriteExecRequest(req *request, r prot.ContainerExecuteProcess, params hcsschema.ProcessParameters) (*request, error) { + r.Settings.ProcessParameters.Value = ¶ms + + buf, err := json.Marshal(r) + if err != nil { + return nil, fmt.Errorf("failed to marshal updated exec request: %w", err) + } + + newReq := &request{ + ctx: req.ctx, + header: req.header, + message: buf, + } + newReq.header.Size = uint32(len(buf)) + prot.HdrSize + return newReq, nil +} + func (b *Bridge) startContainer(req *request) (err error) { _, span := oc.StartSpan(req.ctx, "sidecar::startContainer") defer span.End() @@ -283,7 +321,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.securityOptions.PolicyEnforcer.EnforceExecExternalProcessPolicy( + envToKeep, _, err := b.hostState.securityOptions.PolicyEnforcer.EnforceExecExternalProcessPolicy( req.ctx, commandLine, processParamEnvToOCIEnv(processParams.Environment), @@ -292,6 +330,13 @@ func (b *Bridge) executeProcess(req *request) (err error) { if err != nil { return errors.Wrapf(err, "exec is denied due to policy") } + if envToKeep != nil { + processParams.Environment = ociEnvToProcessParamEnv(envToKeep) + req, err = rewriteExecRequest(req, r, processParams) + if err != nil { + return fmt.Errorf("failed to rewrite exec request with filtered env: %w", err) + } + } b.forwardRequestToGcs(req) } else { // fetch the container command line @@ -315,7 +360,7 @@ func (b *Bridge) executeProcess(req *request) (err error) { Name: processParams.User, } log.G(req.ctx).Tracef("Enforcing policy on exec in container") - _, _, _, err = b.hostState.securityOptions.PolicyEnforcer. + envToKeep, _, _, err := b.hostState.securityOptions.PolicyEnforcer. EnforceExecInContainerPolicyV2( req.ctx, containerID, @@ -328,6 +373,13 @@ func (b *Bridge) executeProcess(req *request) (err error) { if err != nil { return errors.Wrapf(err, "exec in container denied due to policy") } + if envToKeep != nil { + processParams.Environment = ociEnvToProcessParamEnv(envToKeep) + req, err = rewriteExecRequest(req, r, processParams) + if err != nil { + return fmt.Errorf("failed to rewrite exec request with filtered env: %w", err) + } + } } headerID := req.header.ID diff --git a/internal/gcs-sidecar/handlers_test.go b/internal/gcs-sidecar/handlers_test.go index 6de3a0a605..575eae2112 100644 --- a/internal/gcs-sidecar/handlers_test.go +++ b/internal/gcs-sidecar/handlers_test.go @@ -327,3 +327,83 @@ func TestModifySettings_PolicyFragment_TypeAssertionFailure(t *testing.T) { t.Fatal("expected error for empty fragment, got nil") } } + +// Tests for environment variable filtering helpers (envlist persistence) + +func TestOciEnvToProcessParamEnv_Basic(t *testing.T) { + input := []string{"FOO=bar", "PATH=/usr/bin", "EMPTY="} + result := ociEnvToProcessParamEnv(input) + + if result["FOO"] != "bar" { + t.Errorf("FOO = %q, want %q", result["FOO"], "bar") + } + if result["PATH"] != "/usr/bin" { + t.Errorf("PATH = %q, want %q", result["PATH"], "/usr/bin") + } + if result["EMPTY"] != "" { + t.Errorf("EMPTY = %q, want %q", result["EMPTY"], "") + } + if len(result) != 3 { + t.Errorf("len = %d, want 3", len(result)) + } +} + +func TestOciEnvToProcessParamEnv_ValueWithEquals(t *testing.T) { + input := []string{"CONN=host=db;port=5432"} + result := ociEnvToProcessParamEnv(input) + + if result["CONN"] != "host=db;port=5432" { + t.Errorf("CONN = %q, want %q", result["CONN"], "host=db;port=5432") + } +} + +func TestOciEnvToProcessParamEnv_MalformedSkipped(t *testing.T) { + input := []string{"GOOD=value", "NOEQUALS", "ALSO_GOOD=yes"} + result := ociEnvToProcessParamEnv(input) + + if len(result) != 2 { + t.Errorf("len = %d, want 2 (malformed entry should be skipped)", len(result)) + } + if result["GOOD"] != "value" { + t.Errorf("GOOD = %q, want %q", result["GOOD"], "value") + } + if result["ALSO_GOOD"] != "yes" { + t.Errorf("ALSO_GOOD = %q, want %q", result["ALSO_GOOD"], "yes") + } +} + +func TestOciEnvToProcessParamEnv_Empty(t *testing.T) { + result := ociEnvToProcessParamEnv([]string{}) + if len(result) != 0 { + t.Errorf("len = %d, want 0", len(result)) + } +} + +func TestOciEnvToProcessParamEnv_Nil(t *testing.T) { + result := ociEnvToProcessParamEnv(nil) + if result == nil { + t.Error("result should be non-nil empty map, got nil") + } + if len(result) != 0 { + t.Errorf("len = %d, want 0", len(result)) + } +} + +func TestProcessParamEnvToOCIEnv_Roundtrip(t *testing.T) { + original := map[string]string{ + "FOO": "bar", + "PATH": "/usr/bin", + } + + ociEnv := processParamEnvToOCIEnv(original) + roundtripped := ociEnvToProcessParamEnv(ociEnv) + + if len(roundtripped) != len(original) { + t.Fatalf("roundtrip len = %d, want %d", len(roundtripped), len(original)) + } + for k, v := range original { + if roundtripped[k] != v { + t.Errorf("roundtrip[%q] = %q, want %q", k, roundtripped[k], v) + } + } +} From d83b7529d3ff779f1e8e41cf5334ef065e2f2a5a Mon Sep 17 00:00:00 2001 From: Mahati Chamarthy Date: Thu, 7 May 2026 22:07:09 +0100 Subject: [PATCH 2/2] CWCOW: Enforce MappedDirectory inside gcs-sidecar Add EnforceMappedDirectoryMountPolicy/UnmountPolicy to enforce VSMB directory shares for confidential Windows containers. Writable mapped directories are denied; duplicates at the same container path are prevented. Also add path pattern validation for MappedVirtualDisk and MappedVirtualDiskForContainerScratch to ensure SCSI mounts only target c:\mounts\scsi\m. Signed-off-by: Mahati Chamarthy --- internal/gcs-sidecar/handlers.go | 29 +++ pkg/securitypolicy/api.rego | 2 + pkg/securitypolicy/framework.rego | 44 +++++ pkg/securitypolicy/open_door.rego | 2 + pkg/securitypolicy/policy.rego | 2 + pkg/securitypolicy/regopolicy_windows_test.go | 180 ++++++++++++++++++ pkg/securitypolicy/securitypolicyenforcer.go | 18 ++ .../securitypolicyenforcer_rego.go | 17 ++ 8 files changed, 294 insertions(+) diff --git a/internal/gcs-sidecar/handlers.go b/internal/gcs-sidecar/handlers.go index 81e5f9e03f..35ce682b9b 100644 --- a/internal/gcs-sidecar/handlers.go +++ b/internal/gcs-sidecar/handlers.go @@ -9,6 +9,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" "time" @@ -701,6 +702,13 @@ func (b *Bridge) modifySettings(req *request) (err error) { case guestresource.ResourceTypeMappedVirtualDisk: wcowMappedVirtualDisk := modifyGuestSettingsRequest.Settings.(*guestresource.WCOWMappedVirtualDisk) log.G(ctx).Tracef("wcowMappedVirtualDisk { %v}", wcowMappedVirtualDisk) + if wcowMappedVirtualDisk.ContainerPath != "" { + matched, merr := regexp.MatchString(`(?i)^[Cc]:\\mounts\\scsi\\m[0-9]+$`, wcowMappedVirtualDisk.ContainerPath) + if merr != nil || !matched { + return fmt.Errorf("virtual disk mount path %q does not match expected pattern c:\\mounts\\scsi\\m", + wcowMappedVirtualDisk.ContainerPath) + } + } case guestresource.ResourceTypeHvSocket: hvSocketAddress := modifyGuestSettingsRequest.Settings.(*hcsschema.HvSocketAddress) @@ -709,6 +717,18 @@ func (b *Bridge) modifySettings(req *request) (err error) { case guestresource.ResourceTypeMappedDirectory: settings := modifyGuestSettingsRequest.Settings.(*hcsschema.MappedDirectory) log.G(ctx).Tracef("hcsschema.MappedDirectory { %v }", settings) + switch modifyGuestSettingsRequest.RequestType { + case guestrequest.RequestTypeAdd: + if err := b.hostState.securityOptions.PolicyEnforcer.EnforceMappedDirectoryMountPolicy( + ctx, settings.ContainerPath, settings.ReadOnly); err != nil { + return fmt.Errorf("mapped directory mount is denied by policy: %w", err) + } + case guestrequest.RequestTypeRemove: + if err := b.hostState.securityOptions.PolicyEnforcer.EnforceMappedDirectoryUnmountPolicy( + ctx, settings.ContainerPath); err != nil { + return fmt.Errorf("mapped directory unmount is denied by policy: %w", err) + } + } case guestresource.ResourceTypeSecurityPolicy: securityPolicyRequest := modifyGuestSettingsRequest.Settings.(*guestresource.ConfidentialOptions) @@ -867,6 +887,15 @@ func (b *Bridge) modifySettings(req *request) (err error) { wcowMappedVirtualDisk := modifyGuestSettingsRequest.Settings.(*guestresource.WCOWMappedVirtualDisk) log.G(ctx).Tracef("ResourceTypeMappedVirtualDiskForContainerScratch: { %v }", wcowMappedVirtualDisk) + // Validate the scratch disk mount path matches the expected pattern + if wcowMappedVirtualDisk.ContainerPath != "" { + matched, merr := regexp.MatchString(`(?i)^[Cc]:\\mounts\\scsi\\m[0-9]+$`, wcowMappedVirtualDisk.ContainerPath) + if merr != nil || !matched { + return fmt.Errorf("scratch disk mount path %q does not match expected pattern c:\\mounts\\scsi\\m", + wcowMappedVirtualDisk.ContainerPath) + } + } + // This will return the volume path of the mounted scratch. // Scratch disk should be >= 30 GB for refs formatter to work. // fsFormatter understands only virtualDevObjectPathFormat. Therefore fetch the diff --git a/pkg/securitypolicy/api.rego b/pkg/securitypolicy/api.rego index 88c3d64d14..3b89a6d139 100644 --- a/pkg/securitypolicy/api.rego +++ b/pkg/securitypolicy/api.rego @@ -24,4 +24,6 @@ enforcement_points := { "load_fragment": {"introducedVersion": "0.9.0", "default_results": {"allowed": false, "add_module": false}, "use_framework": false}, "scratch_mount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}, "use_framework": false}, "scratch_unmount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}, "use_framework": false}, + "mapped_directory_mount": {"introducedVersion": "0.11.0", "default_results": {"allowed": true}, "use_framework": false}, + "mapped_directory_unmount": {"introducedVersion": "0.11.0", "default_results": {"allowed": true}, "use_framework": false}, } diff --git a/pkg/securitypolicy/framework.rego b/pkg/securitypolicy/framework.rego index daa0fe864e..c919169f7b 100644 --- a/pkg/securitypolicy/framework.rego +++ b/pkg/securitypolicy/framework.rego @@ -1299,6 +1299,35 @@ scratch_unmount := {"metadata": [remove_scratch_mount], "allowed": true} { } } +# Mapped directory (VSMB share) validation for Windows containers +default mapped_directory_mount := {"allowed": false} + +mapped_directory_mounted(target) { + data.metadata.mapped_directories[target] +} + +mapped_directory_mount := {"metadata": [add_mapped_dir], "allowed": true} { + not mapped_directory_mounted(input.containerPath) + input.readOnly + add_mapped_dir := { + "name": "mapped_directories", + "action": "add", + "key": input.containerPath, + "value": {"readOnly": input.readOnly}, + } +} + +default mapped_directory_unmount := {"allowed": false} + +mapped_directory_unmount := {"metadata": [remove_mapped_dir], "allowed": true} { + mapped_directory_mounted(input.unmountTarget) + remove_mapped_dir := { + "name": "mapped_directories", + "action": "remove", + "key": input.unmountTarget, + } +} + # Registry changes validation default registry_changes := {"allowed": false} @@ -1827,6 +1856,21 @@ errors["no scratch at path to unmount"] { not scratch_mounted(input.unmountTarget) } +errors["mapped directory already mounted at path"] { + input.rule == "mapped_directory_mount" + mapped_directory_mounted(input.containerPath) +} + +errors["writable mapped directory not allowed"] { + input.rule == "mapped_directory_mount" + not input.readOnly +} + +errors["no mapped directory at path to unmount"] { + input.rule == "mapped_directory_unmount" + not mapped_directory_mounted(input.unmountTarget) +} + errors[framework_version_error] { policy_framework_version == null framework_version_error := concat(" ", ["framework_version is missing. Current version:", version]) diff --git a/pkg/securitypolicy/open_door.rego b/pkg/securitypolicy/open_door.rego index 02da3fa9b6..44e89499e9 100644 --- a/pkg/securitypolicy/open_door.rego +++ b/pkg/securitypolicy/open_door.rego @@ -23,3 +23,5 @@ runtime_logging := {"allowed": true} load_fragment := {"allowed": true} scratch_mount := {"allowed": true} scratch_unmount := {"allowed": true} +mapped_directory_mount := {"allowed": true} +mapped_directory_unmount := {"allowed": true} diff --git a/pkg/securitypolicy/policy.rego b/pkg/securitypolicy/policy.rego index 195d462931..f8336280b5 100644 --- a/pkg/securitypolicy/policy.rego +++ b/pkg/securitypolicy/policy.rego @@ -26,4 +26,6 @@ runtime_logging := data.framework.runtime_logging load_fragment := data.framework.load_fragment scratch_mount := data.framework.scratch_mount scratch_unmount := data.framework.scratch_unmount +mapped_directory_mount := data.framework.mapped_directory_mount +mapped_directory_unmount := data.framework.mapped_directory_unmount reason := data.framework.reason diff --git a/pkg/securitypolicy/regopolicy_windows_test.go b/pkg/securitypolicy/regopolicy_windows_test.go index 33b49a64f8..aa7372d75f 100644 --- a/pkg/securitypolicy/regopolicy_windows_test.go +++ b/pkg/securitypolicy/regopolicy_windows_test.go @@ -1514,3 +1514,183 @@ func substituteUVMPath(sandboxID string, m mountInternal) mountInternal { _ = sandboxID return m } + +// Tests for MappedDirectory enforcement + +func Test_Rego_EnforceMappedDirectoryMountPolicy_ReadOnly_Allowed_Windows(t *testing.T) { + policy, err := newRegoPolicy( + openDoorRego, + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Fatalf("failed to create policy: %v", err) + } + + ctx := context.Background() + err = policy.EnforceMappedDirectoryMountPolicy(ctx, `C:\data`, true) + if err != nil { + t.Errorf("expected readonly mapped directory to be allowed: %v", err) + } +} + +func Test_Rego_EnforceMappedDirectoryMountPolicy_Writable_Denied_Windows(t *testing.T) { + f := func(p *generatedWindowsConstraints) bool { + securityPolicy := p.toPolicy() + policy, err := newRegoPolicy( + securityPolicy.marshalWindowsRego(), + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Errorf("failed to create policy: %v", err) + return false + } + + ctx := context.Background() + err = policy.EnforceMappedDirectoryMountPolicy(ctx, `C:\data`, false) + if err == nil { + t.Errorf("expected writable mapped directory to be denied") + return false + } + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 5, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_EnforceMappedDirectoryMountPolicy_Writable_Denied_Windows: %v", err) + } +} + +func Test_Rego_EnforceMappedDirectoryMountPolicy_Duplicate_Denied_Windows(t *testing.T) { + f := func(p *generatedWindowsConstraints) bool { + securityPolicy := p.toPolicy() + policy, err := newRegoPolicy( + securityPolicy.marshalWindowsRego(), + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Errorf("failed to create policy: %v", err) + return false + } + + ctx := context.Background() + containerPath := `C:\testmount` + + // First mount should succeed + err = policy.EnforceMappedDirectoryMountPolicy(ctx, containerPath, true) + if err != nil { + t.Errorf("first mount should succeed: %v", err) + return false + } + + // Second mount at same path should fail + err = policy.EnforceMappedDirectoryMountPolicy(ctx, containerPath, true) + if err == nil { + t.Errorf("duplicate mount at same path should be denied") + return false + } + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 5, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_EnforceMappedDirectoryMountPolicy_Duplicate_Denied_Windows: %v", err) + } +} + +func Test_Rego_EnforceMappedDirectoryUnmountPolicy_Windows(t *testing.T) { + f := func(p *generatedWindowsConstraints) bool { + securityPolicy := p.toPolicy() + policy, err := newRegoPolicy( + securityPolicy.marshalWindowsRego(), + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Errorf("failed to create policy: %v", err) + return false + } + + ctx := context.Background() + containerPath := `C:\unmounttest` + + // Mount first + err = policy.EnforceMappedDirectoryMountPolicy(ctx, containerPath, true) + if err != nil { + t.Errorf("mount should succeed: %v", err) + return false + } + + // Unmount should succeed + err = policy.EnforceMappedDirectoryUnmountPolicy(ctx, containerPath) + if err != nil { + t.Errorf("unmount should succeed: %v", err) + return false + } + + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 5, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_EnforceMappedDirectoryUnmountPolicy_Windows: %v", err) + } +} + +func Test_Rego_EnforceMappedDirectoryUnmountPolicy_NotMounted_Denied_Windows(t *testing.T) { + f := func(p *generatedWindowsConstraints) bool { + securityPolicy := p.toPolicy() + policy, err := newRegoPolicy( + securityPolicy.marshalWindowsRego(), + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Errorf("failed to create policy: %v", err) + return false + } + + ctx := context.Background() + // Unmount without mounting should fail + err = policy.EnforceMappedDirectoryUnmountPolicy(ctx, `C:\notmounted`) + if err == nil { + t.Errorf("unmount of non-mounted path should be denied") + return false + } + + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 5, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_EnforceMappedDirectoryUnmountPolicy_NotMounted_Denied_Windows: %v", err) + } +} + +func Test_Rego_EnforceMappedDirectoryMountPolicy_OpenDoor_AllowsAll_Windows(t *testing.T) { + policy, err := newRegoPolicy( + openDoorRego, + []oci.Mount{}, + []oci.Mount{}, + testOSType, + ) + if err != nil { + t.Fatalf("failed to create policy: %v", err) + } + + ctx := context.Background() + + // Open door should allow both readonly and writable + err = policy.EnforceMappedDirectoryMountPolicy(ctx, `C:\readonly`, true) + if err != nil { + t.Errorf("open door should allow readonly mount: %v", err) + } + + err = policy.EnforceMappedDirectoryMountPolicy(ctx, `C:\writable`, false) + if err != nil { + t.Errorf("open door should allow writable mount: %v", err) + } +} diff --git a/pkg/securitypolicy/securitypolicyenforcer.go b/pkg/securitypolicy/securitypolicyenforcer.go index 2a4edefce1..5302dafdec 100644 --- a/pkg/securitypolicy/securitypolicyenforcer.go +++ b/pkg/securitypolicy/securitypolicyenforcer.go @@ -125,6 +125,8 @@ type SecurityPolicyEnforcer interface { 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) + EnforceMappedDirectoryMountPolicy(ctx context.Context, containerPath string, readOnly bool) (err error) + EnforceMappedDirectoryUnmountPolicy(ctx context.Context, containerPath string) (err error) GetUserInfo(spec *oci.Process, rootPath string) (IDName, []IDName, string, error) EnforceVerifiedCIMsPolicy(ctx context.Context, containerID string, layerHashes []string, mountedCim []string) (err error) EnforceRegistryChangesPolicy(ctx context.Context, containerID string, registryValues interface{}) error @@ -312,6 +314,14 @@ func (OpenDoorSecurityPolicyEnforcer) EnforceScratchUnmountPolicy(context.Contex return nil } +func (OpenDoorSecurityPolicyEnforcer) EnforceMappedDirectoryMountPolicy(context.Context, string, bool) error { + return nil +} + +func (OpenDoorSecurityPolicyEnforcer) EnforceMappedDirectoryUnmountPolicy(context.Context, string) error { + return nil +} + func (OpenDoorSecurityPolicyEnforcer) GetUserInfo(spec *oci.Process, rootPath string) (IDName, []IDName, string, error) { return IDName{}, nil, "", nil } @@ -441,6 +451,14 @@ func (ClosedDoorSecurityPolicyEnforcer) EnforceScratchUnmountPolicy(context.Cont return errors.New("unmounting scratch is denied by the policy") } +func (ClosedDoorSecurityPolicyEnforcer) EnforceMappedDirectoryMountPolicy(context.Context, string, bool) error { + return errors.New("mounting mapped directory is denied by the policy") +} + +func (ClosedDoorSecurityPolicyEnforcer) EnforceMappedDirectoryUnmountPolicy(context.Context, string) error { + return errors.New("unmounting mapped directory is denied by the policy") +} + func (ClosedDoorSecurityPolicyEnforcer) GetUserInfo(spec *oci.Process, rootPath string) (IDName, []IDName, string, error) { return IDName{}, nil, "", nil } diff --git a/pkg/securitypolicy/securitypolicyenforcer_rego.go b/pkg/securitypolicy/securitypolicyenforcer_rego.go index 96c5613dd6..846bb2278f 100644 --- a/pkg/securitypolicy/securitypolicyenforcer_rego.go +++ b/pkg/securitypolicy/securitypolicyenforcer_rego.go @@ -1157,6 +1157,23 @@ func (policy *regoEnforcer) EnforceScratchUnmountPolicy(ctx context.Context, scr return nil } +func (policy *regoEnforcer) EnforceMappedDirectoryMountPolicy(ctx context.Context, containerPath string, readOnly bool) error { + input := inputData{ + "containerPath": containerPath, + "readOnly": readOnly, + } + _, err := policy.enforce(ctx, "mapped_directory_mount", input) + return err +} + +func (policy *regoEnforcer) EnforceMappedDirectoryUnmountPolicy(ctx context.Context, containerPath string) error { + input := inputData{ + "unmountTarget": containerPath, + } + _, err := policy.enforce(ctx, "mapped_directory_unmount", input) + return err +} + 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{