diff --git a/go.mod b/go.mod index 56beebfc..9b8b56b5 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.3.5 github.com/moby/sys/mountinfo v0.1.3 github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 - github.com/opencontainers/runtime-spec v1.0.2 + github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 github.com/opencontainers/selinux v1.5.1 github.com/pkg/errors v0.9.1 github.com/seccomp/libseccomp-golang v0.9.1 diff --git a/go.sum b/go.sum index 3e71b19d..7c139fb6 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6Sl github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 h1:9mv9SC7GWmRWE0J/+oD8w3GsN2KYGKtg6uwLN7hfP5E= +github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo= github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y= diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 24989e9f..8c19c4a1 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -70,9 +70,10 @@ type Arg struct { // Syscall is a rule to match a syscall in Seccomp type Syscall struct { - Name string `json:"name"` - Action Action `json:"action"` - Args []*Arg `json:"args"` + Name string `json:"name"` + Action Action `json:"action"` + ErrnoRet *uint `json:"errnoRet"` + Args []*Arg `json:"args"` } // TODO Windows. Many of these fields should be factored out into those parts diff --git a/libcontainer/integration/seccomp_test.go b/libcontainer/integration/seccomp_test.go index 77f1a8d4..75319e7e 100644 --- a/libcontainer/integration/seccomp_test.go +++ b/libcontainer/integration/seccomp_test.go @@ -12,6 +12,78 @@ import ( libseccomp "github.com/seccomp/libseccomp-golang" ) +func TestSeccompDenyGetcwdWithErrno(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + errnoRet := uint(syscall.ESRCH) + + config := newTemplateConfig(rootfs) + config.Seccomp = &configs.Seccomp{ + DefaultAction: configs.Allow, + Syscalls: []*configs.Syscall{ + { + Name: "getcwd", + Action: configs.Errno, + ErrnoRet: &errnoRet, + }, + }, + } + + container, err := newContainer(config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + buffers := newStdBuffers() + pwd := &libcontainer.Process{ + Cwd: "/", + Args: []string{"pwd"}, + Env: standardEnvironment, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + Init: true, + } + + err = container.Run(pwd) + if err != nil { + t.Fatal(err) + } + ps, err := pwd.Wait() + if err == nil { + t.Fatal("Expecting error (negative return code); instead exited cleanly!") + } + + var exitCode int + status := ps.Sys().(syscall.WaitStatus) + if status.Exited() { + exitCode = status.ExitStatus() + } else if status.Signaled() { + exitCode = -int(status.Signal()) + } else { + t.Fatalf("Unrecognized exit reason!") + } + + if exitCode == 0 { + t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode) + } + + expected := "pwd: getcwd: No such process" + actual := strings.Trim(buffers.Stderr.String(), "\n") + if actual != expected { + t.Fatalf("Expected output %s but got %s\n", expected, actual) + } +} + func TestSeccompDenyGetcwd(t *testing.T) { if testing.Short() { return diff --git a/libcontainer/seccomp/seccomp_linux.go b/libcontainer/seccomp/seccomp_linux.go index 33afbbb9..73ddf3d1 100644 --- a/libcontainer/seccomp/seccomp_linux.go +++ b/libcontainer/seccomp/seccomp_linux.go @@ -38,7 +38,7 @@ func InitSeccomp(config *configs.Seccomp) error { return errors.New("cannot initialize Seccomp - nil config passed") } - defaultAction, err := getAction(config.DefaultAction) + defaultAction, err := getAction(config.DefaultAction, nil) if err != nil { return errors.New("error initializing seccomp - invalid default action") } @@ -102,17 +102,23 @@ func IsEnabled() bool { } // Convert Libcontainer Action to Libseccomp ScmpAction -func getAction(act configs.Action) (libseccomp.ScmpAction, error) { +func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) { switch act { case configs.Kill: return actKill, nil case configs.Errno: + if errnoRet != nil { + return libseccomp.ActErrno.SetReturnCode(int16(*errnoRet)), nil + } return actErrno, nil case configs.Trap: return actTrap, nil case configs.Allow: return actAllow, nil case configs.Trace: + if errnoRet != nil { + return libseccomp.ActTrace.SetReturnCode(int16(*errnoRet)), nil + } return actTrace, nil case configs.Log: return actLog, nil @@ -177,7 +183,7 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error { } // Convert the call's action to the libseccomp equivalent - callAct, err := getAction(call.Action) + callAct, err := getAction(call.Action, call.ErrnoRet) if err != nil { return fmt.Errorf("action in seccomp profile is invalid: %s", err) } diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 6df2e2af..2ad1651f 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -855,9 +855,10 @@ func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) { for _, name := range call.Names { newCall := configs.Syscall{ - Name: name, - Action: newAction, - Args: []*configs.Arg{}, + Name: name, + Action: newAction, + ErrnoRet: call.ErrnoRet, + Args: []*configs.Arg{}, } // Loop through all the arguments of the syscall and convert them for _, arg := range call.Args { diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index c7c7c3d0..7b60f8bb 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -667,9 +667,10 @@ type LinuxSeccompArg struct { // LinuxSyscall is used to match a syscall in Seccomp type LinuxSyscall struct { - Names []string `json:"names"` - Action LinuxSeccompAction `json:"action"` - Args []LinuxSeccompArg `json:"args,omitempty"` + Names []string `json:"names"` + Action LinuxSeccompAction `json:"action"` + ErrnoRet *uint `json:"errnoRet,omitempty"` + Args []LinuxSeccompArg `json:"args,omitempty"` } // LinuxIntelRdt has container runtime resource constraints for Intel RDT diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go index bda7e1ca..596af0c2 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -11,7 +11,7 @@ const ( VersionPatch = 2 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "" + VersionDev = "-dev" ) // Version is the specification version that the package types support. diff --git a/vendor/modules.txt b/vendor/modules.txt index 457f6644..f3e2ac0c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -37,7 +37,7 @@ github.com/moby/sys/mountinfo # github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 ## explicit github.com/mrunalp/fileutils -# github.com/opencontainers/runtime-spec v1.0.2 +# github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 ## explicit github.com/opencontainers/runtime-spec/specs-go # github.com/opencontainers/selinux v1.5.1