diff options
| -rw-r--r-- | container/container.go | 4 | ||||
| -rw-r--r-- | container/dispatcher.go | 16 | ||||
| -rw-r--r-- | container/dispatcher_test.go | 3 | ||||
| -rw-r--r-- | container/init.go | 4 | ||||
| -rw-r--r-- | container/sysctl.go | 69 | ||||
| -rw-r--r-- | container/sysctl_deprecated.go | 19 |
6 files changed, 74 insertions, 41 deletions
diff --git a/container/container.go b/container/container.go index 3571d04a..6929c652 100644 --- a/container/container.go +++ b/container/container.go @@ -231,10 +231,10 @@ func (p *Container) Start() error { // map to overflow id to work around ownership checks if p.Uid < 1 { - p.Uid = OverflowUid(p.msg) + p.Uid = OverflowUID() } if p.Gid < 1 { - p.Gid = OverflowGid(p.msg) + p.Gid = OverflowGID() } if !p.RetainSession { diff --git a/container/dispatcher.go b/container/dispatcher.go index 4636a469..ba5e9c10 100644 --- a/container/dispatcher.go +++ b/container/dispatcher.go @@ -37,15 +37,15 @@ type syscallDispatcher interface { // lockOSThread provides [runtime.LockOSThread]. lockOSThread() - // setPtracer provides [SetPtracer]. + // setPtracer provides [ext.SetPtracer]. setPtracer(pid uintptr) error - // setDumpable provides [SetDumpable]. + // setDumpable provides [ext.SetDumpable]. setDumpable(dumpable uintptr) error - // setNoNewPrivs provides [SetNoNewPrivs]. + // setNoNewPrivs provides setNoNewPrivs. setNoNewPrivs() error - // lastcap provides [LastCap]. - lastcap(msg message.Msg) uintptr + // lastcap provides lastcap. + lastcap() uintptr // capset provides capset. capset(hdrp *capHeader, datap *[2]capData) error // capBoundingSetDrop provides capBoundingSetDrop. @@ -54,9 +54,9 @@ type syscallDispatcher interface { capAmbientClearAll() error // capAmbientRaise provides capAmbientRaise. capAmbientRaise(cap uintptr) error - // isatty provides [Isatty]. + // isatty provides [ext.Isatty]. isatty(fd int) bool - // receive provides [Receive]. + // receive provides [params.Receive]. receive(key string, e any, fdp *int) (closeFunc func() error, err error) // bindMount provides procPaths.bindMount. @@ -152,7 +152,7 @@ func (direct) setPtracer(pid uintptr) error { return ext.SetPtracer(pid) } func (direct) setDumpable(dumpable uintptr) error { return ext.SetDumpable(dumpable) } func (direct) setNoNewPrivs() error { return setNoNewPrivs() } -func (direct) lastcap(msg message.Msg) uintptr { return LastCap(msg) } +func (direct) lastcap() uintptr { return lastcap() } func (direct) capset(hdrp *capHeader, datap *[2]capData) error { return capset(hdrp, datap) } func (direct) capBoundingSetDrop(cap uintptr) error { return capBoundingSetDrop(cap) } func (direct) capAmbientClearAll() error { return capAmbientClearAll() } diff --git a/container/dispatcher_test.go b/container/dispatcher_test.go index 242af1bf..9276a2c5 100644 --- a/container/dispatcher_test.go +++ b/container/dispatcher_test.go @@ -352,9 +352,8 @@ func (k *kstub) setDumpable(dumpable uintptr) error { } func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err } -func (k *kstub) lastcap(msg message.Msg) uintptr { +func (k *kstub) lastcap() uintptr { k.Helper() - k.checkMsg(msg) return k.Expects("lastcap").Ret.(uintptr) } diff --git a/container/init.go b/container/init.go index 07fff357..03501646 100644 --- a/container/init.go +++ b/container/init.go @@ -226,7 +226,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { } // cache sysctl before pivot_root - lastcap := k.lastcap(msg) + lc := k.lastcap() if err := k.mount(zeroString, fhs.Root, zeroString, MS_SILENT|MS_SLAVE|MS_REC, zeroString); err != nil { k.fatalf(msg, "cannot make / rslave: %v", optionalErrorUnwrap(err)) @@ -399,7 +399,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { if err := k.capAmbientClearAll(); err != nil { k.fatalf(msg, "cannot clear the ambient capability set: %v", err) } - for i := range lastcap + 1 { + for i := range lc + 1 { if slices.Contains(keepCaps, i) { continue } diff --git a/container/sysctl.go b/container/sysctl.go index e4b7af23..2cf47258 100644 --- a/container/sysctl.go +++ b/container/sysctl.go @@ -2,14 +2,32 @@ package container import ( "bytes" + "fmt" "os" "strconv" "sync" "hakurei.app/fhs" - "hakurei.app/message" ) +// mustSysctlInt reads a sysctl path via proc, interprets it as a decimal +// representation of an integer, and terminates the program if the read was +// unsuccessful or the response is invalid. +func mustSysctlInt(path string) (v int) { + p, err := os.ReadFile(fhs.ProcSys + path) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + if v, err = strconv.Atoi(string(bytes.TrimSpace(p))); err != nil { + fmt.Printf("invalid sysctl response %q\n", string(p)) + os.Exit(1) + } + return +} + +// cached sysctl responses for populateSysctl var ( kernelOverflowuid int kernelOverflowgid int @@ -18,34 +36,31 @@ var ( sysctlOnce sync.Once ) -const ( - kernelOverflowuidPath = fhs.ProcSys + "kernel/overflowuid" - kernelOverflowgidPath = fhs.ProcSys + "kernel/overflowgid" - kernelCapLastCapPath = fhs.ProcSys + "kernel/cap_last_cap" -) - -func mustReadSysctl(msg message.Msg) { +// populateSysctl populates cached sysctl responses guarded by sysctlOnce. +func populateSysctl() { sysctlOnce.Do(func() { - if v, err := os.ReadFile(kernelOverflowuidPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowuidPath, err) - } else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err) - } + kernelOverflowuid = mustSysctlInt("kernel/overflowuid") + kernelOverflowgid = mustSysctlInt("kernel/overflowgid") + kernelCapLastCap = mustSysctlInt("kernel/cap_last_cap") + }) +} - if v, err := os.ReadFile(kernelOverflowgidPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowgidPath, err) - } else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err) - } +// OverflowUID returns the fixed uid returned by legacy 16-bit UID system calls. +// This value is also returned for unmapped UIDs in a user namespace. +func OverflowUID() int { + populateSysctl() + return kernelOverflowuid +} - if v, err := os.ReadFile(kernelCapLastCapPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelCapLastCapPath, err) - } else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err) - } - }) +// OverflowGID is like [OverflowUID], but for GID instead. +func OverflowGID() int { + populateSysctl() + return kernelOverflowgid } -func OverflowUid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowuid } -func OverflowGid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowgid } -func LastCap(msg message.Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) } +// lastcap returns the highest valid capability of the running kernel. Exports +// CAP_LAST_CAP from the kernel. +func lastcap() uintptr { + populateSysctl() + return uintptr(kernelCapLastCap) +} diff --git a/container/sysctl_deprecated.go b/container/sysctl_deprecated.go new file mode 100644 index 00000000..3c76be2c --- /dev/null +++ b/container/sysctl_deprecated.go @@ -0,0 +1,19 @@ +package container + +import "hakurei.app/message" + +// OverflowUid is the old function signature of [OverflowUID]. +// +// Deprecated: This function will be removed in 0.5. +func OverflowUid(_ message.Msg) int { return OverflowUID() } + +// OverflowGid is the old function signature of [OverflowGID]. +// +// Deprecated: This function will be removed in 0.5. +func OverflowGid(_ message.Msg) int { return OverflowGID() } + +// LastCap returns the highest valid capability of the running kernel. Exports +// CAP_LAST_CAP from the kernel. +// +// Deprecated: This function will be unexported in 0.5. +func LastCap(_ message.Msg) uintptr { return lastcap() } |
