Skip to content

Commit 0897a09

Browse files
committed
Merged PR 13694574: hcsv2/uvm, rego: Enforce OCI spec does not contain any LinuxDevices
[cherry-picked from 9f69e49..72b338a] [forward-ported from original PR 13653011 (e631aa42c6039ab0d228b746b280c26809433f00)] See https://msazure.visualstudio.com/ContainerPlatform/_git/Microsoft.hcsshim/pullrequest/13653011 Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
1 parent 32b6b6f commit 0897a09

5 files changed

Lines changed: 95 additions & 8 deletions

File tree

internal/guest/runtime/hcsv2/uvm.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"path"
1616
"path/filepath"
1717
"regexp"
18+
"slices"
1819
"strings"
1920
"sync"
2021
"sync/atomic"
@@ -448,6 +449,18 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
448449
}
449450
}
450451

452+
// Take a backup of the devices array before we populate it with any devices
453+
// found by GCS, in order to pass to the policy enforcer later.
454+
//
455+
// In specGuest.ApplyAnnotationsToSpec, if this is a privileged container,
456+
// we will add devices found in the GCS namespace's /dev. Regardless of
457+
// privileged or not, we also always include /dev/sev-guest. Since the
458+
// policy already lets the user enforce whether the container should be
459+
// privileged or not, and the sev-guest device is always added for a
460+
// confidential container, we do not need the policy enforcer to check these
461+
// devices we dynamically add again.
462+
extraLinuxDevices := slices.Clone(settings.OCISpecification.Linux.Devices)
463+
451464
// Normally we would be doing policy checking here at the start of our
452465
// "policy gated function". However, we can't for create container as we
453466
// need a properly correct sandboxID which might be changed by the code
@@ -583,21 +596,27 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
583596
return nil, err
584597
}
585598

