From b74243270cb0d49ce9c49e7544dfa186034fa243 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 13 Mar 2021 16:40:51 +0100 Subject: [PATCH 01/10] libcontainer/system: move userns utilities to separate package Moving these utilities to a separate package, so that consumers of this package don't have to pull in the whole "system" package. Looking at uses of these utilities (outside of runc itself); `RunningInUserNS()` is used by [various external consumers][1], so adding a "Deprecated" alias for this. [1]: https://grep.app/search?current=2&q=.RunningInUserNS Signed-off-by: Sebastiaan van Stijn --- user/userns/userns.go | 5 ++++ user/userns/userns_fuzzer.go | 15 +++++++++++ user/userns/userns_linux.go | 37 +++++++++++++++++++++++++ user/userns/userns_linux_test.go | 45 +++++++++++++++++++++++++++++++ user/userns/userns_unsupported.go | 17 ++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 user/userns/userns.go create mode 100644 user/userns/userns_fuzzer.go create mode 100644 user/userns/userns_linux.go create mode 100644 user/userns/userns_linux_test.go create mode 100644 user/userns/userns_unsupported.go diff --git a/user/userns/userns.go b/user/userns/userns.go new file mode 100644 index 0000000..f6cb98e --- /dev/null +++ b/user/userns/userns.go @@ -0,0 +1,5 @@ +package userns + +// RunningInUserNS detects whether we are currently running in a user namespace. +// Originally copied from github.com/lxc/lxd/shared/util.go +var RunningInUserNS = runningInUserNS diff --git a/user/userns/userns_fuzzer.go b/user/userns/userns_fuzzer.go new file mode 100644 index 0000000..529f8ea --- /dev/null +++ b/user/userns/userns_fuzzer.go @@ -0,0 +1,15 @@ +// +build gofuzz + +package userns + +import ( + "strings" + + "github.com/opencontainers/runc/libcontainer/user" +) + +func FuzzUIDMap(data []byte) int { + uidmap, _ := user.ParseIDMap(strings.NewReader(string(data))) + _ = uidMapInUserNS(uidmap) + return 1 +} diff --git a/user/userns/userns_linux.go b/user/userns/userns_linux.go new file mode 100644 index 0000000..724e6df --- /dev/null +++ b/user/userns/userns_linux.go @@ -0,0 +1,37 @@ +package userns + +import ( + "sync" + + "github.com/opencontainers/runc/libcontainer/user" +) + +var ( + inUserNS bool + nsOnce sync.Once +) + +// runningInUserNS detects whether we are currently running in a user namespace. +// Originally copied from github.com/lxc/lxd/shared/util.go +func runningInUserNS() bool { + nsOnce.Do(func() { + uidmap, err := user.CurrentProcessUIDMap() + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return + } + inUserNS = uidMapInUserNS(uidmap) + }) + return inUserNS +} + +func uidMapInUserNS(uidmap []user.IDMap) bool { + /* + * We assume we are in the initial user namespace if we have a full + * range - 4294967295 uids starting at uid 0. + */ + if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 { + return false + } + return true +} diff --git a/user/userns/userns_linux_test.go b/user/userns/userns_linux_test.go new file mode 100644 index 0000000..2ddd3ff --- /dev/null +++ b/user/userns/userns_linux_test.go @@ -0,0 +1,45 @@ +// +build linux + +package userns + +import ( + "strings" + "testing" + + "github.com/opencontainers/runc/libcontainer/user" +) + +func TestUIDMapInUserNS(t *testing.T) { + cases := []struct { + s string + expected bool + }{ + { + s: " 0 0 4294967295\n", + expected: false, + }, + { + s: " 0 0 1\n", + expected: true, + }, + { + s: " 0 1001 1\n 1 231072 65536\n", + expected: true, + }, + { + // file exist but empty (the initial state when userns is created. see man 7 user_namespaces) + s: "", + expected: true, + }, + } + for _, c := range cases { + uidmap, err := user.ParseIDMap(strings.NewReader(c.s)) + if err != nil { + t.Fatal(err) + } + actual := uidMapInUserNS(uidmap) + if c.expected != actual { + t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s) + } + } +} diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go new file mode 100644 index 0000000..f45bb0c --- /dev/null +++ b/user/userns/userns_unsupported.go @@ -0,0 +1,17 @@ +// +build !linux + +package userns + +import "github.com/opencontainers/runc/libcontainer/user" + +// runningInUserNS is a stub for non-Linux systems +// Always returns false +func runningInUserNS() bool { + return false +} + +// uidMapInUserNS is a stub for non-Linux systems +// Always returns false +func uidMapInUserNS(uidmap []user.IDMap) bool { + return false +} From db243e21d3287740ef7f50cb4e9d0268449089dd Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 26 Aug 2021 17:34:16 -0700 Subject: [PATCH 02/10] *: rm redundant linux build tag For files that end with _linux.go or _linux_test.go, there is no need to specify linux build tag, as it is assumed from the file name. In addition, rename libcontainer/notify_linux_v2.go -> libcontainer/notify_v2_linux.go for the file name to make sense. Signed-off-by: Kir Kolyshkin --- user/userns/userns_linux_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/user/userns/userns_linux_test.go b/user/userns/userns_linux_test.go index 2ddd3ff..90d9270 100644 --- a/user/userns/userns_linux_test.go +++ b/user/userns/userns_linux_test.go @@ -1,5 +1,3 @@ -// +build linux - package userns import ( From b19e0848c8b70f5d9aed602136176f8e4257e417 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 30 Aug 2021 20:58:22 -0700 Subject: [PATCH 03/10] *: add go-1.17+ go:build tags Go 1.17 introduce this new (and better) way to specify build tags. For more info, see https://golang.org/design/draft-gobuild. As a way to seamlessly switch from old to new build tags, gofmt (and gopls) from go 1.17 adds the new tags along with the old ones. Later, when go < 1.17 is no longer supported, the old build tags can be removed. Now, as I started to use latest gopls (v0.7.1), it adds these tags while I edit. Rather than to randomly add new build tags, I guess it is better to do it once for all files. Mind that previous commits removed some tags that were useless, so this one only touches packages that can at least be built on non-linux. Brought to you by go1.17 fmt ./... Signed-off-by: Kir Kolyshkin --- user/userns/userns_fuzzer.go | 1 + user/userns/userns_unsupported.go | 1 + 2 files changed, 2 insertions(+) diff --git a/user/userns/userns_fuzzer.go b/user/userns/userns_fuzzer.go index 529f8ea..1e00ab8 100644 --- a/user/userns/userns_fuzzer.go +++ b/user/userns/userns_fuzzer.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package userns diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go index f45bb0c..f35c13a 100644 --- a/user/userns/userns_unsupported.go +++ b/user/userns/userns_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package userns From 87e38c827eca3567fdac43d6a83f9c90fe4a152b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 22 Mar 2021 14:22:14 +0100 Subject: [PATCH 04/10] libcontainer/userns: simplify, and separate from "user" package. This makes libcontainer/userns self-dependent, largely returning to the original implementation from lxc. The `uiMapInUserNS` is kept as a separate function for unit-testing and fuzzing. Signed-off-by: Sebastiaan van Stijn --- user/userns/userns.go | 1 - user/userns/userns_fuzzer.go | 11 ++------ user/userns/userns_linux.go | 44 ++++++++++++++++++++++--------- user/userns/userns_linux_test.go | 13 ++------- user/userns/userns_unsupported.go | 4 +-- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/user/userns/userns.go b/user/userns/userns.go index f6cb98e..b225f18 100644 --- a/user/userns/userns.go +++ b/user/userns/userns.go @@ -1,5 +1,4 @@ package userns // RunningInUserNS detects whether we are currently running in a user namespace. -// Originally copied from github.com/lxc/lxd/shared/util.go var RunningInUserNS = runningInUserNS diff --git a/user/userns/userns_fuzzer.go b/user/userns/userns_fuzzer.go index 1e00ab8..bff03f8 100644 --- a/user/userns/userns_fuzzer.go +++ b/user/userns/userns_fuzzer.go @@ -3,14 +3,7 @@ package userns -import ( - "strings" - - "github.com/opencontainers/runc/libcontainer/user" -) - -func FuzzUIDMap(data []byte) int { - uidmap, _ := user.ParseIDMap(strings.NewReader(string(data))) - _ = uidMapInUserNS(uidmap) +func FuzzUIDMap(uidmap []byte) int { + _ = uidMapInUserNS(string(uidmap)) return 1 } diff --git a/user/userns/userns_linux.go b/user/userns/userns_linux.go index 724e6df..a6710b3 100644 --- a/user/userns/userns_linux.go +++ b/user/userns/userns_linux.go @@ -1,9 +1,10 @@ package userns import ( + "bufio" + "fmt" + "os" "sync" - - "github.com/opencontainers/runc/libcontainer/user" ) var ( @@ -12,26 +13,43 @@ var ( ) // runningInUserNS detects whether we are currently running in a user namespace. -// Originally copied from github.com/lxc/lxd/shared/util.go +// +// Originally copied from https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700. func runningInUserNS() bool { nsOnce.Do(func() { - uidmap, err := user.CurrentProcessUIDMap() + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported. + return + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() if err != nil { - // This kernel-provided file only exists if user namespaces are supported return } - inUserNS = uidMapInUserNS(uidmap) + + inUserNS = uidMapInUserNS(string(l)) }) return inUserNS } -func uidMapInUserNS(uidmap []user.IDMap) bool { - /* - * We assume we are in the initial user namespace if we have a full - * range - 4294967295 uids starting at uid 0. - */ - if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 { +func uidMapInUserNS(uidMap string) bool { + if uidMap == "" { + // File exist but empty (the initial state when userns is created, + // see user_namespaces(7)). + return true + } + + var a, b, c int64 + if _, err := fmt.Sscanf(uidMap, "%d %d %d", &a, &b, &c); err != nil { + // Assume we are in a regular, non user namespace. return false } - return true + + // As per user_namespaces(7), /proc/self/uid_map of + // the initial user namespace shows 0 0 4294967295. + initNS := a == 0 && b == 0 && c == 4294967295 + return !initNS } diff --git a/user/userns/userns_linux_test.go b/user/userns/userns_linux_test.go index 90d9270..25c4ac3 100644 --- a/user/userns/userns_linux_test.go +++ b/user/userns/userns_linux_test.go @@ -1,11 +1,6 @@ package userns -import ( - "strings" - "testing" - - "github.com/opencontainers/runc/libcontainer/user" -) +import "testing" func TestUIDMapInUserNS(t *testing.T) { cases := []struct { @@ -31,11 +26,7 @@ func TestUIDMapInUserNS(t *testing.T) { }, } for _, c := range cases { - uidmap, err := user.ParseIDMap(strings.NewReader(c.s)) - if err != nil { - t.Fatal(err) - } - actual := uidMapInUserNS(uidmap) + actual := uidMapInUserNS(c.s) if c.expected != actual { t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s) } diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go index f35c13a..391c811 100644 --- a/user/userns/userns_unsupported.go +++ b/user/userns/userns_unsupported.go @@ -3,8 +3,6 @@ package userns -import "github.com/opencontainers/runc/libcontainer/user" - // runningInUserNS is a stub for non-Linux systems // Always returns false func runningInUserNS() bool { @@ -13,6 +11,6 @@ func runningInUserNS() bool { // uidMapInUserNS is a stub for non-Linux systems // Always returns false -func uidMapInUserNS(uidmap []user.IDMap) bool { +func uidMapInUserNS(uidMap string) bool { return false } From bb724644b16777ba11791c4fcea2bb5875a76f9e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 29 Jun 2024 15:44:42 +0200 Subject: [PATCH 05/10] remove pre-go1.17 build-tags Removed pre-go1.17 build-tags with go fix; go fix -mod=readonly ./... Signed-off-by: Sebastiaan van Stijn --- user/userns/userns_fuzzer.go | 1 - user/userns/userns_unsupported.go | 1 - 2 files changed, 2 deletions(-) diff --git a/user/userns/userns_fuzzer.go b/user/userns/userns_fuzzer.go index bff03f8..d90cffc 100644 --- a/user/userns/userns_fuzzer.go +++ b/user/userns/userns_fuzzer.go @@ -1,5 +1,4 @@ //go:build gofuzz -// +build gofuzz package userns diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go index 391c811..71d4c63 100644 --- a/user/userns/userns_unsupported.go +++ b/user/userns/userns_unsupported.go @@ -1,5 +1,4 @@ //go:build !linux -// +build !linux package userns From 333fe311f6169794258dfea8e688e33c5503111e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 29 Jun 2024 17:53:53 +0200 Subject: [PATCH 06/10] libct/userns: change RunningInUserNS to a wrapper instead of an alias This was a poor decision on my side; ab29593e7b0392c30a04c97cf889b575ed419aad moved this utility to a separate package, and split the exported function from the implementation (and stubs). Out of convenience, I used an alias for the latter part, but there's two downsides to that; - `RunningInUserNS` being an exported var means that (technically) it can be replaced by other code; perhaps that's a "feature", but not one we intended it to be used for. - `RunningInUserNS` being implemented through a var / alias means it's also documented as such on [pkg.go.dev], which is confusing. This patch changes it to a regular function, acting as a wrapper for the underlying implementations. While at it, also slightly touching up the GoDoc to describe its functionality / behavior. [pkg.go.dev]: https://pkg.go.dev/github.com/opencontainers/runc@v1.1.13/libcontainer/userns#RunningInUserNS Signed-off-by: Sebastiaan van Stijn --- user/userns/userns.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/user/userns/userns.go b/user/userns/userns.go index b225f18..069a9bb 100644 --- a/user/userns/userns.go +++ b/user/userns/userns.go @@ -1,4 +1,8 @@ package userns -// RunningInUserNS detects whether we are currently running in a user namespace. -var RunningInUserNS = runningInUserNS +// RunningInUserNS detects whether we are currently running in a Linux +// user namespace and memoizes the result. It returns false on non-Linux +// platforms. +func RunningInUserNS() bool { + return runningInUserNS() +} From bc0de3222f9378478ff1655396d8981ee63a4413 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 29 Jun 2024 18:08:44 +0200 Subject: [PATCH 07/10] libct/userns: make fuzzer Linux-only, and remove stub for uidMapInUserNS The fuzzer for this only runs on Linux; rename the file to be Linux-only so that we don't have to stub out the uidMapInUserNS function. Signed-off-by: Sebastiaan van Stijn --- user/userns/{userns_fuzzer.go => userns_linux_fuzzer.go} | 2 +- user/userns/userns_unsupported.go | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) rename user/userns/{userns_fuzzer.go => userns_linux_fuzzer.go} (79%) diff --git a/user/userns/userns_fuzzer.go b/user/userns/userns_linux_fuzzer.go similarity index 79% rename from user/userns/userns_fuzzer.go rename to user/userns/userns_linux_fuzzer.go index d90cffc..26ba2e1 100644 --- a/user/userns/userns_fuzzer.go +++ b/user/userns/userns_linux_fuzzer.go @@ -1,4 +1,4 @@ -//go:build gofuzz +//go:build linux && gofuzz package userns diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go index 71d4c63..47dd5aa 100644 --- a/user/userns/userns_unsupported.go +++ b/user/userns/userns_unsupported.go @@ -7,9 +7,3 @@ package userns func runningInUserNS() bool { return false } - -// uidMapInUserNS is a stub for non-Linux systems -// Always returns false -func uidMapInUserNS(uidMap string) bool { - return false -} From bc3a8a53224a5b56959e58cbadaebfd9bc4c69f8 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 29 Jun 2024 18:15:07 +0200 Subject: [PATCH 08/10] libct/userns: implement RunningInUserNS with sync.OnceValue Now that we dropped support for go < 1.21, we can use this; moving the sync.once out of the runningInUserNS() implementation would also allow for it to be more easily tested if we'd decide to. Signed-off-by: Sebastiaan van Stijn --- user/userns/userns.go | 2 +- user/userns/userns_linux.go | 36 +++++++++++++------------------ user/userns/userns_unsupported.go | 7 ++---- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/user/userns/userns.go b/user/userns/userns.go index 069a9bb..a07afe0 100644 --- a/user/userns/userns.go +++ b/user/userns/userns.go @@ -4,5 +4,5 @@ package userns // user namespace and memoizes the result. It returns false on non-Linux // platforms. func RunningInUserNS() bool { - return runningInUserNS() + return inUserNS() } diff --git a/user/userns/userns_linux.go b/user/userns/userns_linux.go index a6710b3..a1462e1 100644 --- a/user/userns/userns_linux.go +++ b/user/userns/userns_linux.go @@ -7,32 +7,26 @@ import ( "sync" ) -var ( - inUserNS bool - nsOnce sync.Once -) +var inUserNS = sync.OnceValue(runningInUserNS) // runningInUserNS detects whether we are currently running in a user namespace. // // Originally copied from https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700. func runningInUserNS() bool { - nsOnce.Do(func() { - file, err := os.Open("/proc/self/uid_map") - if err != nil { - // This kernel-provided file only exists if user namespaces are supported. - return - } - defer file.Close() - - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return - } - - inUserNS = uidMapInUserNS(string(l)) - }) - return inUserNS + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported. + return false + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return false + } + + return uidMapInUserNS(string(l)) } func uidMapInUserNS(uidMap string) bool { diff --git a/user/userns/userns_unsupported.go b/user/userns/userns_unsupported.go index 47dd5aa..8ed8307 100644 --- a/user/userns/userns_unsupported.go +++ b/user/userns/userns_unsupported.go @@ -2,8 +2,5 @@ package userns -// runningInUserNS is a stub for non-Linux systems -// Always returns false -func runningInUserNS() bool { - return false -} +// inUserNS is a stub for non-Linux systems. Always returns false. +func inUserNS() bool { return false } From a40602bdb5c20ff865c8207c50cb03597faa7ca7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 16 Jul 2024 19:59:34 +0200 Subject: [PATCH 09/10] user/userns: add godoc for package Signed-off-by: Sebastiaan van Stijn --- user/userns/userns.go | 8 ++++++++ user/userns/userns_linux.go | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/user/userns/userns.go b/user/userns/userns.go index a07afe0..56b24c4 100644 --- a/user/userns/userns.go +++ b/user/userns/userns.go @@ -1,3 +1,11 @@ +// Package userns provides utilities to detect whether we are currently running +// in a Linux user namespace. +// +// This code was migrated from [libcontainer/runc], which based its implementation +// on code from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 package userns // RunningInUserNS detects whether we are currently running in a Linux diff --git a/user/userns/userns_linux.go b/user/userns/userns_linux.go index a1462e1..87c1c38 100644 --- a/user/userns/userns_linux.go +++ b/user/userns/userns_linux.go @@ -11,7 +11,11 @@ var inUserNS = sync.OnceValue(runningInUserNS) // runningInUserNS detects whether we are currently running in a user namespace. // -// Originally copied from https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700. +// This code was migrated from [libcontainer/runc] and based on an implementation +// from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 func runningInUserNS() bool { file, err := os.Open("/proc/self/uid_map") if err != nil { From 5cd502ca1e109c963ac486b3e8f84ae73ff1a74f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 16 Jul 2024 20:17:18 +0200 Subject: [PATCH 10/10] user: require go1.21 or higher Signed-off-by: Sebastiaan van Stijn --- Makefile | 18 ++++++++++++------ user/go.mod | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 14e79bd..c83bf16 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,18 @@ clean: test: test-local set -eu; \ for p in $(PACKAGES); do \ - (cd $$p; go test $(RUN_VIA_SUDO) -v .); \ + if $p = user && go version | grep -qv go1.18; then \ + (cd $$p; go test $(RUN_VIA_SUDO) -v .); \ + fi \ done .PHONY: tidy tidy: set -eu; \ - for p in $(PACKAGES); do \ - (cd $$p; go mod tidy); \ + for p in $(PACKAGES); do \ + if $p = user && go version | grep -qv go1.18; then \ + (cd $$p; go mod tidy); \ + fi \ done # Test the mount module against the local mountinfo source code instead of the @@ -42,9 +46,11 @@ lint: $(BINDIR)/golangci-lint $(BINDIR)/golangci-lint version set -eu; \ for p in $(PACKAGES); do \ - (cd $$p; \ - go mod download; \ - ../$(BINDIR)/golangci-lint run); \ + if $p = user && go version | grep -qv go1.18; then \ + (cd $$p; \ + go mod download; \ + ../$(BINDIR)/golangci-lint run); \ + fi \ done $(BINDIR)/golangci-lint: $(BINDIR) diff --git a/user/go.mod b/user/go.mod index 94e2d1f..d4ad72f 100644 --- a/user/go.mod +++ b/user/go.mod @@ -1,5 +1,5 @@ module github.com/moby/sys/user -go 1.18 +go 1.21 require golang.org/x/sys v0.1.0