Skip to content

Commit f21a96c

Browse files
authored
Merge pull request moby#4003 from tonistiigi/oci-cgroupns
oci: make sure cgroupns is enabled if supported
2 parents 595bfa2 + 45b3856 commit f21a96c

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

client/client_test.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,10 @@ func testCgroupParent(t *testing.T, sb integration.Sandbox) {
853853
t.SkipNow()
854854
}
855855

856+
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
857+
t.Skipf("test requires cgroup v2")
858+
}
859+
856860
c, err := New(sb.Context(), sb.Address())
857861
require.NoError(t, err)
858862
defer c.Close()
@@ -864,8 +868,21 @@ func testCgroupParent(t *testing.T, sb integration.Sandbox) {
864868
st = img.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
865869
}
866870

867-
run(`sh -c "cat /proc/self/cgroup > first"`, llb.WithCgroupParent("foocgroup"))
868-
run(`sh -c "cat /proc/self/cgroup > second"`)
871+
cgroupName := "test." + identity.NewID()
872+
873+
err = os.MkdirAll(filepath.Join("/sys/fs/cgroup", cgroupName), 0755)
874+
require.NoError(t, err)
875+
876+
defer func() {
877+
err := os.RemoveAll(filepath.Join("/sys/fs/cgroup", cgroupName))
878+
require.NoError(t, err)
879+
}()
880+
881+
err = os.WriteFile(filepath.Join("/sys/fs/cgroup", cgroupName, "pids.max"), []byte("10"), 0644)
882+
require.NoError(t, err)
883+
884+
run(`sh -c "(for i in $(seq 1 10); do sleep 1 & done 2>first.error); cat /proc/self/cgroup >> first"`, llb.WithCgroupParent(cgroupName))
885+
run(`sh -c "(for i in $(seq 1 10); do sleep 1 & done 2>second.error); cat /proc/self/cgroup >> second"`)
869886

870887
def, err := st.Marshal(sb.Context())
871888
require.NoError(t, err)
@@ -882,13 +899,22 @@ func testCgroupParent(t *testing.T, sb integration.Sandbox) {
882899
}, nil)
883900
require.NoError(t, err)
884901

902+
// neither process leaks parent cgroup name inside container
885903
dt, err := os.ReadFile(filepath.Join(destDir, "first"))
886904
require.NoError(t, err)
887-
require.Contains(t, strings.TrimSpace(string(dt)), `/foocgroup/buildkit/`)
905+
require.NotContains(t, strings.TrimSpace(string(dt)), cgroupName)
888906

889907
dt2, err := os.ReadFile(filepath.Join(destDir, "second"))
890908
require.NoError(t, err)
891-
require.NotContains(t, strings.TrimSpace(string(dt2)), `/foocgroup/buildkit/`)
909+
require.NotContains(t, strings.TrimSpace(string(dt2)), cgroupName)
910+
911+
dt, err = os.ReadFile(filepath.Join(destDir, "first.error"))
912+
require.NoError(t, err)
913+
require.Contains(t, strings.TrimSpace(string(dt)), "Resource temporarily unavailable")
914+
915+
dt, err = os.ReadFile(filepath.Join(destDir, "second.error"))
916+
require.NoError(t, err)
917+
require.Equal(t, strings.TrimSpace(string(dt)), "")
892918
}
893919

894920
func testNetworkMode(t *testing.T, sb integration.Sandbox) {

executor/oci/spec.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
137137
return nil, nil, err
138138
}
139139