586-
envToKeep, capsToKeep, allowStdio, err := h.securityOptions.PolicyEnforcer.EnforceCreateContainerPolicy(
599+
privileged := isPrivilegedContainerCreationRequest(ctx, settings.OCISpecification)
600+
noNewPrivileges := settings.OCISpecification.Process.NoNewPrivileges
601+
opts := &securitypolicy.CreateContainerOptions{
602+
SandboxID: sandboxID,
603+
Privileged: &privileged,
604+
NoNewPrivileges: &noNewPrivileges,
605+
Groups: groups,
606+
Umask: umask,
607+
Capabilities: settings.OCISpecification.Process.Capabilities,
608+
SeccompProfileSHA256: seccomp,
609+
LinuxDevices: extraLinuxDevices,
610+
}
611+
envToKeep, capsToKeep, allowStdio, err := h.securityOptions.PolicyEnforcer.EnforceCreateContainerPolicyV2(
587612
ctx,
588-
sandboxID,
589613
id,
590614
settings.OCISpecification.Process.Args,
591615
settings.OCISpecification.Process.Env,
592616
settings.OCISpecification.Process.Cwd,
593617
settings.OCISpecification.Mounts,
594-
isPrivilegedContainerCreationRequest(ctx, settings.OCISpecification),
595-
settings.OCISpecification.Process.NoNewPrivileges,
596618
user,
597-
groups,
598-
umask,
599-
settings.OCISpecification.Process.Capabilities,
600-
seccomp,
619+
opts,
601620
)
602621
if err != nil {
603622
return nil, errors.Wrapf(err, "container creation denied due to policy")

pkg/securitypolicy/framework.rego

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,14 @@ seccomp_ok(seccomp_profile_sha256) {
387387
is_windows
388388
}
389389

390+
devices_ok(expected_devices, actual_devices) {
391+
# Allow out of order but not duplicates
392+
set_expected := {dev | dev := expected_devices[_]}
393+
set_actual := {dev | dev := actual_devices[_]}
394+
set_expected == set_actual
395+
count(set_actual) == count(actual_devices)
396+
}
397+
390398
default container_started := false
391399

392400
container_started {
@@ -598,6 +606,8 @@ create_container := {"metadata": [updateMatches, addStarted],
598606
command_ok(container.command)
599607
mountList_ok(container.mounts, container.allow_elevated)
600608
seccomp_ok(container.seccomp_profile_sha256)
609+
# We do not support adding device nodes to the policy yet
610+
devices_ok([], input.devices)
601611
]
602612

603613
count(possible_after_initial_containers) > 0
@@ -1979,6 +1989,12 @@ errors["capabilities don't match"] {
19791989
count(possible_after_caps_containers) == 0
19801990
}
19811991

1992+
errors["devices not supported"] {
1993+
is_linux
1994+
input.rule == "create_container"
1995+
not devices_ok([], input.devices)
1996+
}
1997+
19821998
# covers exec_in_container as well. it shouldn't be possible to ever get
19831999
# an exec_in_container as it "inherits" capabilities rules from create_container
19842000
errors["containers only distinguishable by capabilties"] {

pkg/securitypolicy/regopolicy_linux_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,39 @@ func Test_Rego_EnforceCreateContainer_Capabilities_Drop_NoMatches(t *testing.T)
21272127
}
21282128
}
21292129

2130+
func Test_Regi_EnforceCreateContainer_RequireNoDevices(t *testing.T) {
2131+
f := func(p *generatedConstraints) bool {
2132+
tc, err := setupSimpleRegoCreateContainerTest(p)
2133+
if err != nil {
2134+
t.Error(err)
2135+
return false
2136+
}
2137+
2138+
privileged := false
2139+
2140+
_, _, _, err = tc.policy.EnforceCreateContainerPolicyV2(p.ctx, tc.containerID, tc.argList, tc.envList, tc.workingDir, tc.mounts, tc.user, &CreateContainerOptions{
2141+
SandboxID: tc.sandboxID,
2142+
Privileged: &privileged,
2143+
NoNewPrivileges: &tc.noNewPrivileges,
2144+
Groups: tc.groups,
2145+
Umask: tc.umask,
2146+
Capabilities: tc.capabilities,
2147+
SeccompProfileSHA256: tc.seccomp,
2148+
LinuxDevices: []oci.LinuxDevice{
2149+
{
2150+
Path: "/test",
2151+
},
2152+
},
2153+
})
2154+
2155+
return assertDecisionJSONContains(t, err, "devices not supported")
2156+
}
2157+
2158+
if err := quick.Check(f, &quick.Config{MaxCount: 50, Rand: testRand}); err != nil {
2159+
t.Errorf("Test_Regi_EnforceCreateContainer_RequireNoDevices: %v", err)
2160+
}
2161+
}
2162+
21302163
func Test_Rego_ExtendDefaultMounts(t *testing.T) {
21312164
f := func(p *generatedConstraints) bool {
21322165
tc, err := setupSimpleRegoCreateContainerTest(p)

pkg/securitypolicy/securitypolicyenforcer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type CreateContainerOptions struct {
2929
Umask string
3030
Capabilities *oci.LinuxCapabilities
3131
SeccompProfileSHA256 string
32+
LinuxDevices []oci.LinuxDevice
3233
}
3334
type SignalContainerOptions struct {
3435
IsInitProcess bool

pkg/securitypolicy/securitypolicyenforcer_rego.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ func (policy *regoEnforcer) EnforceCreateContainerPolicy(
715715
Umask: umask,
716716
Capabilities: capabilities,
717717
SeccompProfileSHA256: seccompProfileSHA256,
718+
LinuxDevices: []oci.LinuxDevice{},
718719
}
719720
return policy.EnforceCreateContainerPolicyV2(ctx, containerID, argList, envList, workingDir, mounts, user, opts)
720721
}
@@ -752,6 +753,7 @@ func (policy *regoEnforcer) EnforceCreateContainerPolicyV2(
752753
"sandboxDir": SandboxMountsDir(opts.SandboxID),
753754
"hugePagesDir": HugePagesMountsDir(opts.SandboxID),
754755
"mounts": appendMountData([]interface{}{}, mounts),
756+
"devices": appendDeviceData([]interface{}{}, opts.LinuxDevices),
755757
"privileged": opts.Privileged,
756758
"noNewPrivileges": opts.NoNewPrivileges,
757759
"user": user.toInput(),
@@ -840,6 +842,22 @@ func appendMountData(mountData []interface{}, mounts []oci.Mount) []interface{}
840842
return mountData
841843
}
842844

845+
func appendDeviceData(deviceData []interface{}, devices []oci.LinuxDevice) []interface{} {
846+
for _, device := range devices {
847+
deviceData = append(deviceData, inputData{
848+
"path": device.Path,
849+
"type": device.Type,
850+
"major": device.Major,
851+
"minor": device.Minor,
852+
"fileMode": device.FileMode,
853+
"uid": device.UID,
854+
"gid": device.GID,
855+
})
856+
}
857+
858+
return deviceData
859+
}
860+
843861
func (policy *regoEnforcer) ExtendDefaultMounts(mounts []oci.Mount) error {
844862
policy.defaultMounts = append(policy.defaultMounts, mounts...)
845863
defaultMounts := appendMountData([]interface{}{}, policy.defaultMounts)

0 commit comments

Comments
 (0)