diff --git a/internal/guest/runtime/hcsv2/uvm.go b/internal/guest/runtime/hcsv2/uvm.go index 43669a6f26..a11da27b83 100644 --- a/internal/guest/runtime/hcsv2/uvm.go +++ b/internal/guest/runtime/hcsv2/uvm.go @@ -1205,7 +1205,11 @@ func modifyMappedVirtualDisk( mountCtx, cancel := context.WithTimeout(ctx, time.Second*5) defer cancel() if mvd.MountPath != "" { - if mvd.ReadOnly { + if mvd.BlockDev { + if err = securityPolicy.EnforceMountBlockDevicePolicy(ctx, mvd.MountPath); err != nil { + return errors.Wrapf(err, "creating blockdev symlink at %s (-> scsi controller %d lun %d) denied by policy", mvd.MountPath, mvd.Controller, mvd.Lun) + } + } else if mvd.ReadOnly { var deviceHash string if verityInfo != nil { deviceHash = verityInfo.RootDigest @@ -1233,7 +1237,11 @@ func modifyMappedVirtualDisk( return nil case guestrequest.RequestTypeRemove: if mvd.MountPath != "" { - if mvd.ReadOnly { + if mvd.BlockDev { + if err = securityPolicy.EnforceUnmountBlockDevicePolicy(ctx, mvd.MountPath); err != nil { + return fmt.Errorf("removing blockdev symlink at %s (-> scsi controller %d lun %d) denied by policy: %w", mvd.MountPath, mvd.Controller, mvd.Lun, err) + } + } else if mvd.ReadOnly { if err := securityPolicy.EnforceDeviceUnmountPolicy(ctx, mvd.MountPath); err != nil { return fmt.Errorf("unmounting scsi device at %s denied by policy: %w", mvd.MountPath, err) } diff --git a/pkg/securitypolicy/api.rego b/pkg/securitypolicy/api.rego index 88c3d64d14..eda6757b1e 100644 --- a/pkg/securitypolicy/api.rego +++ b/pkg/securitypolicy/api.rego @@ -5,12 +5,14 @@ version := "@@API_VERSION@@" enforcement_points := { "mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}, "use_framework": false}, "rw_mount_device": {"introducedVersion": "0.11.0", "default_results": {}, "use_framework": true}, + "mount_blockdev": {"introducedVersion": "0.11.1", "default_results": {"allowed": false}, "use_framework": false}, "mount_overlay": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}, "use_framework": false}, "mount_cims": {"introducedVersion": "0.11.0", "default_results": {"allowed": false}, "use_framework": false}, "registry_changes": {"introducedVersion": "0.10.0", "default_results": {"allowed": false}, "use_framework": false}, "create_container": {"introducedVersion": "0.1.0", "default_results": {"allowed": false, "env_list": null, "allow_stdio_access": false}, "use_framework": false}, "unmount_device": {"introducedVersion": "0.2.0", "default_results": {"allowed": true}, "use_framework": false}, "rw_unmount_device": {"introducedVersion": "0.11.0", "default_results": {}, "use_framework": true}, + "unmount_blockdev": {"introducedVersion": "0.11.1", "default_results": {"allowed": false}, "use_framework": false}, "unmount_overlay": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}, "use_framework": false}, "exec_in_container": {"introducedVersion": "0.2.0", "default_results": {"allowed": true, "env_list": null}, "use_framework": false}, "exec_external": {"introducedVersion": "0.3.0", "default_results": {"allowed": true, "env_list": null, "allow_stdio_access": false}, "use_framework": false}, diff --git a/pkg/securitypolicy/framework.rego b/pkg/securitypolicy/framework.rego index daa0fe864e..4b0c049896 100644 --- a/pkg/securitypolicy/framework.rego +++ b/pkg/securitypolicy/framework.rego @@ -112,6 +112,14 @@ rw_unmount_device := {"metadata": [removeRWDevice], "allowed": true} { } } +# blockdev mounts (which are symlinks created at the mount point pointing to +# /dev/sdX instead of an actual mounted filesystem) are not supported on C-ACI +# and thus the framework currently denies them unconditionally. + +default mount_blockdev := {"allowed": false} + +default unmount_blockdev := {"allowed": false} + layerPaths_ok(layers) { length := count(layers) count(input.layerPaths) == length @@ -1418,6 +1426,10 @@ reason := { # Error messages ################################################################ +errors["blockdev mounts are not supported"] { + input.rule in ["mount_blockdev", "unmount_blockdev"] +} + errors["deviceHash not found"] { input.rule == "mount_device" not deviceHash_ok diff --git a/pkg/securitypolicy/open_door.rego b/pkg/securitypolicy/open_door.rego index 02da3fa9b6..63515a2378 100644 --- a/pkg/securitypolicy/open_door.rego +++ b/pkg/securitypolicy/open_door.rego @@ -4,12 +4,14 @@ api_version := "@@API_VERSION@@" mount_device := {"allowed": true} rw_mount_device := {"allowed": true} +mount_blockdev := {"allowed": true} mount_overlay := {"allowed": true} create_container := {"allowed": true, "env_list": null, "allow_stdio_access": true} mount_cims := {"allowed": true} registry_changes := {"allowed": true} unmount_device := {"allowed": true} rw_unmount_device := {"allowed": true} +unmount_blockdev := {"allowed": true} unmount_overlay := {"allowed": true} exec_in_container := {"allowed": true, "env_list": null} exec_external := {"allowed": true, "env_list": null, "allow_stdio_access": true} diff --git a/pkg/securitypolicy/policy.rego b/pkg/securitypolicy/policy.rego index 195d462931..9fba1668de 100644 --- a/pkg/securitypolicy/policy.rego +++ b/pkg/securitypolicy/policy.rego @@ -7,8 +7,10 @@ framework_version := "@@FRAMEWORK_VERSION@@" mount_device := data.framework.mount_device rw_mount_device := data.framework.rw_mount_device +mount_blockdev := data.framework.mount_blockdev unmount_device := data.framework.unmount_device rw_unmount_device := data.framework.rw_unmount_device +unmount_blockdev := data.framework.unmount_blockdev mount_overlay := data.framework.mount_overlay unmount_overlay := data.framework.unmount_overlay mount_cims:= data.framework.mount_cims diff --git a/pkg/securitypolicy/regopolicy_linux_test.go b/pkg/securitypolicy/regopolicy_linux_test.go index 8dd409fccf..ebf27aa0ca 100644 --- a/pkg/securitypolicy/regopolicy_linux_test.go +++ b/pkg/securitypolicy/regopolicy_linux_test.go @@ -7367,3 +7367,34 @@ func substituteUVMPath(sandboxID string, m mountInternal) mountInternal { } return m } + +func Test_Rego_EnforceMountBlockDevicePolicy_DefaultDenied(t *testing.T) { + p := generateConstraints(testRand, 1) + securityPolicy := p.toPolicy() + policy, err := newRegoPolicy(securityPolicy.marshalRego(), []oci.Mount{}, []oci.Mount{}, testOSType) + if err != nil { + t.Fatalf("cannot make rego policy from constraints: %v", err) + } + + target := testDataGenerator.uniqueLayerMountTarget() + err = policy.EnforceMountBlockDevicePolicy(p.ctx, target) + assertDecisionJSONContains(t, err, "blockdev mounts are not supported") + + err = policy.EnforceUnmountBlockDevicePolicy(p.ctx, target) + assertDecisionJSONContains(t, err, "blockdev mounts are not supported") +} + +func Test_Rego_EnforceMountBlockDevicePolicy_OpenDoor(t *testing.T) { + policy, err := newRegoPolicy(openDoorRego, []oci.Mount{}, []oci.Mount{}, testOSType) + if err != nil { + t.Fatalf("cannot compile open door rego policy: %v", err) + } + + target := testDataGenerator.uniqueLayerMountTarget() + if err = policy.EnforceMountBlockDevicePolicy(context.Background(), target); err != nil { + t.Errorf("open door should allow mount_blockdev, got: %v", err) + } + if err = policy.EnforceUnmountBlockDevicePolicy(context.Background(), target); err != nil { + t.Errorf("open door should allow unmount_blockdev, got: %v", err) + } +} diff --git a/pkg/securitypolicy/securitypolicyenforcer.go b/pkg/securitypolicy/securitypolicyenforcer.go index 2a4edefce1..940c0e5f76 100644 --- a/pkg/securitypolicy/securitypolicyenforcer.go +++ b/pkg/securitypolicy/securitypolicyenforcer.go @@ -60,8 +60,10 @@ func init() { type SecurityPolicyEnforcer interface { EnforceDeviceMountPolicy(ctx context.Context, target string, deviceHash string) (err error) EnforceRWDeviceMountPolicy(ctx context.Context, target string, encrypted, ensureFilesystem bool, filesystem string) (err error) + EnforceMountBlockDevicePolicy(ctx context.Context, target string) (err error) EnforceDeviceUnmountPolicy(ctx context.Context, unmountTarget string) (err error) EnforceRWDeviceUnmountPolicy(ctx context.Context, unmountTarget string) (err error) + EnforceUnmountBlockDevicePolicy(ctx context.Context, unmountTarget string) (err error) EnforceOverlayMountPolicy(ctx context.Context, containerID string, layerPaths []string, target string) (err error) EnforceOverlayUnmountPolicy(ctx context.Context, target string) (err error) EnforceCreateContainerPolicy( @@ -207,6 +209,10 @@ func (OpenDoorSecurityPolicyEnforcer) EnforceRWDeviceMountPolicy(context.Context return nil } +func (OpenDoorSecurityPolicyEnforcer) EnforceMountBlockDevicePolicy(context.Context, string) error { + return nil +} + func (OpenDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(context.Context, string) error { return nil } @@ -215,6 +221,10 @@ func (OpenDoorSecurityPolicyEnforcer) EnforceRWDeviceUnmountPolicy(context.Conte return nil } +func (OpenDoorSecurityPolicyEnforcer) EnforceUnmountBlockDevicePolicy(context.Context, string) error { + return nil +} + func (OpenDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(context.Context, string, []string, string) error { return nil } @@ -336,6 +346,10 @@ func (ClosedDoorSecurityPolicyEnforcer) EnforceRWDeviceMountPolicy(context.Conte return errors.New("Read-write device mounting is denied by policy") } +func (ClosedDoorSecurityPolicyEnforcer) EnforceMountBlockDevicePolicy(context.Context, string) error { + return errors.New("block-device mounting is denied by policy") +} + func (ClosedDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(context.Context, string) error { return errors.New("unmounting is denied by policy") } @@ -344,6 +358,10 @@ func (ClosedDoorSecurityPolicyEnforcer) EnforceRWDeviceUnmountPolicy(context.Con return errors.New("Read-write device unmounting is denied by policy") } +func (ClosedDoorSecurityPolicyEnforcer) EnforceUnmountBlockDevicePolicy(context.Context, string) error { + return errors.New("block-device unmounting is denied by policy") +} + func (ClosedDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(context.Context, string, []string, string) error { return errors.New("creating an overlay fs is denied by policy") } diff --git a/pkg/securitypolicy/securitypolicyenforcer_rego.go b/pkg/securitypolicy/securitypolicyenforcer_rego.go index 96c5613dd6..4279975c09 100644 --- a/pkg/securitypolicy/securitypolicyenforcer_rego.go +++ b/pkg/securitypolicy/securitypolicyenforcer_rego.go @@ -533,6 +533,15 @@ func (policy *regoEnforcer) EnforceRWDeviceMountPolicy(ctx context.Context, targ return err } +func (policy *regoEnforcer) EnforceMountBlockDevicePolicy(ctx context.Context, target string) error { + input := inputData{ + "target": target, + } + + _, err := policy.enforce(ctx, "mount_blockdev", input) + return err +} + func (policy *regoEnforcer) EnforceOverlayMountPolicy(ctx context.Context, containerID string, layerPaths []string, target string) error { input := inputData{ "containerID": containerID, @@ -822,6 +831,15 @@ func (policy *regoEnforcer) EnforceRWDeviceUnmountPolicy(ctx context.Context, un return err } +func (policy *regoEnforcer) EnforceUnmountBlockDevicePolicy(ctx context.Context, unmountTarget string) error { + input := inputData{ + "unmountTarget": unmountTarget, + } + + _, err := policy.enforce(ctx, "unmount_blockdev", input) + return err +} + func appendMountData(mountData []interface{}, mounts []oci.Mount) []interface{} { for _, mount := range mounts { mountData = append(mountData, inputData{ diff --git a/pkg/securitypolicy/version_api b/pkg/securitypolicy/version_api index d9df1bbc0c..af88ba8248 100644 --- a/pkg/securitypolicy/version_api +++ b/pkg/securitypolicy/version_api @@ -1 +1 @@ -0.11.0 +0.11.1 diff --git a/pkg/securitypolicy/version_framework b/pkg/securitypolicy/version_framework index 267577d47e..2b7c5ae018 100644 --- a/pkg/securitypolicy/version_framework +++ b/pkg/securitypolicy/version_framework @@ -1 +1 @@ -0.4.1 +0.4.2