140+
if cgroupNamespaceSupported() {
141+
s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
142+
Type: specs.CgroupNamespace,
143+
})
144+
}
145+
140146
if len(meta.Ulimit) == 0 {
141147
// reset open files limit
142148
s.Process.Rlimits = nil

executor/oci/spec_unix.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package oci
66
import (
77
"context"
88
"fmt"
9+
"os"
910
"strings"
11+
"sync"
1012

1113
"github.com/containerd/containerd/containers"
1214
"github.com/containerd/containerd/oci"
@@ -21,6 +23,11 @@ import (
2123
"github.com/pkg/errors"
2224
)
2325

26+
var (
27+
cgroupNSOnce sync.Once
28+
supportsCgroupNS bool
29+
)
30+
2431
const (
2532
tracingSocketPath = "/dev/otel-grpc.sock"
2633
)
@@ -139,3 +146,12 @@ func getTracingSocketMount(socket string) specs.Mount {
139146
func getTracingSocket() string {
140147
return fmt.Sprintf("unix://%s", tracingSocketPath)
141148
}
149+
150+
func cgroupNamespaceSupported() bool {
151+
cgroupNSOnce.Do(func() {
152+
if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) {
153+
supportsCgroupNS = true
154+
}
155+
})
156+
return supportsCgroupNS
157+
}

executor/oci/spec_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,7 @@ func getTracingSocketMount(socket string) specs.Mount {
6363
func getTracingSocket() string {
6464
return fmt.Sprintf("npipe://%s", filepath.ToSlash(tracingSocketPath))
6565
}
66+
67+
func cgroupNamespaceSupported() bool {
68+
return false
69+
}

frontend/dockerfile/dockerfile_test.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5194,10 +5194,27 @@ func testCgroupParent(t *testing.T, sb integration.Sandbox) {
51945194
t.SkipNow()
51955195
}
51965196

5197+
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
5198+
t.Skipf("test requires cgroup v2")
5199+
}
5200+
5201+
cgroupName := "test." + identity.NewID()
5202+
5203+
err := os.MkdirAll(filepath.Join("/sys/fs/cgroup", cgroupName), 0755)
5204+
require.NoError(t, err)
5205+
5206+
defer func() {
5207+
err := os.RemoveAll(filepath.Join("/sys/fs/cgroup", cgroupName))
5208+
require.NoError(t, err)
5209+
}()
5210+
5211+
err = os.WriteFile(filepath.Join("/sys/fs/cgroup", cgroupName, "pids.max"), []byte("10"), 0644)
5212+
require.NoError(t, err)
5213+
51975214
f := getFrontend(t, sb)
51985215
dockerfile := []byte(`
51995216
FROM alpine AS base
5200-
RUN cat /proc/self/cgroup > /out
5217+
RUN mkdir /out; (for i in $(seq 1 10); do sleep 1 & done 2>/out/error); cat /proc/self/cgroup > /out/cgroup
52015218
FROM scratch
52025219
COPY --from=base /out /
52035220
`)
@@ -5216,7 +5233,7 @@ COPY --from=base /out /
52165233

52175234
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
52185235
FrontendAttrs: map[string]string{
5219-
"cgroup-parent": "foocgroup",
5236+
"cgroup-parent": cgroupName,
52205237
},
52215238
LocalDirs: map[string]string{
52225239
dockerui.DefaultLocalNameDockerfile: dir,
@@ -5231,9 +5248,14 @@ COPY --from=base /out /
52315248
}, nil)
52325249
require.NoError(t, err)
52335250

5234-
dt, err := os.ReadFile(filepath.Join(destDir, "out"))
5251+
dt, err := os.ReadFile(filepath.Join(destDir, "cgroup"))
5252+
require.NoError(t, err)
5253+
// cgroupns does not leak the parent cgroup name
5254+
require.NotContains(t, strings.TrimSpace(string(dt)), `foocgroup`)
5255+
5256+
dt, err = os.ReadFile(filepath.Join(destDir, "error"))
52355257
require.NoError(t, err)
5236-
require.Contains(t, strings.TrimSpace(string(dt)), `/foocgroup/buildkit/`)
5258+
require.Contains(t, strings.TrimSpace(string(dt)), `Resource temporarily unavailable`)
52375259
}
52385260

52395261
func testNamedImageContext(t *testing.T, sb integration.Sandbox) {

0 commit comments

Comments
 (0)