mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
Fix runc run "permission denied" when rootless
Since commit 957d97bcf4 was made to fix issue [7],
a few things happened:
- a similar functionality appeared in go 1.20 [1], so the issue
mentioned in the comment (being removed) is no longer true;
- a bug in runc was found [2], which also affects go [3];
- the bug was fixed in go 1.21 [4] and 1.20.2 [5];
- a similar fix was made to x/sys/unix.Faccessat [6].
The essense of [2] is, even if a (non-root) user that the container is
run as does not have execute permission bit set for the executable, it
should still work in case runc has the CAP_DAC_OVERRIDE capability set.
To fix this [2] without reintroducing the older bug [7]:
- drop own Eaccess implementation;
- use the one from x/sys/unix for Go 1.19 (depends on [6]);
- do not use anything when Go 1.20+ is used.
NOTE it is virtually impossible to fix the bug [2] when Go 1.20 or Go
1.20.1 is used because of [3].
A test case is added by a separate commit.
Fixes: #3715.
[1] https://go-review.googlesource.com/c/go/+/414824
[2] https://github.com/opencontainers/runc/issues/3715
[3] https://go.dev/issue/58552
[4] https://go-review.googlesource.com/c/go/+/468735
[5] https://go-review.googlesource.com/c/go/+/469956
[6] https://go-review.googlesource.com/c/sys/+/468877
[7] https://github.com/opencontainers/runc/issues/3520
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package libcontainer
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func eaccess(path string) error {
|
||||
// This check is similar to access(2) with X_OK except for
|
||||
// setuid/setgid binaries where it checks against the effective
|
||||
// (rather than real) uid and gid. It is not needed in go 1.20
|
||||
// and beyond and will be removed later.
|
||||
|
||||
// Relies on code added in https://go-review.googlesource.com/c/sys/+/468877
|
||||
// and older CLs linked from there.
|
||||
return unix.Faccessat(unix.AT_FDCWD, path, unix.X_OK, unix.AT_EACCESS)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
//go:build go1.20
|
||||
|
||||
package libcontainer
|
||||
|
||||
func eaccess(path string) error {
|
||||
// Not needed in Go 1.20+ as the functionality is already in there
|
||||
// (added by https://go.dev/cl/416115, https://go.dev/cl/414824,
|
||||
// and fixed in Go 1.20.2 by https://go.dev/cl/469956).
|
||||
return nil
|
||||
}
|
||||
@@ -198,11 +198,12 @@ func (l *linuxStandardInit) Init() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// exec.LookPath might return no error for an executable residing on a
|
||||
// file system mounted with noexec flag, so perform this extra check
|
||||
// now while we can still return a proper error.
|
||||
if err := system.Eaccess(name); err != nil {
|
||||
return &os.PathError{Op: "exec", Path: name, Err: err}
|
||||
// exec.LookPath in Go < 1.20 might return no error for an executable
|
||||
// residing on a file system mounted with noexec flag, so perform this
|
||||
// extra check now while we can still return a proper error.
|
||||
// TODO: remove this once go < 1.20 is not supported.
|
||||
if err := eaccess(name); err != nil {
|
||||
return &os.PathError{Op: "eaccess", Path: name, Err: err}
|
||||
}
|
||||
|
||||
// Set seccomp as close to execve as possible, so as few syscalls take
|
||||
|
||||
@@ -31,25 +31,6 @@ func (p ParentDeathSignal) Set() error {
|
||||
return SetParentDeathSignal(uintptr(p))
|
||||
}
|
||||
|
||||
// Eaccess is similar to unix.Access except for setuid/setgid binaries
|
||||
// it checks against the effective (rather than real) uid and gid.
|
||||
func Eaccess(path string) error {
|
||||
err := unix.Faccessat2(unix.AT_FDCWD, path, unix.X_OK, unix.AT_EACCESS)
|
||||
if err != unix.ENOSYS && err != unix.EPERM { //nolint:errorlint // unix errors are bare
|
||||
return err
|
||||
}
|
||||
|
||||
// Faccessat2() not available; check if we are a set[ug]id binary.
|
||||
if os.Getuid() == os.Geteuid() && os.Getgid() == os.Getegid() {
|
||||
// For a non-set[ug]id binary, use access(2).
|
||||
return unix.Access(path, unix.X_OK)
|
||||
}
|
||||
|
||||
// For a setuid/setgid binary, there is no fallback way
|
||||
// so assume we can execute the binary.
|
||||
return nil
|
||||
}
|
||||
|
||||
func Execv(cmd string, args []string, env []string) error {
|
||||
name, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user