Skip to content

Commit 86870e7

Browse files
authored
Merge pull request #140 from thaJeztah/integrate_libcontainer_userns
integrate libcontainer/userns into moby/sys/user
2 parents 617cda3 + 5cd502c commit 86870e7

7 files changed

+130
-7
lines changed

Diff for: Makefile

+12-6
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ clean:
1616
test: test-local
1717
set -eu; \
1818
for p in $(PACKAGES); do \
19-
(cd $$p; go test $(RUN_VIA_SUDO) -v .); \
19+
if $p = user && go version | grep -qv go1.18; then \
20+
(cd $$p; go test $(RUN_VIA_SUDO) -v .); \
21+
fi \
2022
done
2123

2224
.PHONY: tidy
2325
tidy:
2426
set -eu; \
25-
for p in $(PACKAGES); do \
26-
(cd $$p; go mod tidy); \
27+
for p in $(PACKAGES); do \
28+
if $p = user && go version | grep -qv go1.18; then \
29+
(cd $$p; go mod tidy); \
30+
fi \
2731
done
2832

2933
# Test the mount module against the local mountinfo source code instead of the
@@ -42,9 +46,11 @@ lint: $(BINDIR)/golangci-lint
4246
$(BINDIR)/golangci-lint version
4347
set -eu; \
4448
for p in $(PACKAGES); do \
45-
(cd $$p; \
46-
go mod download; \
47-
../$(BINDIR)/golangci-lint run); \
49+
if $p = user && go version | grep -qv go1.18; then \
50+
(cd $$p; \
51+
go mod download; \
52+
../$(BINDIR)/golangci-lint run); \
53+
fi \
4854
done
4955

5056
$(BINDIR)/golangci-lint: $(BINDIR)

Diff for: user/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/moby/sys/user
22

3-
go 1.18
3+
go 1.21
44

55
require golang.org/x/sys v0.1.0

Diff for: user/userns/userns.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Package userns provides utilities to detect whether we are currently running
2+
// in a Linux user namespace.
3+
//
4+
// This code was migrated from [libcontainer/runc], which based its implementation
5+
// on code from [lcx/incus].
6+
//
7+
// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49
8+
// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700
9+
package userns
10+
11+
// RunningInUserNS detects whether we are currently running in a Linux
12+
// user namespace and memoizes the result. It returns false on non-Linux
13+
// platforms.
14+
func RunningInUserNS() bool {
15+
return inUserNS()
16+
}

Diff for: user/userns/userns_linux.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package userns
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"sync"
8+
)
9+
10+
var inUserNS = sync.OnceValue(runningInUserNS)
11+
12+
// runningInUserNS detects whether we are currently running in a user namespace.
13+
//
14+
// This code was migrated from [libcontainer/runc] and based on an implementation
15+
// from [lcx/incus].
16+
//
17+
// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49
18+
// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700
19+
func runningInUserNS() bool {
20+
file, err := os.Open("/proc/self/uid_map")
21+
if err != nil {
22+
// This kernel-provided file only exists if user namespaces are supported.
23+
return false
24+
}
25+
defer file.Close()
26+
27+
buf := bufio.NewReader(file)
28+
l, _, err := buf.ReadLine()
29+
if err != nil {
30+
return false
31+
}
32+
33+
return uidMapInUserNS(string(l))
34+
}
35+
36+
func uidMapInUserNS(uidMap string) bool {
37+
if uidMap == "" {
38+
// File exist but empty (the initial state when userns is created,
39+
// see user_namespaces(7)).
40+
return true
41+
}
42+
43+
var a, b, c int64
44+
if _, err := fmt.Sscanf(uidMap, "%d %d %d", &a, &b, &c); err != nil {
45+
// Assume we are in a regular, non user namespace.
46+
return false
47+
}
48+
49+
// As per user_namespaces(7), /proc/self/uid_map of
50+
// the initial user namespace shows 0 0 4294967295.
51+
initNS := a == 0 && b == 0 && c == 4294967295
52+
return !initNS
53+
}

Diff for: user/userns/userns_linux_fuzzer.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build linux && gofuzz
2+
3+
package userns
4+
5+
func FuzzUIDMap(uidmap []byte) int {
6+
_ = uidMapInUserNS(string(uidmap))
7+
return 1
8+
}

Diff for: user/userns/userns_linux_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package userns
2+
3+
import "testing"
4+
5+
func TestUIDMapInUserNS(t *testing.T) {
6+
cases := []struct {
7+
s string
8+
expected bool
9+
}{
10+
{
11+
s: " 0 0 4294967295\n",
12+
expected: false,
13+
},
14+
{
15+
s: " 0 0 1\n",
16+
expected: true,
17+
},
18+
{
19+
s: " 0 1001 1\n 1 231072 65536\n",
20+
expected: true,
21+
},
22+
{
23+
// file exist but empty (the initial state when userns is created. see man 7 user_namespaces)
24+
s: "",
25+
expected: true,
26+
},
27+
}
28+
for _, c := range cases {
29+
actual := uidMapInUserNS(c.s)
30+
if c.expected != actual {
31+
t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s)
32+
}
33+
}
34+
}

Diff for: user/userns/userns_unsupported.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build !linux
2+
3+
package userns
4+
5+
// inUserNS is a stub for non-Linux systems. Always returns false.
6+
func inUserNS() bool { return false }

0 commit comments

Comments
 (0)